允许这个参考逃脱

我将不胜感激从“Java并发实践”中了解以下内容:

从构造函数中调用一个可覆盖的实例方法(一个既不是private也不是final)的方法也可以允许这个引用转义。

  1. 这里的“逃避”只是意味着在实例被完全构造之前,我们可能正在调用一个实例方法?
    我没有看到“这个”以任何其他方式逃避实例的范围。
  2. “最终”是如何防止这种情况发生的?在我创build的实例创build中,“最终”有一些方面吗?
  1. 这意味着在课堂之外调用代码,并通过this
    该代码将假定该实例已完全初始化,如果不是,则可能会中断。
    类似地,你的类可能会假定只有在实例完全初始化之后才会调用某些方法,但是外部代码可能会打破这些假设。

  2. final方法不能被覆盖,所以你可以相信他们不能通过this
    如果在非final类的构造函数中调用非final方法,则派生类可能会覆盖该方法并将其传递this任何地方。

    即使你调用final方法,你仍然需要确保它们被安全地写入 – 它们不会在任何地方传递,而且它们自己也不会调用任何非final方法。

“转义”意味着对部分构build的this对象的引用可能被传递给系统中的某个其他对象。 考虑这种情况:

 public Foo { public Foo() { setup(); } protected void setup() { // do stuff } } public Bar extends Foo implements SomeListener { @Override protected void setup() { otherObject.addListener(this); } } 

问题是在构build完成之前,新的Bar对象正在向otherObject注册。 现在,如果otherObject开始调用otherObject方法,则字段可能尚未初始化,否则barObject可能处于不一致的状态。 barObjectthis本身)的引用在它准备好之前已经“转义”到了系统的其他部分。

相反,如果setup()方法在Foo上是final ,那么Bar类不能在那里放置代码,这会在Foo构造函数完成之前使对象可见。

我相信这个例子是类似的

 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()意图相冲突。

每个安全编码

示例BAD代码:

 final class Publisher { public static volatile Publisher published; int num; Publisher(int number) { published = this; // Initialization this.num = number; // ... } } 

如果一个对象的初始化(因此,它的构造)依赖于构造函数中的安全检查,那么当一个不可信的调用者获得部分初始化的实例时,可以绕过安全检查。 参见规则OBJ11-J 。 小心让构造函数抛出exception以获取更多信息。

 final class Publisher { public static Publisher published; int num; Publisher(int number) { // Initialization this.num = number; // ... published = this; } } 

因为这个字段是非易失性和非终结的,所以构造函数中的语句可以被编译器重新sorting,以便在初始化语句执行之前发布这个引用。

正确的代码

 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 

build。 (有关更多信息,请参阅规则MET05-J。确保构造函数不调用可重写方法。)从可变类的非私有方法返回,这允许调用方间接操作对象的状态。 这通常发生在方法链实现中; 见规则VNA04-J。 确保对链式方法的调用是primefaces的,以获取更多信息。 将其作为parameter passing给从其构造对象的构造函数调用的外来方法。 使用内部类。 内部类隐式地持有对其外部类的实例的引用,除非内部类被声明为静态的。 通过将其分配给正在构造对象的类的构造函数的公共静态variables进行发布。 从构造函数中抛出一个exception。 这样做可能会导致代码易受终结器攻击; 参见规则OBJ11-J。 小心让构造函数抛出exception以获取更多信息。 将内部对象状态传递给外部方法。 这使该方法能够检索内部成员对象的这个引用。

这个规则描述了允许这个引用在对象构造过程中逃逸的潜在后果,包括竞争条件和不正确的初始化。 例如,声明一个字段最终通常可以确保所有的线程看到处于完全初始化状态的字段; 然而,允许这个引用在对象构造过程中转义,可以将字段暴露给处于未初始化或部分初始化状态的其他线程。 规则TSM03-J。 不要发布部分初始化对象,这些对象描述了各种安全发布机制所提供的保证,依赖于这个规则的一致性。 因此,程序在构build对象时不能允许这个引用逃逸。

一般来说,检测这种引用可能超出当前上下文范围的情况是很重要的。 特别是公共variables和方法应该仔细审查。