我之前问过这个问题,但没有得到适当的答案。
如果非最终字段的值可以更改,那么如何在非匿名字段类中使用非最终字段?
class Foo{
private int i;
void bar(){
i = 10
Runnable runnable = new Runnable (){
public void run (){
System.out.println(i); //works fine
}//end method run
}//end Runnable
}//end method bar
}//end class Foo
如果必须在匿名类中使用局部变量,final
以使编译器可以在匿名类代码中内联其值,如下所示:
之前:
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}//end method run
};//end anonymous class
}//end method f
}//end class Access1
后:
public class Access1 {
public Access1() {}//end constructor
public void f() {
Access1$1 access1$1 = new Access1$1(this);
}//end method f
}//end class Access1
和
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}//end constructor
public void run() {
System.out.println(3);
}//end method run
private final Access1 this$0;
}//end class Access1$1
那么 ,编译器如何内联非final字段的值?
方法调用的局部变量(final
内部类必须可以访问)和实例的私有数据成员之间有很大的区别。
内部类可以访问包含的实例,也可以访问该实例的所有成员final
。不需要将它们定型,因为(在您的情况下)已通过引用它们Foo.this
。因此,在访问您的i
成员时,内部类实际上是在访问Foo.this.i
,只是如果引用没有它,就可以隐含Foo.this
(如this
)。
但是匿名类的代码无法以这种方式访问局部变量,因为它们(当然)不是包含类的实例成员。因此,相反,编译器做了一件非常有趣的事情:为每个局部变量创建 匿名
类的实例成员final
,并在创建匿名类的实例时,使用局部变量的值初始化这些成员。
让我们看一下它:
public class InnerEx {
public static final void main(String[] args) {
new InnerEx().test("hi");
}
private void test(String arg) {
final String localVar = arg;
Runnable r = new Runnable() {
public void run() {
System.out.println(localVar);
}
};
r.run();
}
}
编译后,我们得到InnerEx.class
和InnerEx$1.class
。如果我们反编译InnerEx$1.class
,我们将看到:
class InnerEx$1 implements java.lang.Runnable {
final java.lang.String val$localVar;
final InnerEx this$0;
InnerEx$1(InnerEx, java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LInnerEx;
5: aload_0
6: aload_2
7: putfield #2 // Field val$localVar:Ljava/lang/String;
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void run();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field val$localVar:Ljava/lang/String;
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
}
请注意,实例成员名为val$localVar
,这是为代表调用中的局部变量而创建的实例成员InnerEx#test
。
问题内容: 关于编译器错误,有一些关于Stack Overflow的主题,解决方案是“将其声明为最终的,您就完成了”,但是对于这个 理论性的 问题,我想检查一下该代码无法编译的逻辑原因是什么: (解决方案:声明为final),而这一点却做到了: 我真的很困惑 不是最终值,它可以多次更改,而的可怜参数只能在其方法体内更改,而是由编译器负责;) 甚至编译器错误也误导了我。: 与什么不同?与内部类不是在
问题内容: 使用JavaDoc,如何在类中引用最终静态字段的值? 我希望在此示例中将其替换为field的值。 问题答案: 你的意思是?
问题内容: 我试图编译其中一个Java类与拒绝 非法向前引用 错误,其中有问题的参考是词汇 后 的基准场。在显示相同行为时,将尽可能精简以下类: 并且的许多用途仅用作占位符,以删除不相关的代码段。 使用编译时,javac会显示以下错误消息: 因此,编译器抱怨的声明引用,而应在的声明范围内。但是,一旦删除了in 的声明的引用(例如,通过将第5行从更改为),编译器将接受该类。 如何解释呢?我对 前锋的
在此代码示例中,ActionListener的actionPerformed函数中没有使用最终对象jLabel:
问题内容: 让我们从一个简单的测试用例开始: 任何人都在乎猜测什么将作为输出打印(在底部显示,以免立即破坏惊喜)。 问题是: 为什么原始和包装的整数表现不同? 为什么反射访问与直接访问返回不同的结果? 最困扰我的人-为什么String表现得像原始的而不是像? 结果(java 1.5): 问题答案: 内联编译时常量(在javac编译时)。参见JLS,尤其是15.28定义了常量表达式,而13.4.9讨
定义不可变类的策略表明 所有字段都应该是最终字段。 对于ex: 为什么一定要最终决定? 因为我没有给出setter方法吗?它不能改变。谢谢。