通过对象访问时静态最终字段和在声明时初始化的实例最终字段之间的差异
DIfference between a static final field and an instance final field initialized at declaration when accessed through an object
第一个例子
public class MyClass {
final int x = 2;
}
x 是
- final表示初始化后永远无法设置
- 在声明时初始化,这意味着以后不能为它分配不同的值(即使在构造函数中)。
- fixed(无论实例如何)因为它的值不能在构造函数中更改(或其他任何地方)。
第二个例子
public class MyOtherClass {
static final int x = 3;
}
x 是
- final表示初始化后永远无法设置
- 在声明时初始化,这意味着以后不能为它分配不同的值。
- a static 字段和值将始终保持不变,无论实例如何。
- constant 因为它既是 static 又是 final.
我的问题是
两者有什么区别? (不含创建时间)
我是不是漏掉了什么?
虽然final
,但是JVM没有做任何优化和假设,所以class的每个实例都会有一个x
的实例。如果声明成员 static
,class 的每个实例将共享 x
的相同实例,因为它是静态分配的。此外,如所写(x
具有包可见性),其他 classes 可能会静态访问 x
,即没有可用的 class 实例。
如果您为这两种类型的 classes 创建了多个实例,那么,
在第一种情况下,MyClass
的所有对象都将拥有自己的 final x
字段。
在第二种情况下,MyOtherClass
的所有对象都指向一个 final x
class 字段,因为它本质上是静态的。
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 表达式中,而非编译时常量不能。其次,常量的非静态版本将在 MyClass
的每个实例中占据 space。最后,如果您足够愚蠢地尝试使用反射来更改常量,那么行为将会有所不同。 (千万别做...)
区别:
静态属于 class 因此您可以在没有 class 的任何实例的情况下访问它,因此只有它的一个副本。
虽然要访问第二个,您需要 class 的实例来访问它,这样您就可以拥有与属于 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(对对象的引用)中标识的字段的值
第一个例子
public class MyClass { final int x = 2; }
x 是
- final表示初始化后永远无法设置
- 在声明时初始化,这意味着以后不能为它分配不同的值(即使在构造函数中)。
- fixed(无论实例如何)因为它的值不能在构造函数中更改(或其他任何地方)。
第二个例子
public class MyOtherClass { static final int x = 3; }
x 是
- final表示初始化后永远无法设置
- 在声明时初始化,这意味着以后不能为它分配不同的值。
- a static 字段和值将始终保持不变,无论实例如何。
- constant 因为它既是 static 又是 final.
我的问题是
两者有什么区别? (不含创建时间)
我是不是漏掉了什么?
虽然final
,但是JVM没有做任何优化和假设,所以class的每个实例都会有一个x
的实例。如果声明成员 static
,class 的每个实例将共享 x
的相同实例,因为它是静态分配的。此外,如所写(x
具有包可见性),其他 classes 可能会静态访问 x
,即没有可用的 class 实例。
如果您为这两种类型的 classes 创建了多个实例,那么,
在第一种情况下,MyClass
的所有对象都将拥有自己的 final x
字段。
在第二种情况下,MyOtherClass
的所有对象都指向一个 final x
class 字段,因为它本质上是静态的。
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 表达式中,而非编译时常量不能。其次,常量的非静态版本将在 MyClass
的每个实例中占据 space。最后,如果您足够愚蠢地尝试使用反射来更改常量,那么行为将会有所不同。 (千万别做...)
区别: 静态属于 class 因此您可以在没有 class 的任何实例的情况下访问它,因此只有它的一个副本。
虽然要访问第二个,您需要 class 的实例来访问它,这样您就可以拥有与属于 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(对对象的引用)中标识的字段的值