java类加载和初始化顺序
来源:互联网 发布:启信宝的数据可信度 编辑:程序博客网 时间:2024/06/04 19:28
java同其他语言不同,在类首次使用时,类的class字节码才会加载到内存中,通过加载、连接、初始化这三个步骤来对该类进行初始化。
- 加载:是指将类的class文件读入内存,并为之创建一个该类的java.lang.Class对象(注意并不是目标类的对象)。也就是说当程序中使用任何类时都会为之创建一个java.lang.Class对象。
- 连接:类的连接又可以分为如下三个阶段:
- 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致;
- 准备:类的准备阶段则负责为类的静态属性分配内存,并设置默认初始值;
- 解析:将类的二进制数据中的符号引用替换成直接引用。
- 初始化:对变量进行初始化。初始化顺序如下:
- 父类静态成员和静态初始化快,按定义顺序执行。
- 子类静态成员和静态初始化块,按定义顺序执行。
- 父类的实例成员和实例初始化块,按定义顺序执行。
- 执行父类的构造方法。
- 子类实例成员和实例初始化块,按定义顺序执行。
- 执行子类的构造方法。
java类的代码只在类第一次使用时才加载,包括创建第一个对象和第一次调用static。static块只在第一次初始化时初始化,并且只初始化一次。(ClassLoader.getSystemClassLoader().loadClass这个方式只加载类,没有初始化操作,所以static也不会在这个时候初始化)。
关于初始化,使用.class和ClassLoader.getSystemClassLoader().loadClass方式获得类的引用,是不会进行初始化的,并且如果是“编译期常量”,是不需要初始化就能使用的。
简单例子:
public class Test {public static Random rand = new Random(53);public static void main(String[] args) {Class c = T1.class;System.out.println("after T1 ref");System.out.println(T1.a);System.out.println(T1.b);System.out.println(T2.a);try {Class cc = Class.forName("ttt.T3");System.out.println("after T3 ref");System.out.println(T3.a);Class ccc = ClassLoader.getSystemClassLoader().loadClass("ttt.T4");System.out.println("after T4 ref");System.out.println(T4.a);} catch (ClassNotFoundException e) {e.printStackTrace();}}}class T1 {static final int a = 23;static final int b = Test.rand.nextInt(12);static {System.out.println("-----T1-----");}}class T2 {static int a = 123;static {System.out.println("-----T2-----");}}class T3 {static int a = 232;static {System.out.println("-----T3-----");}}class T4 {static int a = 235;static {System.out.println("-----T4-----");}}
运行结果:
after T1 ref23-----T1-----8-----T2-----123-----T3-----after T3 ref232after T4 ref-----T4-----235
上述例子就很好体现了.class和ClassLoader.getSystemClassLoader().loadClass获得类的引用时是不会进行初始化的,并且static final 常量是可以直接使用的。
下面再用简单的例子详细描述下整个过程:
public class Test {private static Test test = new Test(5);public static int a;public static int b = 3;public int c = getI();public int d = 2;public String aa;static {System.out.println("static==" + a);System.out.println("static==" + b);}{System.out.println("non static==" + a);System.out.println("non static==" + b);System.out.println("non static==" + c);System.out.println("non static d==" + d);}public int getI() {System.out.println("======getI======="+d);d++;System.out.println("======getI======="+d);return d;}public Test(int i) {System.out.println("===========i==========" + i);System.out.println("==" + test);System.out.println("==" + a);System.out.println("==" + b);System.out.println("==" + c);System.out.println("==" + d);a++;b++;c++;d++;System.out.println("===" + a);System.out.println("===" + b);System.out.println("===" + c);System.out.println("===" + d);}//public static Test getIns() {//return test;//}public static void main(String[] args) {//Test test = Test.getIns();Test test = new Test(15);System.out.println("---------------------------");System.out.println(test.a);System.out.println(test.b);System.out.println(test.c);System.out.println(test.d);}}
首先,寻找main方法,找到后由于是首次调用Test这个类,所以Test的class字节码会被载入内存,创建Class对象。
接着,是连接的三个阶段,其中准备阶段会将类的静态属性分配内存,并设置默认初始值。(所有的基本类型会赋予其默认值,对象引用会赋予null)。
然后,进行初始化操作。由于是按代码顺序执行,当执行第一句
private static Test test = new Test(5);由于遇到new关键字,需要创建对象(创建对象调用类的构造器,实际上构造器是隐式的static),又由于static只在首次时进行初始化操作,并且只执行一次,所以这次不会在执行static,所以直接对实例变量进行赋默认值并初始化,然后调用构造方法。接着,顺序执行之前未完成的static的初始化操作,然后是main方法中遇到new关键字,执行上述过程。最后,执行main方法里的其他语句。
程序运行结果:
======getI=======0 //在打印这部之前,已经为static分配了内存,并赋予默认值,遇到第一句初始化,所以进行实例变量的初始化操作======getI=======1 //程序走到这里d已经变为1non static==0 //执行非静态块(同非静态变量一起按顺序执行)non static==0 non static==1 //由于执行了getI方法,c被赋值为d的值 non static d==2 //对d进行显式初始化,所以d的值变为2了,这也说明实例变量在分配内存后也会被赋予默认值===========i==========5 //执行构造函数(这里是第一句的new关键字创建的对象)==null //由于对象未创建,对象引用被赋予null,所以这里是null==0 ==0 //static 变量还未初始化,只赋予默认值,所以是0==1 //由于创建对象需要先初始化实例变量,所以这里已经初始化为1了==2 //由于创建对象需要先初始化实例变量,所以这里已经初始化为2了(注意,这2个变量都是由于第一句static初始化时需要创建对象而进行的初始化,并非说明实例变量先于static变量初始化)===1 //对4个变量进行增量操作了===1===2===3static==1 //static按顺序执行,所以在第一句static初始化创建了Test对象后,又回到static的初始化了static==3 //之前b的值为1,但初始化为3了,所以这里打印出来是3======getI=======0 //static初始化完成了,程序执行main方法里的代码,第一句又遇到new关键字创建对象,重复上述过程了,只不过这次是第二次调用Test类,所以不需要初始化static了======getI=======1non static==1non static==3non static==1non static d==2===========i==========15==com.busi.controller.web.member.Test@15db9742 //这里已经在static初始化的时候创建了对象了,所以不为null了==1==3==1==2===2===4===2===3--------------------------- //执行main方法中其余代码2423
以上是单个类的初始化过程,有继承的,需要先进行父类的初始化,过程与此相同,需要注意的是创建子类对象时,会先调用父类的无参构造函数,如果父类的无参构造函数中调用了被子类重写的方法,则调用子类重写过的方法,子类中可以通过super来调用父类被重写的方法。
本文参考:http://www.cnblogs.com/amunote/p/4170627.html
0 0
- java类加载和初始化顺序
- JAVA基础教程:类加载和初始化顺序
- java类加载和初始化顺序
- java 类加载-初始化顺序
- Java类的加载和初始化顺序分析
- Java类的加载和初始化顺序分析
- java经典面试题(类加载和初始化顺序)
- java类的加载以及初始化顺序
- java类的加载以及初始化顺序
- java类的加载以及初始化顺序
- java类的加载以及初始化顺序
- Java类的加载及初始化顺序
- java类的加载以及初始化顺序
- Java类加载问题:变量初始化顺序
- JAVA类加载(初始化)顺序
- Java类加载及初始化顺序
- java类的加载以及初始化顺序
- java类的加载以及初始化顺序
- 1037. Magic Coupon (25)
- 数据项目知识整理
- Java泛型和通配符
- 元数据MetaData
- RESTful风格
- java类加载和初始化顺序
- 远程调用其他系统bean报EJBCLIENT000037: Could not load ejb proxy class com.tgb.itoo.basic.service.xxBean的错误
- iscroll部分机型高度计算错误原因--transform属性
- 批量 mybatis
- PHP重命名文件夹下的文件后缀名
- pyhotn的p2p-sip网络电话小试牛刀
- JAVA设计模式之创建者模式
- android studio导入opencv人脸识别案例的一些报错
- 查找——AVL