用Java写代码的时候,感觉用的最多的便是new了吧,这几天在恶补Java基础知识的时候突然想到,我们在new一个对象时JVM具体都做了什么操作,且它们的顺序是什么样子的呢?于是我就写了一个小Demo,通过查看运行结果和Debug来看一下
首先定义一个Person类:
public class Person {
private int age = 1;
static String country = "CN";
//静态代码块
static {
System.out.println("static code block 1");
}
//构造代码块
{
System.out.println("constructed code block");
}
Person() {
System.out.println("I'm a person");
}
//这个静态代码块放在最后,用来测试执行顺序
static {
System.out.println("static code block 2");
}
}
然后写一个类用来验证结果:
public class UsingStaticCodeBlock {
public static void main(String[] args) {
System.out.println("begin");
Person p1 = new Person();
}
}
- 在
Person p1 = new Person();
这一行前加一个断点,开始进行Debug。首先在控制台打印出"begin"这就不多说了
static String country = "CN";
而不是int age = 1;
故可以证明,类加载时会把其中的静态成员变量放到方法区(也叫全局区或者静态区)中,并进行默认初始化和赋值(此处为null->"CN")
继续F5进行调试,程序来到了第一个静态代码块,按F6执行后,直接跳到第二个静态代码块,再按F6,输出结果
可以看出,静态代码块在静态变量初始化和赋值后执行,执行顺序由上而下
- 按F6跳回UsingStaticCodeBlock.java,再按F5跳转到构造方法
Person()
处
private int age = 1;
这一行
Person p1 = new Person()
执行完毕,此时new新建对象就全部完成了,对象的首地址赋给了存在栈内存中的变量p1
,使p1
指向了这个对象
简单的一条new语句,执行起来却复杂的很。但是为什么在调试过程中会跳回UsingStaticCodeBlock.java一次呢?经过查看Debug窗口的方法调用信息发现,第1步与第2步实际调用的方法是不一样的
通过查阅资料知道,首先进行的是类初始化,然后再进行实例初始化
结论
- 首先加载类到内存中
- 如果有,初始化静态变量并赋值,然后执行静态代码块
- 在堆内存中开辟空间,分配内存,初始化实例变量并赋值(如果有),然后执行构造代码块和构造函数
- 对象首地址赋给栈内存中的引用变量