java引用详解_Java四种引用详解

EndlessLethe原创文章,转载请注明: 转载自小楼吹彻玉笙寒

前言

Java中引用是一个最常用的Object,我们有必要对它有一个基本的理解。而且引用通常出现在一本Java书的开头,这导致,几乎没有Java书敢详细地叙述关于引用的复杂机制。本文剖析了Java的四种引用。

注意,理解这四种引用需要明白:什么是对象,什么是引用。一定注意叙述中的“对象”和“引用”。

本文没有涵盖的内容:JVM内存管理。

参数传递过程

引用的生存周期和finalize()方法

软引用和弱引用的应用

常量池中的符号引用

引用和内存管理、参数传递过程都有密切的关系,本文并不探讨引用的引用计数算法、可达性分析等涉及过多内存管理的内容,也不探讨参数传递过程,以免顾此失彼,用力不均。

至于引用的生存周期和finalize()方法,限于篇幅,也不讲解。感兴趣的读者可以见参考文献5。

对软引用和弱引用的应用感兴趣的读者见参考文献4。

如果想深入了解Java的参数传递过程,戳:TBC。

什么是对象

对象是类的实例。

Person person = new Person("张三");

这里new出来(等号右边)的就是一个对象。

什么是引用(reference)

还是刚刚的代码为例

Person person = new Person("张三");

等式左边的变量名person就是一个reference。

这也就是为什么我们可以先声明一个reference,但可以不初始化的原因。

“=”操作符使reference指向刚创建的Person对象”张三”。

注:有人愿意把’person’称作句柄而不是reference,从而消除引用的二义性——引用既可以指:以变量名形式声明的reference,也可以是四种引用的统称。本文把以变量名形式存在的引用写作“reference”,把四种引用的统称写作“引用”。

对象的访问定位

建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作堆上的具体对象。

由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、和访问,所以这取决于虚拟机的具体实现。

现在主流的方法有使用句柄和直接指针两种。具体实现和优缺点见《深入理解JVM》,这里照搬原文也没有意义。

四种引用

Java中实际上有四种强度不同的引用,从强到弱它们分别是,强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference)和虚引用(Phantom Reference)。

它们的区别在于声明的形式和内存回收机制采取行动回收它们的时机。

强引用

强引用其实就是我们平时写的最多新建对象的那种用法,你也许根本就没有意识到这个就是传说中的强引用:

Person person = new Person("张三");

强引用是所有的引用里面最强的一种,这意味着只要这个引用还存在,那么垃圾回收器就绝对不会回收这个对象。即使内存已经不足,JVM宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

软引用

软引用在强度上弱于强引用,通过类SoftReference来表示:

SoftReference softPerson = new SoftReference(new Person("张三"));

它是用来描述一些还有用但并非必须的对象。对于被软引用关联着的对象,在系统将要发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次的回收。

值得注意的是,在新建SoftReference对象的时候,可以是像上面一样传一个完全新建的对象进去,也可以是传一个reference进去,但如果是传reference进去的话,那这个对象就不仅仅是只被软引用了。垃圾回收器回收它的时候是看它最强的那个引用来决定回收时机。

这样的话,MyObject对象是强引用对象。

随即,我们可以结束aReference对这个MyObject实例的强引用:

aRef = null;

这个MyObject对象成为了软引用对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。

垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收被软引用关联的对象,而且虚拟机会尽可能优先回收长时间闲置不用的对象,对那些刚刚构建的或刚刚使用过的“新”对象会被虚拟机尽可能保留。在回收这些对象之前,我们可以通过:

MyObject anotherRef=(MyObject)aSoftRef.get();

重新获得对该实例的强引用。而回收之后,调用get()方法就只能得到null了。

一个例子

软引用的作用是告诉垃圾回收器,我新建的这个对象其实不那么重要(至少没有强引用建立的对象重要)。当JVM中的内存不足的时候,垃圾回收器可以释放这些只被软引用关联的对象。如果全部释放完这些对象之后,内存还不足,才会抛出OutOfMemory错误。

