- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
所以似乎通过this
是个坏主意来自 Java 中的构造函数。
class Foo {
Foo() {
Never.Do(this);
}
}
我的简单问题是:为什么?
For example if your class has a final (non-static) field, then you can usually depend on it being set to a value and never changing.
When the object you look at is currently executing its constructor, then that guarantee no longer holds true.
最佳答案
基本上,您已经列举了可能发生的坏事,因此您已经部分回答了您自己的问题。我将提供您提到的内容的详细信息:
分发 this
在初始化最终字段之前
For example if your class has a final (non-static) field, then you can usually depend on it being set to a value and never changing.
When the object you look at is currently executing its constructor, then that guarantee no longer holds true.
How is that?
this
在设置
final
之前字段,那么它不会被设置,但:
class X{
final int i;
X(){
new Y(this); // ouch, don't do this!
i = 5;
}
}
class Y{
Y(X x){
assert(x.i == 5);//This assert should be true, since i is final field, but it fails here
}
}
Y
看到一个
X
带有未初始化的
final
field 。这是一个很大的禁忌!
final
字段只初始化一次,在初始化之前不会被读取。一旦您泄漏,此保证将消失
this
.
final
上。同样糟糕的领域。然而,如果
final
,人们更惊讶。发现字段未初始化。
this
在基类构造函数中引用,您泄漏了尚未初始化其派生字段的对象。这个可以
class A{
static void doFoo(X x){
x.foo();
}
}
class X{
X(){
A.doFoo(this); // ouch, don't do this!
}
void foo(){
System.out.println("Leaking this seems to work!");
}
}
class Y extends X {
PrintStream stream;
Y(){
this.stream = System.out;
}
@Overload // Polymorphism ruins everything!
void foo(){
// NullPointerException; stream not yet initialized
stream.println("Leaking + Polymorphism == NPE");
}
}
X
与
foo
方法。
X
泄露给
A
在其构造函数和
A
中电话
foo
.对于
X
类,这很好用。但是对于
Y
类,一个
NullPointerException
被抛出。原因是
Y
覆盖
foo
并在其中使用其字段之一 (
stream
)。自
stream
当
A
时尚未初始化电话
foo
,您收到异常。
this
,从你的基类继承的类(它可能不是你写的,而是其他不知道泄露
this
的人)可能会炸毁一切。
this
给自己
this
,因为它带来了与泄漏到另一个类的引用类似的问题。例如,考虑前面带有不同
X
的示例。构造函数:
X(){
// A.doFoo();
foo(); // ouch, don't do this!
}
this
至
A
但我们通过拨打
foo
将其泄露给自己.同样,同样的坏事发生了:A 类
Y
覆盖
foo()
并使用它自己的领域之一会造成严重破坏。
final
field 。同样,通过某种方法泄露给自己可能允许找到
final
字段未初始化:
class X{
final int i;
X(){
foo();
i = 5;
}
void foo(){
assert(i == 5); // Fails, of course
}
}
foo
然后设置
i
是错的。但是现在再考虑继承:你的
X.foo()
方法甚至可能不使用
i
,所以在初始化
i
之前调用就可以了.但是,子类可能会覆盖
foo()
并使用
i
在其中,再次打破一切。
foo()
方法可能会泄漏
this
甚至通过将其传递给其他类。所以虽然我们只是打算泄漏
this
给我们自己打电话
foo()
子类可能会覆盖
foo()
并发布
this
到全世界。
this
可能值得商榷。但是,如你所见,它带来了类似的问题,所以我想在这里讨论一下,即使很多人可能不同意调用自己的方法被认为是泄漏的
this
.
final
或
static
方法,因为这些方法不能被无辜的派生类覆盖。
final
field 。例如,这可以通过在分配 final 字段的构造函数的末尾添加内存屏障来完成。
但是,一旦您分发 this
,此保证就会消失。太早了。 再举个例子:
class X{
final int i;
X(Y y){
i = 5;
y.x = this; // ouch, don't do this!
}
}
class Y{
public static Y y;
public X x;
Y(){
new X(this);
}
}
//... Some code in one thread
{
Y.y = new Y();
}
//... Some code in another thread
{
assert(Y.y.x.i == 5); // May fail!
}
this
为时过早,但只有
后 初始化
i
.所以在单线程环境中,一切都很好。但是现在进入并发:我们创建了一个静态
Y
(它接收受污染的
X
实例)并在第二个线程中访问它。现在,断言可能再次失败,因为现在编译器或 CPU 乱序执行
允许重新订购
i = 5
的分配以及
Y.y = new Y()
的分配.
{
Y.y = new Y();
}
rX
是本地寄存器):
{
r1 = 'allocate memory for Y' // Constructor of Y
r1.x = new X(r1); // Constructor of Y
Y.y = r1;
}
new X()
:
{
r1 = 'allocate memory for Y' // constructor of Y
r2 = 'allocate memory for X' // constructor of X
r2.i = 5; // constructor of X
r1.x = r2; // constructor of X
Y.y = r1;
}
r2.i = 5
到最后:
{
r1 = 'allocate memory for Y' // 1.
r2 = 'allocate memory for X' // 2.
r1.x = r2; // 3.
Y.y = r1; // 4.
r2.i = 5; // 5.
}
4.
的所有步骤。然后被中断(在设置
final
字段之前!)。现在,线程 2 执行所有代码,因此它的
assert(Y.y.x == 5);
失败。
this
逃避构造函数的那个代表一个部分构造的对象,通常你永远不想拥有部分构造的对象,因为它们很难推理(见我的第一个例子)。尤其是当继承发挥作用时,事情变得更加复杂。只需记住:泄漏 this
+ 继承 = 这里是龙。 this
内存模型放弃大部分保证在构造函数中,如此疯狂的重新排序可能会产生几乎不可能调试的非常奇怪的执行。只需记住:泄漏 this
+ concurreny = 这里是龙。 关于java - 从 Java 构造函数传递 'this' 到底有什么危险?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25281301/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!