类初始化发生时机

类初始化

前提知识

类加载阶段

  1. 加载 :查找该类的class字节码文件(.java文件经过编译形成的.class文件),将文件的静态存储结构转化为方法区的运行时数据结构,并利用文件创建一个java.lang.Class对象,存储在 Java 堆中,对象引用方法区的数据结构
  2. 验证:检查加载的 class 文件的正确性
  3. 准备
    • 静态变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段完成
    • 如果static变量是final的基本类型以及字符串常量编译阶段值就确定了,赋值在准备阶段完成
    • 如果static变量是final的,但属于引用类型,那么赋值也会在初始化阶段完成
  4. 解析:将常量池中的符号引号解析为直接引用,符号引用就理解为一个标识,而在直接引用直接指向内存中的地址
  5. 初始化:对静态变量和静态代码块执行初始化

以上的五个阶段不是执行完上一步就要开始下一步,即加载之后就不一定就立马会初始化

区分 类的初始化对象的初始化

  • 类的初始化:是类加载阶段的其中一个步骤,是对静态变量、静态代码块的初始化
  • 对象的初始化,一般发生于new实例对象,去调用构造方法而发生的过程
//用一个例子来说明
class User {
    private int age;
    private String name;

    static {
        System.out.println("user类初始化");
    }
    User(){
        System.out.println("user对象无参方式初始化");
    }
    User(int age, String name){
        this.age = age;
        this.name = name;
        System.out.println("user对象有参方式初始化");
    }
}

public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        User user = new User();
        System.out.println("第二次new一个user对象------------------------------");
        User user1 = new User();
	}
}


/*
运行结果:
main方法所在类初始化
开始执行main方法
user类初始化
user对象无参方式初始化
第二次new一个user对象------------------------------
user对象无参方式初始化

分析:main所在类的初始化总是先发生,类初始化只发生一次
*/

类初始化发生的时机

类初始化是【懒惰的

  • main方法所在的类,总会被首先初始化

    • 相应例子对应着上方的代码
  • 首次访问这个类的静态变量或静态方法时


class User {
    private int age;
    private String name;
    static String element = "user类的静态变量";
    
    static {
        System.out.println("user类初始化");
    }
    User(){
        System.out.println("user对象无参方式初始化");
    }
    User(int age, String name){
        this.age = age;
        this.name = name;
        System.out.println("user对象有参方式初始化");
    }
}

public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        //调用user类的静态变量 ,发生user类的初始化
        System.out.println(User.element);
        System.out.println("第二次调用类的静态变量---------");
        System.out.println(User.element);
	}
}

/*
运行结果:
main方法所在类初始化
开始执行main方法
user类初始化
user类的静态变量
第二次调用类的静态变量---------
user类的静态变量
分析:首次访问这个类的静态变量或静态方法时,类才会初始化,类初始化只发生一次
*/
在举例说明以下两个类初始发生时机前,先说明子类初始化的过程


class User {
    private int age;
    private String name;
    static String element = "user类的静态变量";

    {
        System.out.println("user对象代码块");
    }
    static {
        System.out.println("user类初始化");
    }
    User(){
        System.out.println("user对象无参方式初始化");
    }
    User(int age, String name){
        this.age = age;
        this.name = name;
        System.out.println("user对象有参方式初始化");
    }
}
class Student extends User{

    {
        System.out.println("student对象代码块");
    }
    static {
        System.out.println("student类初始化");
    }
    Student(){
        System.out.println("student对象无参方式初始化");
    }
    Student(int age, String name){
        System.out.println("student对象有参方式初始化");
    }


}

public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        Student student = new Student();

	}
}

/*
运行结果:
main方法所在类初始化
开始执行main方法
user类初始化
student类初始化
user对象代码块
user对象无参方式初始化
student对象代码块
student对象无参方式初始化
**分析:**子类初始化过程:先发生父类的初始化再发生子类的初始化,产生了父类对象,再产生子类对象
在进行对象初始化前会进行对象本身内容,对应user对象代码块,对象的初始化发生在最后
*/

  • 子类初始化,如果父类还没初始化会引发父类的初始化

class User {
    private int age;
    private String name;
    static String element = "user类的静态变量";

    {
        System.out.println("user对象代码块");
    }
    static {
        System.out.println("user类初始化");
    }
    User(){
        System.out.println("user对象无参方式初始化");
    }
    User(int age, String name){
        this.age = age;
        this.name = name;
        System.out.println("user对象有参方式初始化");
    }
}
class Student extends User{

    {
        System.out.println("student对象代码块");
    }
    static {
        System.out.println("student类初始化");
    }
    Student(){
        System.out.println("student对象无参方式初始化");
    }
    Student(int age, String name){
        System.out.println("student对象有参方式初始化");
    }


}

public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        System.out.println("user类-----------");
        User user = new User();
        System.out.println("student类-----------");
        Student student = new Student();

	}
}

/*
运行结果:
main方法所在类初始化
开始执行main方法
user类-----------
user类初始化
user对象代码块
user对象无参方式初始化
student类-----------
student类初始化
user对象代码块
user对象无参方式初始化
student对象代码块
student对象无参方式初始化
可以看到子类初始化,父类已经初始化了,因此user类即父类无需初始化

如果main方法里面是
System.out.println(“开始执行main方法”);
System.out.println(“student类-----------”);
Student student = new Student();
运行结果:
main方法所在类初始化
开始执行main方法
student类-----------
user类初始化
student类初始化
user对象代码块
user对象无参方式初始化
student对象代码块
student对象无参方式初始化
**分析:**子类初始化,如果父类还没初始化就会引发父类的初始化
*/

  • 子类访问父类的静态变量,只会触发父类的初始化
public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        System.out.println(Student.element);
	}
}

/*
运行结果:
main方法所在类初始化
开始执行main方法
user类初始化
user类的静态变量
*/

不会导致类初始化的情况

  • 访问类的static final静态常量(基本类型和字符串)不会触发初始化
//将user类中的element设置为final
final static String element = "user类的静态变量";
public class ClassLoader {
    static {
        System.out.println("main方法所在类初始化");
    }
    public static void main(String[] args) {
        System.out.println("开始执行main方法");
        System.out.println(User.element);
    }
}

/*
运行结果:
main方法所在类初始化
开始执行main方法
user类的静态变量
*/

疑问:

  1. 类从class文件被加载到内存时,类的数据是被存放到方法区嘛
    • 类的静态变量或者静态方法存储于方法区,在从class文件中加载类,创建了一个class类对象,存储于,去引用类的静态数据结构
  2. 类的初始化之后,对象的初始化过程
    • 如果类还没有被加载,则会先加载到内存
    • 如果类还没有初始化,会先进行类的初始化(如果父类没有初始化,那么先进行父类的初始化)
    • 对象初始化:先进行父类对象的初始化再进行子类的初始化
      • 在堆内存中开辟空间,分配内存地址
      • 在堆内存中建立对象的特有属性,并进行默认初始化(引用类型为null)
      • 对属性进行显示初始化(如果.java文件对某些属性进行了原本赋值,例如对user类中 private String name =“java”;
      • 进行构造代码块初始化
      • 进行构造方法初始化
      • 将内存地址赋值给栈内存中的变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值