允许此引用转义

时间:2013-12-09 15:39:01

标签: java concurrency

我非常感谢帮助理解以下“Java Concurrency in Practice'”

  

调用可重写的实例方法(两者都不是   私有也不是最终的)从构造函数也可以允许   这个参考逃脱。

  1. 逃脱'这里只是意味着我们可能在实例完全构造之前调用实例方法? 我没有看到这个'以任何其他方式转义实例的范围。
  2. '最终'防止这种情况发生?是否有某些方面的“最终”#39;在我不知道的实例创作中?

4 个答案:

答案 0 :(得分:26)

  1. 这意味着在课堂外调用代码,并传递this 该代码将假定实例已完全初始化,如果不是则可能会中断 类似地,您的类可能会假设某些方法仅在实例完全初始化后调用,但外部代码可能会破坏这些假设。

  2. final方法无法覆盖,因此您可以相信它们不会传递this
    如果在构造函数中为非final类调用任何非final方法,派生类可能会覆盖该方法并将this传递到任何位置。

    即使您调用final方法,您仍然需要确保它们已被安全地写入 - 它们不会在this传递任何地方,而且它们本身也不会调用任何非final方法方法

答案 1 :(得分:21)

“Escape”表示对部分构造的this对象的引用可能会传递给系统中的某个其他对象。请考虑以下情况:

public Foo {
    public Foo() {
        setup();
    }

    protected void setup() {
       // do stuff
    }
}

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

问题是新的Bar对象在构建完成之前正在otherObject注册。现在,如果otherObject开始在barObject上调用方法,则字段可能尚未初始化,或者barObject可能处于不一致状态。对barObjectthis本身)的引用已经“逃逸”到系统的其余部分,然后才准备好。

相反,如果setup()方法在final上为Foo,则Bar类不能在其中放置代码,以使对象在{{{{}}之前可见1}}构造函数完成。

答案 2 :(得分:12)

我相信这个例子就像是

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

答案 3 :(得分:5)

secure coding

示例 BAD 代码:

final class Publisher {
  public static volatile Publisher published;
  int num;

  Publisher(int number) {
    published = this;
    // Initialization
    this.num = number;
    // ...
  }
}   

如果对象的初始化(以及它的构造)依赖于构造函数中的安全性检查,则当不受信任的调用者获得部分初始化的实例时,可以绕过安全性检查。请参阅规则OBJ11-J。警惕让构造函数抛出异常以获取更多信息。

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。要小心   让构造函数抛出异常以获取更多信息。       将内部对象状态传递给外来方法。这使该方法能够检索内部成员对象的此引用。

     

此规则描述了允许此操作的潜在后果   在对象构造期间引用逃逸,包括种族   条件和初始化不当。例如,声明一个字段   final通常确保所有线程完全看到该字段   初始化状态;但是,允许此引用转义   在对象构造期间可以将字段暴露给其中的其他线程   未初始化或部分初始化的状态。规则TSM03-J。不要   发布部分初始化对象,描述保证   由各种安全出版机制提供,依赖   符合这条规则。因此,计划不得允许   这个引用在对象构造期间的转义。

     

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