7.3.2 引用类型基本概念
所有的引用类型都是java.lang.ref.Reference类的子类。Reference类中包含了所有引用类型公用的方法。对一个Reference类的对象来说,在创建的时候都需要提供一个它所指向的对象。引用一个对象的通常做法如代码清单7-3所示,变量obj引用了一个新创建的Object类的对象,这相当于在该对象上添加了一个强引用。
代码清单7-3 引用对象的通常做法
Object obj=new Object();
如果使用引用类型,相关的实现如代码清单7-4所示。通过创建一个Reference子类java.lang.ref.SoftReference类的对象ref,就可以在变量obj所引用的对象上添加一个软引用。在创建SoftReference类的对象时,要把所指向的对象作为构造方法的参数传递进去。需要注意的是,在创建了新的软引用之后,要显式地把之前创建的对象上的强引用清除,这也是代码清单7-4中“obj=null;”语句的作用。在清除了强引用之后,指向新创建的Object类的对象的引用就只有SoftReference类的对象ref。如果在程序中没有清除之前的强引用,添加其他引用类型实际上是毫无作用的。因为只要强引用存在,该对象就无法被垃圾回收器处理。
代码清单7-4 引用类型使用示例
Object obj=new Object();
SoftReference<Object>ref=new SoftReference<Object>(obj);
obj=null;
使用了引用类型之后,对象之间的引用关系也发生了变化。在代码清单7-3中,通过obj持有对Object类的对象的强引用;而在代码清单7-4中,程序持有的是对SoftReference类的对象ref的强引用,ref再通过软引用来指向Object类的对象。这种引用关系的改变,使对Object类的对象进行垃圾回收变为可能。
在创建引用对象时的一种错误做法如代码清单7-5所示。这种做法的问题在于没有使用强引用先指向待引用的对象。垃圾回收器可能随时进行内存的回收工作。可能出现的一种情况是:在SoftReference类的对象创建出来之后,垃圾回收器正好回收了SoftReference类的对象所指向的对象,这会使该引用对象实际上毫无作用。
代码清单7-5 使用引用类型的错误用法
SoftReference<Object>ref=new SoftReference<Object>(new Object());
Reference类的get方法用来获取所引用的实际对象。如果引用关系已经被清除,该方法的返回值为null。因此在使用get方法时,需要检查返回值是否为null,并分别使用不同的代码逻辑。Reference类的clear方法用来清除引用关系。当在某个时间点上不再需要继续引用Reference类的对象所指向的对象时,可以使用clear方法来清除这个引用。当clear方法被调用之后,无法通过get方法获取所引用的对象,一般由垃圾回收器或程序本身在合适的时机调用clear方法来清除引用。Reference类中的另外两个方法enqueue和isEnqueued都与引用队列相关,分别用来把当前引用对象放入对应的引用队列中,以及判断当前引用对象是否已经被放入队列中。
在使用get方法的时候,需要确保有强引用指向get方法的返回值,否则可能会出现错误。代码清单7-6给出了错误的用法,虽然先检查了get方法的返回值是否为null,但是obj变量所指向的对象仍有可能为null。这是因为垃圾回收器可能在if判断语句之后运行,回收了引用对象所指向的对象,使下一次的get方法调用返回null。
代码清单7-6 使用get方法的错误示例
if(ref.get()!=null){
Object obj=ref.get();
}
引用队列是一个包含了引用类型对象的队列,按照先进先出的方式来操作。引用队列由类java.lang.ref.ReferenceQueue来表示。ReferenceQueue类中提供了从队列中获取引用对象的方法,但是并没有向队列中添加对象的方法。添加引用对象到队列中的操作由垃圾回收器自动完成或通过Reference类的enqueue方法来完成。从引用队列中获取对象有两种方式,分别是阻塞式和非阻塞式的。阻塞式的方式是通过remove方法来实现的,该方法会阻塞当前线程直到从队列中获取到一个引用对象为止;非阻塞式的方式则通过poll方法来实现。如果当前队列中有引用对象,poll方法会返回队列中的第一个对象,否则直接返回null。在创建Reference类对象时,可以提供一个额外的引用队列的对象作为参数,这样可以把引用对象和该引用队列关联起来。垃圾回收器会在合适的时间点上把引用对象放入到对应的队列中。代码清单7-7给出了引用队列的使用示例。
代码清单7-7 引用队列的使用示例
Object obj=new Object();
ReferenceQueue queue=new ReferenceQueue();
SoftReference<Object>ref=new SoftReference<Object>(obj, queue);
obj=null;