本文参考了《疯狂Java讲义 第四版》
正文开始
Java四种引用包括强引用,软引用,弱引用,虚引用。
- 强引用:
就是最普通最常见的引用,这种情况只要引用存在,垃圾回收器就永远不会回收该对象,比如:
Object obj = new Object();
- 软引用:
若一个对象只有软引用,只有当内存不够时,垃圾回收机制才会将其回收,用法如下代码所示:
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//返回obj,有时候会返回null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
- 弱引用:
若一个对象只有弱引用,则只要进行了垃圾回收,就会将其回收,可以通过如下代码实现
public class ReferenceTest
{
public static void main(String[] args)
throws Exception
{
// 创建一个字符串对象
String str = new String("1234");
// 创建一个弱引用,让此弱引用引用到"1234"字符串
WeakReference wr = new WeakReference(str);
// 切断str引用和"1234"字符串之间的引用
str = null;
// 取出弱引用所引用的对象
System.out.println(wr.get()); //由于还没有执行垃圾回收,输出1234
// 强制垃圾回收
System.gc();
System.runFinalization();
// 再次取出弱引用所引用的对象
System.out.println(wr.get()); //输出为空,已经被回收
}
}
- 虚引用:
虚引用与没有引用的情况基本相同,无法通过虚引用取到对象值(get方法的返回值永远为null),它的作用主要是跟踪对象被垃圾回收的状态(检测该对象是否已被回收),只能与ReferenceQueue同时使用,如果该对象已经被回收的话,虚引用将会被自动添加到关联的ReferenceQueue队列中,所以可以通过虚引用是否在队列中判断对象是否已经被回收,可以通过如下代码实现:
public class PhantomReferenceTest
{
public static void main(String[] args)
throws Exception
{
// 创建一个字符串对象
String str = new String("1234");
// 创建一个引用队列
ReferenceQueue rq = new ReferenceQueue();
// 创建一个虚引用,让此虚引用引用到"1234"字符串
PhantomReference pr = new PhantomReference (str , rq);
// 切断str引用和"1234"字符串之间的引用
str = null;
// 取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象,所以此处输出null
System.out.println(pr.get()); //输出null,虚引用的get方法永远输出为null
// 强制垃圾回收
System.gc();
System.runFinalization();
// 垃圾回收之后,虚引用将被放入引用队列中
// 取出引用队列中最先进入队列中的引用与pr进行比较
System.out.println(rq.poll() == pr); //输出true,代表虚引用确实已经被加入队列,对象已经被回收
}
}
由于垃圾回收的不确定性,很有可能出现当想从软引用或弱引用中取出对象时该对象已经被释放了的情况。若想要再次创建该对象,可以采用如下两种方法:
1.
obj = wr.get();//取出弱引用的对象
if(obj == null){
wr = new WeakReference(recreateIt());//重新创建一个新的对象,再次让wr去引用它
obj = wr.get();//再次从弱引用获取该对象
}
2.
obj = wr.get();//取出弱引用的对象
if(obj == null){
obj = recreateIt();/重新创建一个新的对象
wr = new WeakReference(obj)//再次用wr去引用它
}
注意:
上述两段代码都是伪代码,其中recreateIt为自定义的重新创建一个obj对象的方法,但第一段代码可能出现一种问题,就是当if块执行完毕后,obj又被回收了。而第二段代码则不会,因为有一个obj强引用。