Java 传入匿名内部类时为Null对象

Java 传入匿名内部类时为Null对象,java,inner-classes,java1.4,Java,Inner Classes,Java1.4,当传递最后一个对象(下面代码中的字符串)时,它从匿名内部类打印时显示为null。但是,当传入最终值类型或直接最终字符串时,其值将正确显示。在匿名内部类的上下文中,final真正意味着什么?为什么对象被传递为null public class WeirdInners { public class InnerThing { public InnerThing() { print(); } pub

当传递最后一个对象(下面代码中的字符串)时,它从匿名内部类打印时显示为null。但是,当传入最终值类型或直接最终字符串时,其值将正确显示。在匿名内部类的上下文中,
final
真正意味着什么?为什么对象被传递为null

public class WeirdInners
{
    public class InnerThing
    {
        public InnerThing()
        {
            print();
        }

        public void print(){

        }
    }

    public WeirdInners()
    {
        final String aString = "argh!".toString();
        final String bString = "argh!";
        System.out.println(aString);
        System.out.println(bString);


        InnerThing inner =new InnerThing(){
            public void print()
            {
                System.out.println("inner"+aString); // When called from constructor, this value is null.
                System.out.println("inner"+bString); // This value is correctly printed.
            }
        };

        inner.print();
    }


    public static void main(String[] args)
    {
        WeirdInners test1 = new WeirdInners();
    }

}
这对我来说是非常奇怪的行为,因为人们期望字符串是一个对象,为什么调用
toString()
会改变事情呢


其他信息:这种行为只在Java1.4中观察到,在Java5中没有。有什么解决办法的建议吗?不在现有字符串上调用
toString()
已经足够公平了,但由于这只是一个示例,如果我在非字符串对象上执行它,它将具有现实意义。

如果您查看JLS中有关
编译时常量的部分,您将看到调用
.toString()
确实会有所不同。像前缀为
false?null+”:
这样的废话也是如此

这里重要的是设置封闭字段和构造函数的相对顺序。如果使用
-target 1.4
或更高版本(这不是1.4中的默认设置!),则在调用super之前会复制字段。对于1.3之前的规范,这是非法字节码


在这些情况下,
javap-c
有助于查看javac编译器正在做什么。该规范有助于理解原因(如果您有足够的耐心)。

我的猜测是,当InnerThing()的构造函数(隐式)将其
this
传递给InnerThing的匿名子类的print方法时,您会触发未定义的行为,而对象尚未完全构造。这个
This
又依赖于对wierdinner的
This
的隐式引用


调用
.toString()
将aString的初始化从编译时移动到运行时。Java 1.4和1.5之间未定义行为不同的原因可能是JVM实现细节。

从超类构造函数调用重写的方法是危险的,因为它们将在初始化对象的子类部分之前被调用

此外,访问封闭范围的最终变量的内部类实际上将访问这些变量的副本(这就是为什么它们必须是最终的),并且这些复制的字段驻留在匿名子类中


我怀疑对
bString
区别对待的原因是它的值在编译时已知,这允许编译器将字段访问内联到子类中,使得字段初始化的时间无关紧要。

您在Java 1.4下看到了什么结果?我正在使用Java6,我得到了(可能格式不好,并且没有评论预览):啊!啊!啊!啊!啊!啊!这并不是说它对您有任何帮助,而是Java1.4的EOL通知是在2006年12月发布的,Java1.4的EOSL是在一年前的这个星期五发布的。Java5的EOL通知是在2008年4月发布的,它在本周五发布了EOL。有没有可能升级到Java 6?您正在看到预期的输出。使用“argh!”.toString()时,1.4行为为innernull;。