java中的静态代码块、代码块、构造器的执行顺序是怎样呢,这三者有啥作用呢
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中的静态代码块、代码块、构造器的执行顺序是怎样呢,这三者有啥作用呢相关的知识,希望对你有一定的参考价值。
先说执行顺序:我们在new一个对象时,如果类还没加载,就需要先将类加载到内存,此时就会执行静态代码块,在创建对象时,是先执行代码块,然后执行构造器。所以静态代码块、代码块、构造器的执行顺序是:静态代码块→代码块→构造器。
再说作用:静态代码块是用于初始化类的(在加载类时执行,只执行一次),代码块是用于初始化对象的(在创建对象时执行,每次创建对象时都执行),构造器是用于创建对象的。
class T
private static final String msg = "this is a test class!";
private static int version = 1;
static
version = 2;
desc = "class T";
private static String desc = “T is a test Class”;
private int age = 2;
age = 10;
name = "panda";
public T()
age = 12;
name = "foxli";
private String name = "micky";
类T,存在静态域version和desc,实例域age和name。
类中,存在静态代码块static以及实例化代码块和默认的构造方法。
------------------------------------------------------------------------------------------------------------
先来说一下类型初始化方法<clinit>:
JVM通过Classload进行类型加载时,如果在加载时需要进行类型初始化操作时,则会调用类型的初始化方法。类型初始化方法主要是对static变量进行初始化操作,对static域和static代码块初始化的逻辑全部封装在<clinit>方法中。
java.lang.Class.forName(String name, boolean initialize,ClassLoader loader),其中第二个参数就是是否需要初始化。
Java类型初始化过程中对static变量的初始化操作依赖于static域和static代码块的前后关系,static域与static代码块声明的位置关系会导致java编译器生成<clinit>方法打字节码。
由上例子,T类型的类型初始化方法<clinit>最后编译出来的字节码顺序大概是这样的:
T.<clinit>
private static int version;
private static String desc;
version = 1;
version = 2;
desc = "class T";
desc = "T is a test Class";
类型的初始化方法<clinit>只在该类型被加载时才执行,且只执行一次。
-------------------------------------------------------------------------------------------------------------
接下来说一下对象实例化方法<init>:
Java对象在被创建时,会进行实例化操作。该部分操作封装在<init>方法中,并且子类的<init>方法中会首先对父类<init>方法的调用。
Java对象实例化过程中对实例域的初始化赋值操作全部在<init>方法中进行,<init>方法显式的调用父类的<init>方法,实例域的声明以及实例初始化语句块同样的位置关系会影响编译器生成的<init>方法的字节码顺序,<init>方法以构造方法作为结束。
由上例子,T对象的对象实例化方法<init>最后编译出来的字节码顺序大概是这样的:
T.<init>
int age;
String name;
this.age = 2;
this.age = 10;
this.name = "panda";
this.name = "micky";
this.age = 12;
this.name = "foxli";
对象实例化方法<init>以构造方法作为结束。当前并没有考虑存在父类的情况,如果存在,<init>方法首先会调用父类的<init>方法。
-------------------------------------------------------------------------------------------------------------
PS:这里面的msg称为编译时常量,他不再<clinit>方法中出现。
其中<clinit>方法在javap命令中没有显示,javap输出的static就是<clinit>方法。 参考技术B 静态代码块->构造器->代码块
Java中的Static静态代码块以及各代码块之间的执行顺序
结论:
基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块代码块执行顺序:静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
继承中代码块执行顺序:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器
代码块的分类
基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块
代码块执行顺序静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
1、静态代码块(也叫静态块、静态初始化块)
Java静态代码块中的代码会在类加载JVM时运行,且只被执行一次,也就是说这些代码不需要实例化类就能够被调用。一般情况下,如果有些代码必须在项目启动的时候就执行的时候,就需要使用静态代码块,所以静态块常用来执行类属性的初始化!
关于Static静态代码块的五个小结点
1、Java静态代码块中的代码会在类加载JVM时运行,且只被执行一次
2、静态块常用来执行类属性的初始化
3、静态块优先于各种代码块以及构造函数,如果一个类中有多个静态代码块,会按照书写顺序依次执行
4、静态代码块可以定义在类的任何地方中除了方法体中【这里的方法体是任何方法体】
5、静态代码块不能访问普通变量
针对4中描述静态代码块不能存在任何方法体中的原因其实也是很简单,由于普通方法是通过加载类,然后new出实例化对象,通过对象才能运行这个方法,而静态代码块只需要加载类之后就能运行了。对于静态方法,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是自己主动运行的,而静态方法是被动调用运行的。不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。
针对5中描述静态代码块不能访问普通变量,原因同样简单。普通变量是被实例对象调用的,静态代码块中都不存在其实例对象,谈何调用变量?
Static静态代码块使用格式
static
..............
2、构造代码块(也叫构造初始化块)
听这名字就知道和构造方法离不开!没错,但是还是和构造方法有着本质区别,我们都知道,没个方法中都可以有很多构造方法,每创建一个对象其构造方法就执行一个,而一个构造方法可以创建N个对象,构造方法就比较“高冷”了,而构造代码块就比较“舔狗”了,只要该类实例了一个对象,构造代码就执行一次,利用每次创建对象的时候都会提前调用一次构造代码块特性,所以它可以做统计创建对象的次数功能。当然构造代码块用的相对少!
构造代码块小结:
1、构造代码块在创建对象时被调用,每次创建对象都会调用一次
2、构造代码块优先于构造函数执行,同时构造代码块的运行依赖于构造函数
3、构造代码块在类中定义
针对2中的 依赖 可理解为如果不实例化对象(也就是不执行构造方法),构造代码块是不会执行的!
3、代码块(又叫普通代码块、初始化块)
代码块小结
1、普通代码块定义在方法体中
2、普通代码块与构造代码块的格式一致都是
3、普通代码块与构造代码块唯一能直接看出的区别是构造代码块是在类中定义的,而普通代码块是在方法体中定义的
执行顺序的代码测试
以上已经讲得差不多了,开始上代码了!
public class Initializationblock
int intA;
int intB;
public Initializationblock()
System.out.println("无参构造器00000000");
public Initializationblock(int a)
System.out.println("一个参数的构造器");
intA = 10;
intB = 15;
System.out.println("构造初始化块11111");
System.out.println("构造初始化块22222");
System.out.println("构造初始化块33333");
//静态初始化块
static
System.out.println("静态初始化块01010101");
static
System.out.println("静态初始化块0202020202");
public void method()
System.out.println("普通初始化块");
测试demo
/* 初始化块一
* 因为静态块是在类的初始化阶段完成的,
* 因此在创建某个类的第二个对象时,该类的静态块就不会执行了
*
* 在单个类中,静态初始化块,初始化块,构造器
* 多个类的继承中初始化块、静态初始化块、构造器的执行顺序
在继承中,先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器
*/
public class Demo1
public static void main(String[] args)
Initializationblock initializationblock = new Initializationblock();
initializationblock.method();
System.out.println("------------");
//多打印几个对象的目的是:好看出Static静态代码块只执行一次!!!
Initializationblock initializationblock2 = new Initializationblock(); //因为静态块是在类的初始化阶段完成的,因此在创建某个类的第二个对象时,该类的静态块就不会执行了
initializationblock2.method();
Initializationblock initializationblock3 = new Initializationblock();
initializationblock3.method();
打印结果
静态初始化块01010101
静态初始化块0202020202
构造初始化块11111
构造初始化块22222
构造初始化块33333
无参构造器00000000
普通初始化块
------------
构造初始化块11111
构造初始化块22222
构造初始化块33333
无参构造器00000000
普通初始化块
构造初始化块11111
构造初始化块22222
构造初始化块33333
无参构造器00000000
普通初始化块
得出结论:执行顺序静态代码块 > 构造代码块 > 构造函数 > 普通代码块
继承中各代码块的执行顺序
啥都不说了,直接撸代码:继承关系为 BaseThree——> BaseTwo——> BaseOne
BaseOne类
public class BaseOne
public BaseOne()
System.out.println("BaseOne构造器");
System.out.println("BaseOne初始化块");
System.out.println();
static
System.out.println("BaseOne静态初始化块");
BaseTwo类
public class BaseTwo extends BaseOne
public BaseTwo()
System.out.println("BaseTwo构造器");
System.out.println("BaseTwo初始化块");
static
System.out.println("BaseTwo静态初始化块");
BaseThree 类
public class BaseThree extends BaseTwo
public BaseThree()
System.out.println("BaseThree构造器");
System.out.println("BaseThree初始化块");
static
System.out.println("BaseThree静态初始化块");
测试demo2类
/*
注:这里的ABC对应BaseOne、BaseTwo、BaseThree
* 多个类的继承中初始化块、静态初始化块、构造器的执行顺序
在继承中,先后执行父类A的静态块,父类B的静态块,最后子类的静态块,
然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器
*/
public class Demo2
public static void main(String[] args)
BaseThree baseThree = new BaseThree();
System.out.println("-----");
BaseThree baseThree2 = new BaseThree();
运行结果
BaseOne静态初始化块
BaseTwo静态初始化块
BaseThree静态初始化块
BaseOne初始化块
BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器
-----
BaseOne初始化块
BaseOne构造器
BaseTwo初始化块
BaseTwo构造器
BaseThree初始化块
BaseThree构造器
多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器【注:这里的ABC对应BaseOne、BaseTwo、BaseThree 】
结论:多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器
以上是关于java中的静态代码块、代码块、构造器的执行顺序是怎样呢,这三者有啥作用呢的主要内容,如果未能解决你的问题,请参考以下文章
Java中的Static静态代码块以及各代码块之间的执行顺序
Java中的Static静态代码块以及各代码块之间的执行顺序