Java 类加载过程之初始化

Java 类加载过程之初始化,第1张

Java 类加载过程之初始化 类初始化

字节码角度分析类初始化过程

init 方法

看看空的类的

package com.chi.jvm.clazz;
public class InitTest1 {
}

0 aload_0
1 invokespecial #1  : ()V>
4 return

编译器会自动为类加上一个无参构造器,并调用Object父类的无参构造方法

package com.chi.jvm.clazz;

public class InitTest1 {
    private  int a = 1;
}

// 调用父类构造器
0 aload_0
1 invokespecial #1  : ()V>
// 初始化实例成员属性
4 aload_0
5 iconst_1
6 putfield #2 
9 return

成员变量在默认的构造器方法中初始化,但可以注意到调用父类方法的指令始终放在前面

package com.chi.jvm.clazz;

public class InitTest1 {
    private  int a;
    public InitTest1(){
        a = 2;
    }
}


// 调用父构造
 0 aload_0
 1 invokespecial #1  : ()V>
 
 // 初始化为 1
 4 aload_0
 5 iconst_1
 6 putfield #2 

 // 赋值为 2
 9 aload_0
10 iconst_2
11 putfield #2 
14 return
package com.chi.jvm.clazz;
public class InitTest1 {
    private  int a;
    public InitTest1(){
        System.out.println(a);	// 输出 0
        a = 2;
    }
}

 0 aload_0
 1 invokespecial #1  : ()V>
 4 getstatic #2 
 7 aload_0
 8 getfield #3 
11 invokevirtual #4 
14 aload_0
15 iconst_2
16 putfield #3 
19 return

如果实例属性字段声明阶段没有初始化,在字节码层面上不会添加默认的初始值,但是通过sout能够正常打印,说明默认值是硬编码到字节码文件中的。

clinit 方法
package com.chi.jvm.clazz;
public class ClinitTest1 {
    private static int a;
}

// 无clint方法
静态字段
package com.chi.jvm.clazz;
public class ClinitTest1 {
    private static int a = 1;
}

// clint
0 iconst_1
1 putstatic #2 
4 return
静态代码块
package com.chi.jvm.clazz;

public class ClinitTest1 {
    private static int a = 1;
    static {
        a = 2;
    }
    static {
        a = 3;
    }
}

// clinit 方法
 0 iconst_1
 1 putstatic #2 
 4 iconst_2
 5 putstatic #2 
 8 iconst_3
 9 putstatic #2 
12 return
package com.chi.jvm.clazz;

public class ClinitTest2 {

    static {
        System.out.println("1");
    }

    static {
        System.out.println("2");
    }

    public ClinitTest2(){
        System.out.println(3);
    }

    public static void main(String[] args) {
        new ClinitTest2();
        new ClinitTest2();
    }
}


静态属性和静态代码块只会初始化一次

final 关键字修饰的字段
  • 修饰实例属性:必须在声明时初始化,或在构造方法中初始化
  • 修饰静态属性:必须在声明时初始化,或在static静态代码块中初始化
在构造方法中调用其他构造方法
  • 调用自己:this()
  • 调用父类:super()
    无论调用哪个构造器,必须放在所有语句之前
总结
  • 编译器会提供默认的构造方法,用于调用父类构造方法和初始化实例属性
  • 编译器会提供clinit方法初始化静态属性、按顺序执行静态代码块
  • 在构造器中调用其他构造器方法必须放在第一句
  • final修饰的属性必须进行初始化

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5660338.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存