gpt4 book ai didi

java - `this` 引用外部类如何通过发布内部类实例转义?

转载 作者:IT老高 更新时间:2023-10-28 21:12:43 26 4
gpt4 key购买 nike

早先 but asking for a yes/no answer 提出的这个问题略有不同,但我正在寻找书中(Java 并发实践)中缺少的解释,说明这个明显的大错误将如何被恶意或意外利用。

A final mechanism by which an object or its internal state can be published is to publish an inner class instance, as shown in ThisEscape in Listing 3.7. When ThisEscape publishes the EventListener, it implicitly publishes the enclosing ThisEscape instance as well, because inner class instances contain a hidden reference to the enclosing instance.

Listing 3.7. Implicitly Allowing the this Reference to Escape. Don't do this.

public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}

3.2.1. Safe Construction Practices

ThisEscape illustrates an important special case of escape—when the this references escapes during construction. When the inner EventListener instance is published, so is the enclosing ThisEscape instance. But an object is in a predictable, consistent state only after its constructor returns, so publishing an object from within its constructor can publish an incompletely constructed object. This is true even if the publication is the last statement in the constructor. If the this reference escapes during construction, the object is considered not properly constructed.[8]

[8] More specifically, the this reference should not escape from the thread until after the constructor returns. The this reference can be stored somewhere by the constructor so long as it is not used by another thread until after construction. SafeListener in Listing 3.8 uses this technique.

Do not allow the this reference to escape during construction.

如何在 OuterClass 完成构建之前对此进行编码以到达 OuterClass?第一段斜体中提到的隐藏的内部类引用是什么?

最佳答案

请参阅 this article.那里清楚地解释了当你让 this 转义时会发生什么。

这里是 follow-up有进一步的解释。

这是 Heinz Kabutz 惊人的时事通讯,其中讨论了这个和其他非常有趣的话题。我强烈推荐它。

这是从链接中获取的示例,它显示了 如何 this 引用转义:

public class ThisEscape {
private final int num;

public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
num = 42;
}

private void doSomething(Event e) {
if (num != 42) {
System.out.println("Race condition detected at " +
new Date());
}
}
}

When it gets compiled, javac generates two classes. The outer class looks like this:

public class ThisEscape {
private final int num;

public ThisEscape(EventSource source) {
source.registerListener(new ThisEscape$1(this));
num = 42;
}

private void doSomething(Event e) {
if (num != 42)
System.out.println(
"Race condition detected at " + new Date());
}

static void access$000(ThisEscape _this, Event event) {
_this.doSomething(event);
}
}

Next we have the anonymous inner class:

class ThisEscape$1 implements EventListener {
final ThisEscape this$0;

ThisEscape$1(ThisEscape thisescape) {
this$0 = thisescape;
super();
}

public void onEvent(Event e) {
ThisEscape.access$000(this$0, e);
}
}

在这里,在外部类的构造函数中创建的匿名内部类被转换为一个包访问类,该类接收对外部类的引用(允许 this 转义的类)。为了让内部类能够访问外部类的属性和方法,在外部类中创建了一个静态包访问方法。这是access$000

这两篇文章都展示了实际的转义是如何发生的以及可能发生的情况。

“什么”基本上是一种竞争条件,当尝试使用尚未完全初始化的对象时,可能会导致 NullPointerException 或任何其他异常。在这个例子中,如果一个线程足够快,它可能会运行 doSomething() 方法,而 num 尚未正确初始化为 42。在第一个链接中有一个测试表明了这一点。

编辑:缺少有关如何针对此问题/功能进行编码的几行代码。我只能考虑坚持一套(可能是不完整的)规则/原则来避免这个问题和其他类似问题:

  • 仅在构造函数中调用 private 方法
  • 如果你喜欢肾上腺素并想从构造函数中调用 protected 方法,请执行此操作,但将这些方法声明为 final,这样它们就不会被子类覆盖
  • 从不在构造函数中创建内部类,无论是匿名的、本地的、静态的还是非静态的
  • 在构造函数中,不要将 this 作为参数直接传递给任何东西
  • 避免上述规则的任何传递组合,即不要在构造函数中调用的 privateprotected final 方法中创建匿名内部类
  • 使用构造函数只构造一个类的实例,并让它只初始化类的属性,无论是使用默认值还是使用提供的参数

如果您需要做更多的事情,请使用构建器或工厂模式。

关于java - `this` 引用外部类如何通过发布内部类实例转义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28676796/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com