目录
二、demo中提到的类加载发生在什么时候,是程序编译还是程序运行时?
三、只要调用demo类JobFailMonitorHelper的任意方法,该类都会加载吗?
四、demo中提到的单例模式,此单例模式和双重检验锁实现的单例模式有什么区别?
一、简单demo解释发生过程
public class JobFailMonitorHelper {
private static JobFailMonitorHelper instance = new JobFailMonitorHelper();
public static JobFailMonitorHelper getInstance() {
return instance;
}
public static void print() {
System.out.println("test");
}
}
上述类中,JobFailMonitorHelper
类定义了一个私有的静态实例变量 instance
,并提供了一个公共的静态方法 getInstance()
来获取该实例。
当在代码中调用 JobFailMonitorHelper.getInstance()
时,发生了以下过程:
-
初始阶段:
- 类加载器加载
JobFailMonitorHelper
类。 instance
静态变量被初始化为null
。getInstance()
方法被放入方法区,并关联到JobFailMonitorHelper
类。
- 类加载器加载
-
调用
JobFailMonitorHelper.getInstance()
:- 在第一次调用
getInstance()
方法时,由于instance
变量为null
,进入下一步。 - 执行
new JobFailMonitorHelper()
创建一个新的JobFailMonitorHelper
对象。 - 将新创建的对象的引用赋值给
instance
变量。 - 返回
instance
变量的引用。
- 在第一次调用
-
后续调用
JobFailMonitorHelper.getInstance()
:- 因为
instance
已经被赋值为非null
的对象引用,直接返回instance
变量的引用,无需再创建新的对象。
- 因为
总结:
- 在第一次调用
JobFailMonitorHelper.getInstance()
方法时,会创建一个新的JobFailMonitorHelper
对象并将其赋值给instance
变量。 - 后续调用
JobFailMonitorHelper.getInstance()
方法时,直接返回已经创建的JobFailMonitorHelper
对象的引用,不会再创建新的对象。 - 这种方式称为单例模式,通过使用一个静态的实例变量和一个公共的静态方法来确保只有一个实例被创建并提供全局访问点。
二、demo中提到的类加载器加载发生在什么时候,是程序编译还是程序运行时?
类加载发生在程序运行时,而不是编译时。
类加载是Java虚拟机(JVM)在运行Java程序时的一个过程,它负责将字节码文件(编译后的Java类文件)加载到内存中,并进行初始化和链接操作,使得程序能够正确执行。
类加载过程包括以下几个阶段:
-
加载(Loading):查找并加载字节码文件到内存中。这个过程可以通过类加载器来完成,类加载器根据类的全限定名在类路径下查找对应的字节码文件,并读取到内存中。
-
链接(Linking):
- 验证(Verification):验证被加载的字节码文件的正确性,包括语法检查、字节码验证等。
- 准备(Preparation):为类的静态变量分配内存空间,并设置默认初始值。
- 解析(Resolution):将类的符号引用转换为直接引用,即将常量池中的符号引用替换为直接指向内存中类、方法、字段的指针。
-
初始化(Initialization):为类的静态变量赋予初始值,执行静态代码块。这是类加载的最后一个阶段,也是类加载过程中的关键阶段。
总之,类加载发生在程序运行时,它是将字节码文件加载到内存中,并对类进行初始化和链接的过程。编译只是将源代码编译成字节码文件的一个步骤,类加载发生在程序运行时,实际执行程序时加载类并使用它们。
三、只要调用demo类JobFailMonitorHelper的任意方法,该类都会加载吗?
其实这个问题是不严谨的,如果是第一次调用JobFailMonitorHelper的任意方法,该类会被加载,之后再调用就不会加载了。
类加载是指将类的字节码文件加载到内存中,并在内存中创建对应的Class对象的过程。当您调用JobFailMonitorHelper
的任意方法时,并不意味着该类会被加载。类加载是在首次使用类的时候发生的,而不是在类被定义或声明的时候。当您第一次使用JobFailMonitorHelper
类时,例如通过调用JobFailMonitorHelper.test()
方法,才会触发类的加载。
类加载的触发条件可以是以下几种情况之一:
- 实例化对象:通过
new
关键字创建类的实例。 - 访问静态成员:访问类的静态字段或静态方法。
- 继承关系:当一个类继承自另一个类时,子类的加载会引起父类的加载。
- 调用反射API:使用反射机制调用类的方法或访问字段。
一旦类被加载到内存中,JVM会在方法区创建一个对应的Class对象来表示该类,并提供类的相关信息给JVM使用。
需要注意的是,类加载是一种懒加载(Lazy Loading)机制。即只有在需要使用该类时才会进行加载,以减少不必要的资源消耗。一旦类被加载到内存中,后续的调用将直接使用已加载的类,而不会再次触发类的加载过程,除非类被卸载或重新加载。
总结:类加载是将类的字节码文件加载到内存中的过程,它是在首次使用类的时候发生的。当您调用JobFailMonitorHelper
的任意方法时,并不意味着类会被加载,只有在首次使用该类时才会进行类加载。
四、demo中提到的单例模式,此单例模式和双重检验锁实现的单例模式有什么区别?
有一些区别。
-
饿汉式单例模式:
- 在demo中,
JobFailMonitorHelper
使用的是饿汉式单例模式。 - 在类加载时就创建了一个实例对象,并在静态变量中持有该实例。
- 优点是实现简单、线程安全,且在多线程环境下能够保证只有一个实例被创建。
- 缺点是无论是否使用该实例,都会在类加载时创建,可能会占用一定的资源,特别是在实例较为庞大或初始化耗时较长时。
- 在demo中,
-
双重检验锁单例模式:
- 双重检验锁单例模式使用了懒加载的方式,即在第一次获取实例时才进行实例化。
- 在第一次调用
getInstance()
方法时,通过双重检验锁的方式,确保只有一个线程创建实例对象。 - 优点是实现了延迟加载,只在需要时才创建实例,节省了资源。
- 缺点是实现稍微复杂一些,需要考虑多线程环境下的线程安全性。
总结:
- 饿汉式单例模式在类加载时就创建实例,保证了线程安全性,但可能会产生不必要的资源占用。
- 双重检验锁单例模式实现了延迟加载和线程安全,但代码相对复杂一些。
- 选择使用哪种方式取决于具体需求和场景。如果资源消耗较小且希望在类加载时就创建实例,可以使用饿汉式单例。如果希望延迟加载且需要考虑多线程环境下的线程安全,可以考虑使用双重检验锁单例。