我们先看看类的加载过程,在我们运行java代码时,首先会通过编译器将.java的文件编程成.class文件,之后在通过类加载器将.class文件转为字节码加载到jvm运行时数据区里面,整个加载过程如下
上图只是类的加载过程,还没有创建对象; 那如果创建对象的话,比如执行 new Object(); 这段代码,里面主要有2个步骤,分别是实例化和初始化
如果创建对象的是有引用变量,那就是这样的
实例化(半初始化)
对象实例化的过程,其实也叫半初始化,就是时候你创建对象的过程中创建到一半的情况下,拿下面的代码来举例:
package com.test;
public class Test {
public static void main(String[] args) throws InterruptedException {
Obj object_1 = new Obj();
}
}
class Obj {
int i = 13;
}
首先会为该对象分配一块堆内存用来存放对象和父类的属性实例,new这个关键字就是用来分配内存的,并且给对象和父类的属性进行复制,这里的赋值都是给默认值,也就是零值;
初始化和引用赋值
初始化就需要给属性赋值真正的数据了,这个数据是用户给定的,如果是引用的话,就会将引用变量指向给定的地址,并且调用初始化方法
init() 方法:就是我们说的构造器,构造器就包括 构造方法、{}包住的代码等。
clinit()方法: <clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}语句)中的语句合并产生的,编译器收集的顺序是有语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块中可以赋值,但不能访问。
引用赋值
引用赋值是先分配一块栈内存来存放 object_1 并将栈内存的object_1指向堆内存的对象实例,这也是对象创建的最后一步操作;引用赋值完成后,整个对像就创建完成了!
init() 与 clinit() 加载顺序
当一个类初始化的时候,先调用<cinit>方法 ,然后在继续执行<init>
我们用用代码测试下
package com.test;
/**
* 类初始化
*/
public class Init {
public static void main(String[] args) {
ChildClass childClass = new ChildClass();
}
}
class ChildClass extends ParentClass{
static {
System.out.println("我是子类的静态方法");
}
public ChildClass(){
System.out.println("我是子类的构造函数");
}
}
class ParentClass{
static {
System.out.println("我是父类的静态方法");
}
public ParentClass(){
System.out.println("我是父类的构造函数");
}
}
打印结果
我是父类的静态方法
我是子类的静态方法
我是父类的构造函数
我是子类的构造函数