第一个例子
public class MyClass {
final int x = 2;
}
x 是
第二个例子
public class MyOtherClass {
static final int x = 3;
}
x 是
我的问题是
两者有什么不同? (不包括创作时间)
我错过了什么吗?
答案 0 :(得分:6)
x
和MyClass
中MyOtherClass
之间的差异是:
第一个只能通过MyClass
实例访问,并且可以有多个常量副本。
可以在没有MyOtherClass
实例的情况下访问第二个,并且只能存在一个副本。
在您的示例中,在具有一个或多个常量实例之间没有实际差异 1 。但请考虑一下:
public class YetAnotherClass {
final int x;
public YetAnotherClass(int x) {
this.x = x;
}
}
...显示实例常量如何在不同的实例中具有不同的值。
1 - 这是夸大其辞。首先,static final int x = 3;
声明一个编译时常量,并且编译时常量可以用在switch case表达式中,其中非编译时常量可以是&t; t。其次,常量的非静态版本将在MyClass
的每个实例中占用空间。最后,如果你足够愚蠢地尝试使用反射来改变常量,那么行为会有所不同。 (只是不要这样做......)
答案 1 :(得分:1)
虽然final
,但JVM不进行任何优化或假设,因此该类的每个实例都将具有x
的实例。如果您声明成员static
,则该类的每个实例将共享同一个x
实例,因为它是静态分配的。此外,正如书面(x
具有包可见性),其他类可以静态访问x
,即没有可用类的实例。
答案 2 :(得分:1)
如果您创建两种类型的多个实例,那么
在第一种情况下,MyClass
的所有对象都有自己的final x
字段。
在第二种情况下,MyOtherClass
的所有对象都会指向一个final x
类字段,因为它本质上是静态的。
答案 3 :(得分:1)
差异: 静态属于类所以你可以在没有任何类的实例的情况下访问它,因此只能访问它的一个副本。
虽然要访问第二个,但您需要一个类的实例来访问它,这样您就可以拥有与静态最终Object一样多的副本,因为它属于Object。
您可以使用字节代码进行验证: 对于静态
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
非静态决赛
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_2
6: putfield #2 // Field x:I
9: return
LineNumberTable:
正如您所看到的那样, putfield 设置了objectref中标识的字段的值(对象的引用)