前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >为什么final引用不能从构造函数内“逸出”

为什么final引用不能从构造函数内“逸出”

原创
作者头像
好派笔记
修改2021-10-08 14:52:05
修改2021-10-08 14:52:05
51300
代码可运行
举报
文章被收录于专栏:好派笔记好派笔记
运行总次数:0
代码可运行

  前面我们提到过,写final域的重排序规则可以确保:在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过了。其实要得到这个效果,还需要一个保证:在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。为了说明问题,让我们来看下面示例代码:

代码语言:javascript
代码运行次数:0
运行
复制
public class FinalReferenceEscapeExample {
	final int i;
	static FinalReferenceEscapeExample obj;

	public FinalReferenceEscapeExample () {
		i = 1;                              //1写final域
		obj = this;                          //2 this引用在此“逸出”
	}

	public static void writer() {
		new FinalReferenceEscapeExample ();
	}

	public static void reader() {
		if (obj != null) {                     //3
			int temp = obj.i;                 //4
		}
	}
}

  假设一个线程A执行writer()方法,另一个线程B执行reader()方法。这里的操作2使得对象还未完成构造前就为线程B可见。即使这里的操作2是构造函数的最后一步,且即使在程序中操作2排在操作1后面,执行read()方法的线程仍然可能无法看到final域被初始化后的值,因为这里的操作1和操作2之间可能被重排序。实际的执行时序可能如下图所示:

final关键字的好处

  • 将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行分析优化。比如被final修饰的方法,JVM会尝试为之寻求内联,这对于提升Java的效率是非常重要的。因此,假如能确定方法不会被继承,那么尽量将方法定义为final的。
  • 被final修饰的常量,在编译阶段会存入调用类的常量池中(比如子类继承父类,那么父类的final常量会被复制到子类常量池中),当子类使用这个常量时,不会引起父类的初始化。
  • final可以用于不可变对象的创建,而不可变对象一定是线程安全的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • final关键字的好处
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档