package test;
public class A {
private static A a = new A();
private B b = B.getInstance();
private A() {
}
public static A getInstance() {
System.out.println("A被调用");
return a;
}
public void test() {
System.out.println(b);
}
}
package test;
public class B {
private static B b = new B();
private A a = A.getInstance();
private B() {
}
public static B getInstance() {
System.out.println("B被调用");
return b;
}
public void test() {
System.out.println(a);
}
}
package test;
/**
* 面先说一下环境,比如现在有两个类,A和B,两个类都是单例类,这个时候如果A有个B的实例变量,B有个A的实例变量,
* 会发生什么情况呢?开始我以为会出现栈溢出。但是让我迷惑的是,居然没问题。只是其中一个类的 实例变量会是NULL。
* 下面看代码。
* @author zhang_zengmin
*
*/
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
B b = B.getInstance();
b.test();
System.out.println("==========");
A a = A.getInstance();
a.test();
// A a = A.getInstance();
// a.test();
// System.out.println("==========");
// B b = B.getInstance();
// b.test();
// System.out.println("==========");
/**
* 1.类Test的main()方法的B.getInstance()生成的invokestatic指令触发了类B的初始化
2.执行类B的<clinit>方法的过程中,显式调用了B自己的<init>方法(static b = new B())
3.B的<init>方法中,调用A.getInstance()生成的invokestatic指令触发了A的初始化(private A a = A.getInstance())
4.执行A的<clinit>方法的过程中调用了A自己的<init>方法(static a = new A())
5.类A的<init>方法中需要调用B.getInstance(),但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次,因此不会再触发B的初始化,既不会再执行B.<clinit>方法。
6.那由a.<init>触发的B.getInstance()被执行,输出第一行“B被调用”,B.getInstance()方法结束。虽然这时候B的初始化阶段尚未结束,但是解析阶段已经完成,所以getInstance()方法可以被正确执行,但这时候静态字段static B b仍然为null(注意,b.<init>方法还没完呢),所以这个B.getInstance()方法返回值为null,所以A.b为null。
7.A.<clinit>方法结束,第3步的invokestatic指令正式执行,即A.getInstance()被执行,输出第二行“A被调用”,返回A的实例,这时候B.a不为null了。
8.B.<clinit>方法结束,第1步的invokestatic指令正式执行,即B.getInstance()被执行,输出第三行“B被调用”
9.B.test()方法被执行,输出B.a的toString()方法结果,即第四行“A@35ce36”。
10.A.getInstance()方法被执行,输出第五行“A被调用”。
11.A.test()方法被执行,输出null,即A.b的值。
*/
}
}
分享到:
相关推荐
- **JVM初始化步骤**:包括加载、验证、准备、初始化和使用。 - **类初始化的时机**:类加载后,首次主动使用类时(如创建类实例、访问静态字段、调用静态方法等)会触发初始化。 - **JVM结束生命周期的情况**:程序...
- `init`: 输出JVM初始化时的信息。 - 示例:`-verbose:class` 6. **-version**: 显示JVM的版本信息。 - 示例:`-version` #### 高级参数 1. **-D**<property>=<value>: 设置系统属性。 - 示例:`-Dfile....
静态初始化块是以`static`关键字标识的代码块,它在类被加载到Java虚拟机(JVM)时执行,且只执行一次。静态初始化块常用于设置类级别的变量,或者执行只应执行一次的初始化操作。例如,如果一个类需要在程序运行前...
- `-Xms`:JVM初始化堆的大小,即虚拟机启动时向系统申请的初始内存大小。 - `-Xmx`:JVM堆的最大值,定义了虚拟机可以使用的最大内存空间。 合理设置这两个参数对于避免内存溢出和提高应用性能极为重要。通常,...
-Xms 用于设置 JVM 初始化堆的大小,-Xmx 用于设置 JVM 堆的最大值。这些值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在...
在Java编程语言中,程序初始化的顺序是一个关键概念,它涉及到类加载、对象创建以及执行流程的安排。了解这些顺序对于编写高效、无错误的代码至关重要。以下是对Java程序初始化顺序的详细说明: 1. **类加载阶段**...
如果多个线程同时尝试初始化同一个类,JVM会保证类的初始化仅执行一次。这是通过类锁实现的,确保了线程安全。 在分析`init`目录中的文件时,你可以看到这些概念的实际应用。例如,可能会有一个类包含静态和非静态...
Java虚拟机JVM类加载初始化是Java程序运行过程中的关键环节,它负责将类的字节码文件加载到内存中并进行相应的处理,以便程序能够正确执行。在Java中,类加载器(Classloader)扮演着核心角色。下面将详细讨论类加载...
java JVM 类加载-初始化 过程
1. **静态成员变量的初始化**:当类被加载到JVM中时,静态成员变量首先被初始化。 2. **静态初始化块的执行**:紧随静态成员变量之后,静态初始化块被执行。 3. **父类静态成员变量及初始化块**:如果当前类继承自另...
这意味着静态初始化块在类被加载到JVM中时执行,而不是在创建每个对象时执行。静态初始化块主要用于初始化类级别的变量,即静态变量。由于它是在类加载时执行,所以它总是在任何对象创建之前运行。静态初始化块不能...
之后,类的初始化(初始化阶段)才会进行,包括执行静态初始化块和设置静态变量。 ClassLoader的层次结构通常包括Bootstrap ClassLoader(启动类加载器)、Extension ClassLoader(扩展类加载器)和AppClassLoader...
5. **错误处理**:源码中包含了对各种可能的错误情况的处理,比如JVM初始化失败或Java程序执行异常,这些都会通过Windows服务的错误处理机制报告。 6. **服务卸载**:当不再需要服务时,JavaService提供了卸载服务...
7. **处理异常**:如果在启动过程中出现错误,如找不到主类、JVM初始化失败等,Java Launcher会捕获这些异常并显示相应的错误信息。 Java Launcher的灵活性允许开发者通过各种方式定制应用程序的启动行为,例如通过...
JVM初始化类的步骤可以分为五步:首先,假如这个类还没有被加载和连接,程序先加载并连接该类。其次,假如该类的直接父类还没有被初始化,则先初始化其直接父类。然后,假如类中有初始化语句,则系统依次执行这些...
`<clinit>`方法就是JVM在类加载时用于执行这些初始化操作的特殊方法。它在类被首次加载时调用,且只调用一次。在上述代码示例中,`debug.MyTest`类中的静态变量`i`除以0导致`ArithmeticException`,异常信息显示在`...
1. **命令行启动应用时由JVM初始化加载**。 2. **通过`Class.forName()`方法动态加载**。 3. **通过`ClassLoader.loadClass()`方法动态加载**。 其中,`Class.forName()`和`ClassLoader.loadClass()`的主要区别在于...
在Java虚拟机(JVM)中,类的初始化会在类加载和连接完成后发生。 初始化阶段的目标是确保类的静态成员被正确地初始化。有两种主要的方式进行类的初始化: 1. **静态变量的声明处赋值**:这是最直接的方式,如`...
当Java虚拟机(JVM)首次遇到某个类的实例或者静态变量,或者当类的静态方法被调用时,会触发类的加载和初始化。类的初始化主要涉及到以下几个步骤: 1. 类加载:JVM会通过类加载器将类的.class文件加载到内存中。 ...