因此,软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。比如考虑一个图像浏览器的程序,该程序会把图像文件的全部内容都读取到内存中,以方便进行处理。而用户也可以同时打开多个文件。当同时打开的文件过多的时候,就可能造成内存不足。如果使用软引用来指向图像文件内容的话,垃圾回收器就可以在必要的时候回收掉这些内存。

弱引用

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。 这意味着,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。

WeakReference weakPerson = new WeakReference(new Person("张三"));

不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

引用对列(ReferenceQueue)

作为一个Java对象,SoftReference(WeakReference/PhantomReference)除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当被软引用的对象被回收之后,这个SoftReference已经不再具有存在的价值,需要一个适当的清除机制,来避免大量SoftReference对象带来的内存泄漏。

通过ReferenceQueue可以实现这样的软/弱/虚引用清除机制。而且我们还可以为被回收的对象进行收尾处理。

在创建SoftReference对象的时候,我们可以将ReferenceQueue对象作为参数提供:

当这个SoftReference所软引用的aMyOhject被垃圾收集器回收时,这个SoftReference会被自动添加到对应ReferenceQueue。

从而我们可以利用poll()方法(返回队列头的元素)来得知是否有对象被回收,并处理对应的SoftReference。

虚引用

“虚引用”也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。与其他几种引用不同的是,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用(弱引用)一样,在任何时候都可能被垃圾回收器回收。而且它控制其指向的对象非常弱(tenuous),以至于它不能获得这个对象(get()方法)。

虚引用只有一种使用方法:跟踪在ReferenceQuene中的已经被回收的对象。

虚引用和弱引用的不同在于其入队进入ReferenceQuene的时机。这个SoftReference进入ReferenceQuene发生在终结(finialize)和垃圾回收实际发生之前。理论上,通过finilize()方法,被回收的对象能重新复活(resurrected),虽然SoftReference已经是指向null的了。而PhantomReference只有当对象在物理上从内存中移出时,才会入队。

两个优点能确定某一个对象从内存中移除的时间

避免使用finilize()方法

实现代码

Reference类构造函数实现代码:

参考文献

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的监听器(Listener)是一种广泛应用的设计模式,它用于处理程序中的事件。通过监听器,我们可以在事件发生时执行一些特定的操作。在Java中,我们可以使用内置的监听器API或自定义监听器实现此功能。 下面是Java中的常见监听器类型: 1. ActionListener:用于处理按钮、菜单等组件的动作事件。 2. WindowListener:用于处理窗口事件,如窗口打开、关闭等。 3. MouseListener:用于处理鼠标事件,如单击、双击、拖动等。 4. KeyListener:用于处理键盘事件,如键盘按下、释放等。 5. FocusListener:用于处理组件焦点事件,如获得或失去焦点等。 使用监听器的步骤如下: 1. 创建一个监听器类,该类实现了相应的监听器接口。 2. 在需要监听的组件上添加监听器对象。 3. 在监听器类中实现相应的方法来处理事件。 下面是一个简单的示例代码,演示了如何使用ActionListener监听器处理按钮单击事件: ```java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ButtonListenerDemo implements ActionListener { private JFrame frame; private JButton button; public ButtonListenerDemo() { frame = new JFrame("Button Listener Demo"); button = new JButton("Click Me"); button.addActionListener(this); frame.getContentPane().add(button); frame.pack(); frame.setVisible(true); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(frame, "Button Clicked!"); } public static void main(String[] args) { new ButtonListenerDemo(); } } ``` 在上面的代码中,我们创建了一个ButtonListenerDemo类,该类实现了ActionListener接口。在构造函数中,我们创建了一个按钮对象,然后将该按钮添加到窗口中,并将该按钮的监听器设置为当前类。当用户单击按钮时,程序将调用actionPerformed()方法来处理事件,该方法将弹出一个消息框来告诉用户按钮已被单击。 总之,监听器是Java编程中非常重要的组成部分。使用监听器,我们可以轻松地处理程序中的事件,并实现交互式用户界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值