Java 单例早期初始化、静态字段初始化和类加载
请考虑以下代码:Java 单例早期初始化、静态字段初始化和类加载,java,Java,请考虑以下代码: class MyClass { private static MyClass myobj = new MyClass(); private MyClass() { } public static MyClass getMyobj() { return myobj; } } 1) 在上面的代码中,当加载Myclass或getMyobj()时,when将mybj初始化为Myclass.getMyobj() 2) 假设我
class MyClass {
private static MyClass myobj = new MyClass();
private MyClass() {
}
public static MyClass getMyobj() {
return myobj;
}
}
1) 在上面的代码中,当加载Myclass
或getMyobj()
时,when将mybj
初始化为Myclass.getMyobj()代码>
2) 假设我们调用两次:
MyClass.getMyobj();
MyClass.getMyobj();
它会在第二次调用时创建新的MyClass()
对象吗
您的类将在何时加载
不,它不会创建另一个对象,它将使用已经存在的对象
1) 在上面的代码中,myobj何时初始化
第一次加载类时
2) 假设我们调用两次:…它会在第二次调用时创建新的MyClass()对象吗
否主要是因为myobj
是静态的,所以getMyobj()
将始终返回相同的实例,即加载时创建的实例
当MyClass被加载时
即使调用它十次,MyClass也只存在一个实例
一旦加载MyClass,它就会被初始化。但它只会初始化一次。正如前面所指出的,初始化类时将创建对象,并且由于该方法只返回它,因此示例代码中不会创建其他此类实例
但是,如果要执行此操作,还可以使用单个实例创建enum
:
public enum MyClass {
myobj;
}
然后,需要使用myobj
的代码可以简单地访问MyClass.myobj
字段,而代码本身保留单例行为。当然,类似的效果也可以通过公共静态final
字段实现
枚举的好处是编译器知道您不希望意外创建任意对象。它的缺点是,如果您想要延迟初始化,则需要为延迟加载的部分引入额外的委托 说一个类一加载就被初始化是不正确的。只有在运行的程序创建该类的实例(通过使用new关键字调用其构造函数)或使用属于该类的任何静态方法或字段时,才会初始化该类
有关初始化的JVM规则,请参阅
我应该指出,我之所以知道这一点,是因为Joshua Bloch的优秀著作《高效Java,第二版》。第71项提供了关于延迟初始化和“延迟初始化持有者类习惯用法”的建议
因此,在回答您的问题时,MyClass.myobj
只有在运行的程序(例如main()
方法)实际调用MyClass.getMyobj()
时才会初始化,而不是在那之前的片刻
因为每个类只初始化一次静态字段,所以下次调用MyClass.getMyobj()
时,它将简单地返回MyClass.myobj
的现有值,因此您将获得对完全相同的MyClass对象的两个引用。2。不,它只创建一次,因为静态初始值设定项只运行一次。当它加载到JVM中时,它会创建一次。您应该了解Java中对象的生命周期。第一个搜索结果之一似乎涵盖了您感兴趣的内容:最好检查“myobj”是否为null。如果为null,则生成它,否则不必进行初始化即可加载类。Tom Hawtin是对的。在您通过调用类的一个方法或访问它的一个字段来强制它初始化之前,类不会初始化。如果您的代码只调用条件循环中的静态方法,并且在软件的特定运行期间从未满足该条件,则在该运行期间不会初始化该类。因此,在绝对必要之前,类字段不会使用内存。@TomHawtin tackline:考虑到在加载类时静态初始化块只执行一次。对于类(静态)成员,不也应该这样吗?这就是我回答问题时遵循的逻辑。按照@user1515834的逻辑,类的类成员在第一次需要之前就被初始化了。这不是效率低下吗?在启动应用程序的主线程之前,在初始化阶段初始化当前应用程序中需要的所有类不是更好吗。如果我们遵循这条推理路线,如果循环从未在特定的运行中输入,则该类甚至不必加载。@Razvan我不知道这是否更有效。我只知道一个兼容的JVM是根据JLS运行的,JLS说一个类直到运行代码需要时才初始化。在程序运行开始时发生的初始化阶段永远不知道以后会满足哪些条件(例如,通过用户输入),因此它永远不知道要提前初始化哪些类。不,你可以加载一个类而不初始化它。@TomHawtin-tackline:我说的是OP提出的具体问题。实际上,MyClass
可以加载而不初始化。Tom Hawtin是对的。有关初始化的JVM规则,请参阅。