类的加载顺序

初始化顺序:
1.先静态后非静态    先初始化静态的成员变量,在类第一次被使用时初始化一次
2.先声明后赋值         所有声明的成员变量先获得默认值(基本类型对应基本类型默认值,引用类型为null),然后执行等号后的赋值
3.先属性后方法 先执行属性定义处的初始化,然后执行方法的初始化(构造块先于构造方法)
4.先父类后子类        继承时候先初始化父类成员,后子类成员

所以通常的加载顺序为:

1.加载父类中的静态属性和静态代码块

2.加载子类中的静态属性和静态代码块

3.加载父类中的普通属性和代码块

4.加载父类中的构造方法

5.加载子类中的普通属性和代码块

6.加载子类中的构造方法

static是最先加载的,因为当class文件被加载进内存开始初始化的时候,被static修饰的变量和代码块即分配进了内存,而其他变量是在对象创建后开始加载的,构造方法是最后加载。

/**
 * 类装载时的初始化:为类的静态变量赋予正确的初始值
 * 注意类装载时只会初始化静态变量,jvm装载类时,只会装载静态变量,不会装载非静态的;
 * 非静态的在创建实例的时候才会初始化(所以直接用类名调用静态方法的时候只会出发静态字段/方法初始化)
 *
 * 触发初始化条件:
 * 1.用new getStatic,putStatic,invokeStatic这四条字节码指令时(new一个对象,读取或设置一个类的静态字段(被final修饰,已在编译器把结果放入常量池的静态字段除外),调用类的静态方法)
 * (读取类的静态变量\调用类的静态方法只会触发静态字段\代码块的初始化,创建实例会触发静态和非静态的初始化)
 * 2.用java.lang.Reflect包的方法对类进行反射调用时
 * 3.初始化一个类,若父类没有初始化,则触发父类的初始化
 * 4.执行一个主类时,会初始化这个主类(只初始化静态字段\方法)
 * 5.当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则先触发其类初始化;
 *
 * 以下情况不会触发类的初始化:
 同类子类引用父类的静态字段,不会导致子类初始化。至于是否会触发子类的加载和验证,取决于虚拟机的具体实现;
 通过数组定义来引用类,也不会触发类的初始化;例如:People[] ps = new People[100];
 引用一个类的常量也不会触发类的初始化
 *
 *
 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。如:Class.forName("bacejava.Langx");
 注意通过类名.class得到Class文件对象并不会触发类的加载。
 初始化某个类的子类
 直接使用java.exe命令来运行某个主类(java.exe运行,本质上就是调用main方法,所以必须要有main方法才行)。
 注意:通过子类调用父类的静态成员时,只会初始化父类而不会初始化子类。 因为没有调用子类的相关静态成员,这也叫做能不加载不加载原则。
 注意:调用静态成员时,会加载静态成员真正所在的类及其父类。 这也好理解,因为本类的父类没有加载就会先去加载父类。
 注意:类的加载成功后,即静态成员都被加载后,是不会再加载第二次的。只有非静态成员,如非静态成员变量、非静态代码块、非静态方法(不调用不加载)、构造方法都会被多次实例化的时候多次加载。
 注意:如果静态属性有 final 修饰时,则不会加载,当成常量使用。例:public static final int a =123; public static final int a=5/3*2+1;public static final double a=Math.PI;等
 注意:但是静态属性 有final修饰 时也有情况会被加载,public static final int a=getNum();这样也会被加载。getNum()是静态方法,并且不管这个静态方法是子类的还是父类的;
 在上面触发类被初始化的情况称为对类的主动引用,除此之外,那些引用类的方式没有触发初始化的叫做被动引用(JAVA程序对类的使用方式可分为两种:主动使用(六种),被动使用)
 */

demo:

public class Fathor {

    private static String static_field = getStaticField();

    private String field = getField();

    public int j = 18;

    private static String getStaticField() {
        System.out.println("父类静态属性");
        return "fathor";
    }

    private String getField() {
        System.out.println("父类属性");
        return "fathor";
    }

    public Fathor() {
        System.out.println("父类构造方法");
    }

    static {
        System.out.println("父类静态代码块");
    }

    {
        System.out.println("父类代码块");
    }
}
public class Son extends Fathor {

    private static String son_static_field = getSonStaticField();

    private String son_field = getSonField();

    public int i=getI();//属性加载是有顺序的,自上而下加载

    public int j = 18;

    public final static int z = 18;


    private int getI(){
        return j;
    }

    public static String getSonStaticField(){
        System.out.println("子类静态属性");
        return "son";
    }

    public String getSonField(){
        System.out.println("子类属性");
        return "son";
    }


    public Son() {
        System.out.println("子类构造方法");
    }

    static {
        System.out.println("子类静态代码块");
    }

    {
        System.out.println("子类代码块");
    }

    /**
     * 主类所在类初始化,初始化静态方法
     * @param args
     */
    public static void main(String[] args) {

    }
}

 

public class InitTest {

    /**
     * 创建实例初始化
     */
    @Test
    public void initTest() {
        Fathor fathor = new Fathor();
        System.out.println("i: " + fathor.j);
        /**
         * 运行结果:
         父类静态属性
         父类静态代码块
         父类属性
         父类代码块
         父类构造方法
         i: 18
         */

    }

    /**
     * 创建一个类时,父类没有初始化,先出发父类的初始化
     */
    @Test
    public void initTest2() {
        Son son = new Son();
        System.out.println("i: " + son.i);
        /**
         * 运行结果:
         父类静态属性
         父类静态代码块
         子类静态属性
         子类静态代码块
         父类属性
         父类代码块
         父类构造方法
         子类属性
         子类代码块
         子类构造方法
         i: 0
         */
    }

    /**
     * 调用静态方法时初始化,只会加载静态变量\方法,不会加载非静态的
     */
    @Test
    public void initTest3() {
        Son.getSonStaticField();
        /**
         * 运行结果:
         父类静态属性
         父类静态代码块
         子类静态属性
         子类静态代码块
         子类静态属性
         */
    }

    /**
     * 反射调用初始化
     * @throws Exception
     */
    @Test
    public void initTest4() throws Exception{
        Object o = Son.class.newInstance();
        Son.class.getMethod("getSonStaticField",null).invoke(o,null);
        /**
         * 运行结果:
         * 父类静态属性
         父类静态代码块
         子类静态属性
         子类静态代码块
         父类属性
         父类代码块
         父类构造方法
         子类属性
         子类代码块
         子类构造方法
         子类静态属性
         */
    }

    /**
     * 通过数组定义引用类不会初始化
     */
    @Test
    public void initFailTest(){
        Son[] sons = new Son[12];
        /**
         * 运行结果:无
         */
    }
}

 

参考(用手机打开):https://m.toutiaocdn.com/i6760923640960647683/?app=news_article&timestamp=1589730688&req_id=20200517235127010008043106015BB887&group_id=6760923640960647683&tt_from=mobile_qq&utm_source=mobile_qq&utm_medium=toutiao_ios&utm_campaign=client_share

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值