2009-10-28 65 views
2

传递最终对象(下面代码中的字符串)时,它从匿名内部类打印时显示为空。但是,当最终值类型或直接最终字符串传入时,其值正确显示。 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()变化的东西呢?

其他信息:此行为仅在Java 1.4中观察到,而不在Java 5中。有关解决方法的任何建议?在现有字符串上不调用toString()是公平的,但由于这只是一个例子,如果我在非String对象上执行它,它会有真实世界的影响。

+0

你在Java 1.4下看到了什么结果?我正在使用Java 6,并且我得到了(可能不会很好地格式化,并且没有预览注释): argh! 啊! innerargh! innerargh! innerargh! innerargh! 这不是对你有任何帮助,但EOL Notification for Java 1.4于2006年12月宣布,EOSL for Java 1.4是本周五一年前发布的。Java 5的EOL Notification是在2008年4月,它在本周五触及了EOSL。任何你可以升级到Java 6的机会? http://java.sun.com/products/archive/eol.policy.html – shoover 2009-10-28 21:09:37

+0

您将看到预期的输出。使用“argh!”时1.4行为是innernull。toString();. – 2009-10-28 23:11:52

回答

5

如果您在JLS中查看compile-time constants的部分,您会发现致电.toString()确实有所作为。像废话一样前缀false?null+"":

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

在这些情况下通常会出现这种情况,javap -c对于了解javac编译器在做什么很有用。该规范对理解为什么有用(应该有足够的耐心)。

+1

+1。你必须在你的床头柜上有JLS :-) – ChssPly76 2009-10-28 21:26:21

0

我的猜测是,当InnerThing()的构造函数将其this传递给InnerThing的匿名子类的打印方法,而对象未完全构造时,则会触发未定义的行为。这个this反过来依赖于对WierdInners的this的隐式引用。

调用.toString()将aString从编译时初始化为运行时。为什么未定义的行为在Java 1.4和1.5之间有所不同,这可能是JVM实现细节。

0

从超类构造函数中调用重写的方法是很危险的,因为它们将在对象的子类部分初始化之前调用。此外,访问封闭范围的最终变量的内部类将实际访问这些变量的副本(这就是为什么它们需要是最终的),并且这些复制的字段驻留在匿名子类中。

我怀疑bString的处理方式不同是因为它的值在编译时已知,这允许编译器内联子类中的字段访问,使得字段初始化的时间无关紧要。