本文以简单的Cat类(继承自Animal类)来说明对象的创建过程以及内存变化
//父类
class Animal{
public static int totalAnimal;
static {
totalAnimal=10;
}
}
//子类
class Cat extends Animal{
//属性
public static int totalCat;
private String name;
private int age;
//代码块
static{
totalCat=5;
}
{
name="小猫";
age=1;
}
//构造器
Cat(){};
Cat(String name,int age){
this.name=name;
this.age=age;
}
//方法
public void makeSound(){
System.out.println("喵~");
}
//嵌套类
}
//公共类
public class TestClass{
//main方法
public static void main(String[] args) {
//创建对象
Cat xiaobai = new Cat("小白", 3);
//方法调用
xiaobai.makeSound();
}
}
前置知识 java类的五大组成部分
首先是类的加载,然后是具体实例对象的创建
在程序未启动时候的内存状态
类的加载-
加载loading
-
JVM在该阶段的主要目的是将字节码从 不同的数据源(可能是class文件,也可能是jar包,甚至是网络)转换成二进制字节流加载到内存(方法区中)并生成一个该类的java.lang.Class对象(堆中)
-
先父类后子类
-
-
连接Linking
-
验证verification
-
目的是为了确保Class文件的字节流中包含的信息复合当前虚拟机的要求,并且不会危害虚拟机自身的安全
-
包括:文件格式验证(是否以魔数 oxcafebage开头)、元数据验证、字节码验证和符号引用验证
-
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机加载的时间
-
-
准备preparation
-
JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始化值,eg.0,0L,null,false等)(和Class对象一起存放在堆中)
-
-
解析resolution
-
虚拟机将常量池内的符号引用替换为直接引用的过程
-
即在编译阶段,并没有放到内存中没有内存地址,所以引用是符号引用;在解析这一步把符号引用替换为直接引用
-
-
-
初始化initialization
-
到初始化阶段,才开始执行类中定义的Java代码,此阶段执行clinit()方法的过程
-
按顺序执行静态属性的初始化和静态代码块,先父类后子类(只执行一次)
-
clinit()方法是由比那一起按照语句在源文件中出现的顺序,一次自动收集类中所有的静态变量的复赋值动作和静态代码块中的语句,并进行合并
-
虚拟机会保证一个类的clinit()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,俺么只会有一个线程去执行这个类的clint()方法
-
-
在堆中创建对象
-
将堆中对象的地址返回给栈中的引用
-
进行默认初始化
-
执行普通属性和普通代码块(每创建一次对象,执行一次)
如果是引用类型的属性,则在方法区的常量池中创建
-
执行构造器
至此对象创建完毕



