• 简述JAVA类的生命周期


    介绍

    一个java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段:

    加载

    主要是:把类的信息加载到方法区中,并在堆中实例化一个Class对象。

    加载方式

    根据类的全路径加载class文件
    从jar的包中读取class文件
    根据一定的规则实时生成,比如设计模式中的动态代理模式,就是根据相应的类自动生成它的代理类。

    加载的时期

    不是jvm启动就加载,而是在真是使用的时候才会触发加载。

    • new 一个类的时候
    • 调用类的静态方法,以及读取或者修改一个类的静态字段的时候(不是常量)
    • 这个类是程序的入口类
    • 对这个类进行反射的时候(执行了上面的行为)

    连接

    一般会跟加载阶段和初始化阶段交叉进行。

    验证

    验证一下这个类是否合法,

    • 字节码格式是否合法
    • 变量和方法是否有重复
    • 继承和实现是否符合标准
      。。。

    准备

    给类的静态变量分配并初始化存储空间;
    也就是给静态变量赋默认的初始值(不包括非静态变量)

    解析

    把符合引用转换为直接引用。
    比如我们要在内存中找一个类里面的一个叫做show的方法,显然是找不到。但是在解析阶段,
    jvm就会把show这个名字转换为指向方法区的的一块内存地址,比如c17164,通过c17164就可以找到show这个方法具体分配在内存的哪一个区域了。
    这里show就是符号引用,而c17164就是直接引用。
    在解析阶段jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。

    初始化

    执行静态变量的初始化和静态Java代码块,并初始化程序员设置的变量值!

    时机

    和加载的时机一样,更准确的说初始化之前必须先经过加载,所以他们基本一样

    • new 一个类的时候
    • 调用类的静态方法,以及读取或者修改一个类的静态字段的时候(不是常量)
    • 对这个类进行反射的时候(执行了上面的行为)
    • 初始化一个类的子类,该子类所有的父类都会被初始化。
    • 作为程序的入口类(如:main方法所在的类,java 命令跟着的类)

    过程

    按照顺序自上而下运行类中的【变量赋值语句】和【静态语句】,
    如果有父类,则首先按照顺序运行父类中的变量赋值语句和静态语句。

    使用

    使用阶段包括主动引用和被动引用。

    主动引用(会引起类的初始化)

    • new 一个类的时候
    • 调用类的静态方法,以及读取或者修改一个类的静态字段的时候(不是常量)
    • 这个类是程序的入口类
    • 对这个类进行反射的时候(执行了上面的行为)

    注意:以上几种主动引用会引起类的初始化,这里的几种情况只有new或者使用class.newInstance()才会调用构造函数!

    被动引用(不会引起类的初始化)

    引用父类的静态字段
    定义类数组
    引用类的常量

    例如

    public class TestClassInit {
        static {
            System.out.println("初始化InitClass");
        }
        public static String staticA = null;
        public final static String finalB = "b";
        public static void staticMethod(){}
    }
    
    public class Test {
        public static void main(String args []){
            //主动引用
            //new 一个类的时候
            TestClassInit tct=new TestClassInit();
            //读取静态变量
            String staticA=TestClassInit.staticA;
            //设置静态变量
            TestClassInit.staticA="A";
            //调用静态方法
            TestClassInit.staticMethod();
    
            //被动引用
            //定义类数组,不会引起类的初始化
            TestClassInit[] tcts=new TestClassInit[10];
            //引用类的常量,不会引起类的初始化
            String finalB=TestClassInit.finalB;
        }
    }
    

    卸载

    满足下面请看才会卸载类:

    • java堆中不存在该类的任何实例。
    • 加载该类的ClassLoader已经被回收
    • 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法

    相关问题

    Class.forName()和ClassLoader.loadClass的区别

    Class.forName(className)方法,内部实际调用的方法是 Class.forName(className,true,classloader);
    第2个boolean参数表示类是否需要初始化, Class.forName(className)默认是需要初始化。
    一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。

    ClassLoader.loadClass(className)方法,内部实际调用的方法是 ClassLoader.loadClass(className,false);
    第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,
    不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行;

    如:Student.class.getClassLoader().loadClass("com.we.web.erp.jvm.Student");
    Class.forName("com.we.web.erp.jvm.Student");

    静态代码块,构造代码块,构造函数,以及静态变量赋值, 实例变量赋值 的执行顺序;

    • 没有继承关系的情况下:
      静态变量赋值 > 静态代码 > 实例变量赋值 > 构造代码 > 构造函数
    public class School {
    
        public School(){
            System.out.println("School的构造函数执行[静态属性]!");
        }
    }
    
    public class Student {
        public Student(){
            System.out.println("Student类的构造函数执行[非静态属性]!");
        }
    }
    
    public class SubClass  {
        private Student student=new Student();
        private static School school=new School();
        static
        {
            System.out.println("静态代码块!");
        }
    
        {
            System.out.println("构造代码块!");
        }
    
        public SubClass()
        {
            System.out.println("构造函数!");
        }
    }
    

    执行结果:

    School的构造函数执行[静态属性]!
    静态代码块!
    Student类的构造函数执行[非静态属性]!
    构造代码块!
    构造函数!
    
    

    执行这里的静态变量赋值和实例变量赋值可以按照下图理解;

    • 有继承的情况下
    public class SubClass extends SuperClass {
        private Student student=new Student();
        private static School school=new School();
        static
        {
            System.out.println("子类静态代码块!");
        }
    
        {
            System.out.println("子类的构造代码块!");
        }
    
        public SubClass()
        {
            System.out.println("子类构造函数!");
        }
    }
    
    public class SuperClass {
        static {
            System.out.println("父类静态代码块!");
        }
        {
            System.out.println("父类的构造代码块!");
        }
        SuperClass()
        {
            System.out.println("父类的构造函数!");
        }
    
    }
    

    执行结果:

    父类静态代码块!
    School的构造函数执行[静态属性]!
    子类静态代码块!
    父类的构造代码块!
    父类的构造函数!
    Student类的构造函数执行[非静态属性]!
    子类的构造代码块!
    子类构造函数!
    
    
  • 相关阅读:
    高数基础
    java.io.FileNotFoundException: [WEB-INF/spring-servlet.xml] cannot be opened because it does not exist
    HBase1.0.0 实现数据增删查
    cm 安装cdh 后添加hive服务
    HBase启动错误提示别的机器60000已经存在
    CM集群管理
    CM 安装CDH 错误: 安装失败。 无法接收 Agent 发出的检测信号。
    Js运动框架
    单片机DA转换实现正弦波
    怎样让树莓派接移动硬盘
  • 原文地址:https://www.cnblogs.com/wangsen/p/10838733.html
Copyright © 2020-2023  润新知