Java代码块与代码加载顺序原理详解
这篇文章主要介绍了Java代码块与代码加载顺序原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
本文首先介绍几个基本的名次,然后介绍了三种代码块的特性和使用方法。
在面试大型公司时,如果遇到大型国企或者大的互联网私企,笔试中经常遇到代码块和代码加载顺序的笔试题。这里做一个总结,也方便各位小伙伴飙车不会飘。
名词解释
代码块
由{}包起来的代码,称为代码块
静态代码块
由static{}包起来的代码,称为静态代码块。
不同类型变量定义示例:
classDemo{ Stringx;//非静态成员变量,又称为属性,对该类不同的对象来说,属性互不相同 staticinty=32;//类变量,一个类中只有一个该变量,该类不同的对象共享同一个静态成员变量 publicstaticvoidmain(String[]args){ intz=0;//局部变量,只在方法内部可见,在方法结束后由垃圾收集器自动回收 } }
局部代码块
位置:局部位置(方法内部)。
作用:限定变量的生命周期,尽早释放,节约内存。
调用:调用其所在的方法时执行。
方法中的局部代码块一般进行一次性地调用,调用完立刻释放空间,避免在接下来的调用过程中占用栈空间。栈空间内存有限,方法调用可能会生成很多局部变量导致栈内存不足,使用局部代码块可以避免此缺陷。
publicclass局部代码块{ publicstaticvoidgo(){ //局部代码块 { intage=30; System.out.print("go:"+age); } } publicstaticvoidmain(String[]args){ go(); } }
构造代码块
位置:类成员的位置,即类中方法之外的位置。
作用:把多个构造方法共同的部分提取出来,共用构造代码块。
调用:每次调用构造方法时,都会优先于构造方法执行,也就是每次new一个对象时自动调用,实现对象初始化。
publicclassA{ inti=1; intinitValue;//成员变量,初始化交给代码块来完成 A(){ System.out.println("构造方法在代码块执行后运行"); } { System.out.println("代码块从上至下依次运行"); //代码块的作用体现于此:在调用构造方法之前,用某段代码对成员变量进行初始化。 //而不是在构造方法调用时再进行。 for(inti=0;i<100;i++){ initValue+=i; } } { System.out.println(initValue); System.out.println(i);//此时会打印1 inti=2;//局部变量,和成员变量不冲突,但会优先使用代码块的变量 System.out.println(i);//此时打印2 //System.out.println(j);//提示非法向后引用,因为此时j的的初始化还没开始。 } intj=2; { System.out.println(j); System.out.println(i);//代码块中的变量运行后自动释放,不会影响代码块之外的代码 } } publicclass构造代码块{ @Test publicvoidtest(){ Aa=newA(); } }
执行结果
代码块从上至下依次运行 1 2 构造方法在代码块执行后运行
静态代码块
位置:类成员位置。
作用:对类进行一些初始化,只加载一次。当new多个对象时,只有第一次会调用静态代码块,因为静态代码块和类变量一样,
是属于类的,所有对象共享一份。
调用:new一个对象时自动调用。
publicclass静态代码块{ @Test publicvoidtest(){ Cc1=newC(); Cc2=newC(); //结果,静态代码块只会调用一次,类的所有对象共享该代码块 System.out.println("我是普通方法"); } } classC{ C(){ System.out.println("构造方法调用"); } { System.out.println("代码块调用"); } static{ System.out.println("静态代码块调用"); } }
调用结果:
静态代码块调用 代码块调用 构造方法调用 代码块调用 构造方法调用 我是普通方法
执行顺序静态代码块—–>构造代码块——->构造方法
笔试题
写出下列程序输出结果:
publicclassHelloA{ publicHelloA(){ System.out.println("HelloA"); } { System.out.println("I'mAclass"); } static{ System.out.println("staticA"); } } publicclassHelloBextendsHelloA{ publicHelloB(){ System.out.println("HelloB"); } { System.out.println("I'mBclass"); } static{ System.out.println("staticB"); } publicstaticvoidmain(String[]args){ newHelloB(); } }
执行结果:
分析:首先要知道静态代码块是随着类的加载而加载,而构造代码块和构造方法都是随着对象的创建而加载。
1,在编译HelloB.java时,由于HelloB继承HelloA,先加载了HelloA类,因此HelloA类的静态代码块首先执行,而后加载HelloB类,HelloB类的静态代码块执行,这没什么好说的。
2,然后创建HelloB的对象,大家都知道构造代码块优先于构造方法执行,这时候问题来了,这时应该先看HelloB类的构造方法,HelloB类里的构造方法里有一句隐式的super()首先被执行,所以找到HelloA类的构造方法,而HelloA类的构造方法中也有一句隐式的super()执行(调用Object类的构造方法),并没有什么返回结果,接下来才是在执行HelloA类构造方法的方法体前先执行了HelloA类的构造代码块(I'mAclass),再执行HelloA类构造方法的方法体(也就是HelloA),最后又回到HelloB类的构造方法中,这时HelloB类的super()已经执行完了,在执行HelloB类构造方法的方法体前先执行HelloB类的构造代码块(I'mBclass),再执行子类构造方法的方法体(HellB)。
无继承初始化顺序:
有继承初始化顺序:
接下来看一道阿里笔试题:
publicclassB{ publicstaticBt1=newB(); publicstaticBt2=newB(); { System.out.println("构造块"); } static{ System.out.println("静态块"); } publicstaticvoidmain(String[]args){ Bt=newB(); } }
执行结果:
总结
Java代码初始化顺序
由static关键字修饰的,如类变量和静态代码块,将在类创建实例之前被初始化,而且是按顺序从上到下依次被执行。(类变量、静态代码块)属于类本身,不依赖于类的实例。
没有static关键字修饰的(如:实例变量(非静态变量)、非静态代码块)初始化实际上是会被提取到类的构造器中被执行的,但是会比类构造器中的代码块优先执行。实例变量、非静态代码块的地位是相等的,它们将按顺序被执行。
容易混淆的一个知识点
静态方法只允许直接访问静态成员,而实例方法中可以访问静态成员和实例成员,原因是类还没有实例化,所以实例成员也没有被创建,静态方法中因此也不能用this。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。