Java 允许此引用转义
如果能从“实践中的Java并发”中理解以下内容,我将不胜感激: 调用可重写的实例方法(既不是 构造函数的私有(也不是最终)也可以允许 这是指逃逸Java 允许此引用转义,java,concurrency,Java,Concurrency,如果能从“实践中的Java并发”中理解以下内容,我将不胜感激: 调用可重写的实例方法(既不是 构造函数的私有(也不是最终)也可以允许 这是指逃逸 这里的“escape”仅仅意味着我们可能在实例完全构造之前调用实例方法吗? 我没有看到“this”以任何其他方式脱离实例的范围 “final”是如何防止这种情况发生的?在实例创建中是否存在我所缺少的“final”的某些方面 它意味着在类外调用代码,并传递这个 该代码将假定实例已完全初始化,如果未初始化,则可能会中断。 类似地,您的类可能假设某些方法只有
我没有看到“this”以任何其他方式脱离实例的范围李>
这个
该代码将假定实例已完全初始化,如果未初始化,则可能会中断。
类似地,您的类可能假设某些方法只有在实例完全初始化后才会被调用,但外部代码可能会破坏这些假设
final
方法不能被重写,因此您可以相信它们不会传递此
。如果为非
final
类调用构造函数中的任何非final
方法,则派生类可能会重写该方法并将此传递到任何位置。
即使在调用final
方法时,您仍然需要确保它们是安全编写的–它们不会在任何地方传递this
,并且它们本身不会调用任何非final
方法
我相信这个例子是这样的
public class Foo {
public Foo() {
doSomething();
}
public void doSomething() {
System.out.println("do something acceptable");
}
}
public class Bar extends Foo {
public void doSomething() {
System.out.println("yolo");
Zoom zoom = new Zoom(this); // at this point 'this' might not be fully initialized
}
}
由于总是首先调用超级构造函数(隐式或显式),因此子类总是会调用doSomething
。由于上述方法既不是final
也不是private
,因此您可以在子类中重写它并执行任何您想执行的操作,这可能与Foo#doSomething()
的本意相冲突。Per
示例错误代码:
final class Publisher {
public static volatile Publisher published;
int num;
Publisher(int number) {
published = this;
// Initialization
this.num = number;
// ...
}
}
如果对象的初始化(及其构造)依赖于构造函数中的安全检查,则当不受信任的调用方获得部分初始化的实例时,可以绕过安全检查。见规则。要小心让构造函数抛出异常以获取更多信息
final class Publisher {
public static Publisher published;
int num;
Publisher(int number) {
// Initialization
this.num = number;
// ...
published = this;
}
}
由于字段是非易失性和非最终的,因此
编译器可以通过以下方式对构造函数重新排序:
此引用在初始化语句之前发布
已经执行了
正确的代码:
final class Publisher {
static volatile Publisher published;
int num;
Publisher(int number) {
// Initialization
this.num = number;
// ...
published = this;
}
}
该引用在可用时被称为已逃逸
超出其当前范围。以下是解决此问题的常用方法
引用可以转义:
Returning this from a non-private, overridable method that is invoked from the constructor of a class whose object is being
建造的。(有关更多信息,请参见规则MET05-J。确保
构造函数不调用可重写的方法。)
从可变类的非私有方法返回,这允许调用方间接操纵对象的状态。这
通常发生在方法链接实现中;见规则VNA04-J。
确保对链式方法的调用是原子的,以获取更多信息。
将其作为参数传递给从正在构造其对象的类的构造函数调用的外来方法。
使用内部类。除非声明内部类,否则内部类隐式地持有对其外部类实例的引用
静止的
通过从正在构造其对象的类的构造函数中将其分配给公共静态变量来发布。
正在从构造函数中引发异常。这样做可能会导致代码容易受到终结器攻击;参见规则OBJ11-J。小心
允许构造函数抛出异常以获取更多信息。
将内部对象状态传递给外部方法。这使该方法能够检索内部成员对象的This引用
此规则描述了允许此操作的潜在后果
对象构造期间转义的引用,包括种族
条件和不正确的初始化。例如,声明一个字段
final通常确保所有线程都能以完全一致的方式查看字段
初始化状态;但是,允许此引用转义
在对象构造期间,可以将字段公开给
未初始化或部分初始化状态。规则TSM03-J.请勿
发布部分初始化的对象,其中描述了保证
由各种安全出版机制提供,依赖于
遵守这条规则。因此,程序不得允许
此引用用于在对象构造期间转义
总的来说,重要的是要发现这种情况
引用可能泄漏到当前上下文的范围之外。在里面
特别是,公共变量和方法应谨慎使用
仔细检查
“Escape”表示对部分构造的此对象的引用可能会传递给系统中的其他对象。考虑这种情况:
public Foo {
public Foo() {
setup();
}
protected void setup() {
// do stuff
}
}
public Bar extends Foo implements SomeListener {
@Override protected void setup() {
otherObject.addListener(this);
}
}
问题是,新的条形图
对象在其构造完成之前正在向其他对象
注册。现在,如果otherObject
开始调用barObject
上的方法,则字段可能尚未初始化,或者barObject
可能处于不一致的状态。对barObject
(this
自身)的引用在准备就绪之前已“转义”到系统的其余部分
相反,如果Foo
上的setup()
方法是final
,Bar
类不能在Foo
构造函数完成之前将对象可见的代码放入其中。这是一个很好的问题。我认为,对于您的发布者
类来说,更好的模式是使用某种形式的工厂(可能是静态
方法)来创建