Java枚举实例的生命周期
Java枚举实例的生命周期
我知道枚举类型的预定义常量每个都只有一个实例。这是正确的吗?现在假设有一个名为value
的实例变量,假设ONE
是一个预定义常量。如果修改ONE
实例的value
,那么会影响所有引用到ONE
的变量。但是当垃圾收集器清除所有枚举实例时,value
的值会发生什么变化呢?它们会被丢弃吗?\n示例\n这里有一个简单的例子,有两个变量A
和B
,都引用到ONE
。在B
第一次使用之前,value
的值被取反。如果垃圾收集器在Number B = Number.ONE;
这行之前清除了ONE
的实例,那么value
的值将会被“重置”回1,我猜是这样的。这样理解对吗?\n枚举类型:\n
public enum Number { ONE(1), TWO(2), THREE(3); int value; Number(int value) { this.value = value; } void negate() { value = -value; } @Override public String toString() { return "value: " + value; } }
\n主方法:\n
public static void main(String[] args) { Number A = Number.ONE; A.negate(); Number B = Number.ONE; System.out.println(A + " " + B); }
\n输出:\n
\nvalue: -1 value: -1\n
\n所以我的问题是,输出是否可能是以下内容(如果在使用B
之前添加了A = null;
)?\n
\nvalue: -1 value: 1\n
Life span of a Java enum instance
在Java中,枚举(enum)是一种特殊的数据类型,它限定了一个变量只能取一组预定义的值。在枚举中,每个值都是一个实例,这些实例在定义枚举类型时被创建。然而,枚举实例的生命周期与类的生命周期是不同的。
对于以下的枚举类型FooEnum:
public enum FooEnum { CONST }
Java编译器会生成如下的字节码:
Compiled from "FooEnum.java" public final class FooEnum extends java.lang.Enum{ public static final FooEnum CONST; static {}; Code: 0: new #1 // class FooEnum 3: dup 4: ldc #12 // String CONST 6: iconst_0 7: invokespecial #13 // Method " ":(Ljava/lang/String;I)V 10: putstatic #17 // Field CONST:LFooEnum; 13: iconst_1 14: anewarray #1 // class FooEnum 17: dup 18: iconst_0 19: getstatic #17 // Field CONST:LFooEnum; 22: aastore 23: putstatic #19 // Field ENUM$VALUES:[LFooEnum; 26: return public static FooEnum[] values(); // 省略 public static FooEnum valueOf(java.lang.String); // 省略 }
其中,CONST是一个final static变量,其生命周期与类的生命周期相同。
根据Java语言规范(Java SE 8版本)第8.9节:
枚举类型除了由其枚举常量定义的实例之外,没有其他实例。显式地实例化枚举类型是一个编译时错误(§15.9.1)。
除了编译时错误之外,还有三种机制确保枚举类型的实例不会超出其枚举常量的定义:
Enum类中的final clone方法确保枚举常量无法被克隆。
禁止通过反射实例化枚举类型。
序列化机制的特殊处理确保反序列化时不会创建重复的实例。
因此,枚举实例的生命周期与类的生命周期相关联。只有在类被垃圾回收时,枚举实例才会被垃圾回收。这也意味着,即使枚举实例不再被使用,只要类还存在于内存中,枚举实例就不会被垃圾回收。
以上是关于Java枚举实例生命周期的解释和说明。希望对你有所帮助!
Java中的枚举实例是隐式的`public static final`字段,因此它们永远不会被垃圾回收。
在具有多个类加载器的jvm实例中,情况会稍微复杂一些。然后,当它们的类被卸载时,静态字段的值可能会消失。
然而,默认情况下,只有一个类加载器在jvm实例存活期间是活动的,因此在您的示例应用程序中,静态字段永远不会被垃圾回收。
它们不是"effectively" `public static final`,它们就是。
好的,为了澄清,`public static final`意味着实例将永远驻留在内存中,即使对它的所有引用都丢失了?
实际上,这并不能严格保证。如果它们的类加载器被垃圾回收,它们可能会随之消失。
是的,而且正是这三个修饰符中的`static`会阻止引用数据被垃圾回收。一旦类被类加载器加载和初始化,其静态数据就存在于类加载器实例的整个生命周期内。
不。它只保证在类加载器的生命周期内存在。如果您有一个加载它们的自定义类加载器,那么它们可能会被卸载。
确实如此。我只是想简化解释,因为在OP提供的示例应用程序中只有一个类加载器。我无论如何编辑了我的评论;谢谢您的备注。
是的,更好的说法是"implicitly"。我相应地编辑了答案。谢谢。
从上述可以看出,在正常情况下,只有一个类加载器,因此静态字段永远不会被垃圾回收。什么时候会有多个类加载器?也许这是一个太广泛的话题,在这里讨论太复杂了。类加载器和不同的JVM的概念超出了我对Java的了解。
现代应用服务器例如使用多个类加载器。您可以将多个不同的应用程序部署到同一个应用服务器实例中,以便它们都在同一个jvm实例(应用服务器实例)中运行。但是应用程序彼此独立,每个应用程序都有自己的类加载器,尽管它们共享同一个jvm实例、同一个堆等。从技术上讲,在底层事情又变得更加复杂了,因为应用服务器可以使用多个类加载器来加载应用程序所使用的应用服务器模块。
但是,重点是相同的。在应用服务器示例中,从部署的应用程序的角度来看,静态字段(包括枚举)将不会在应用程序被取消部署或应用服务器关闭之前被卸载/垃圾回收,这意味着静态状态在部署的应用程序的整个生命周期中保持不变。