Java初始化块及执行顺序
- 理解
初始化块又称为代码块。属于类中的第四大成员。本质上是一个方法,它也有方法体,但没有方法名,没有参数,没有返回,而且也不是通过对象或类名显式调用,而是通过隐式调用
是构造器的补充
语法
[修饰符]{
方法体
}
注意:
①修饰符只能是static,使用static修饰的初始化块称为静态初始化块
没有使用static修饰的初始化块称为普通初始化块
//静态初始化块 static{ } //普通初始化块 { }
②方法体中可以为任意逻辑语句,包含输入、输出、变量、运算等
- 好处
1、和构造器很像,都是用于初始化信息
2、当多个构造器中有重复的语句,可以将这些重复的语句往上提取到初始化块中,提高代码的重用性
- 特点
1、静态初始化块的调用时机:加载类
普通初始化块的调用时机:创建对象
初始化块示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class InitDemo{ String name; int age; double height; { System.out.println("初始化语句"); } public InitDemo(){ } public InitDemo(String name) { super(); this.name = name; } public InitDemo(String name, int age) { super(); this.name = name; this.age = age; } public InitDemo(String name, int age, double height) { super(); this.name = name; this.age = age; this.height = height; } }
2、静态初始化块只会调用一次,随着类的加载而加载,(类只加载一次)
普通初始化块可以调用多次,随着对象的创建而加载
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* *此类用于演示初始化块的特点2: *静态初始化块只能被调用一次,因为类只能加载一次。 *普通初始化块可以被调用多次,因为可以创建多个对象 */ public class TestInit2 { public static void main(String[] args) { // System.out.println(InitDemo2.a);//加载类 new InitDemo2(); new InitDemo2(); new InitDemo2(); new InitDemo2(); new InitDemo2(); } } class InitDemo2{ static int a =100; { System.out.println("我是普通初始化块"); } static{ System.out.println("我是静态初始化块"); } }
3、一个类中可以有多个静态初始化块和多个普通初始化块
静态初始化块的执行要早于普通初始化块
同一个类型的初始化块的执行顺序取决于定义的先后顺序!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * 此类用于演示初始化块的特点3: * 一个类中允许有多个普通初始化块和静态初始化块,则执行顺序: * 静态初始化块————普通初始化块 * 同一个类别的初始化块,执行顺序取决于定义的先后顺序 */ public class TestInit3 { public static void main(String[] args) { // System.out.println(InitDemo2.a);//加载类 new InitDemo3();//加载+创建对象 } } class InitDemo3{ { System.out.println("我是普通初始化块1"); } static{ System.out.println("我是静态初始化块1"); } { System.out.println("我是普通初始化块2"); } { System.out.println("我是普通初始化块3"); } static{ System.out.println("我是静态初始化块2"); } static{ System.out.println("我是静态初始化块3"); } }
输出:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
我是静态初始化块1
我是静态初始化块2
我是静态初始化块3
我是普通初始化块1
我是普通初始化块2
我是普通初始化块3
4、一个类中如果有:静态初始化块、普通初始化块、普通属性初始化、静态属性初始化、构造器
执行顺序:
静态初始化块 | 静态属性初始化 > 普通初始化块 | 普通属性初始化 > 构造器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * 此类用于演示初始化块的特点4: * 如果一个类中有普通属性初始化、静态属性初始化、普通初始化块、静态初始化块、构造器,则执行顺序: * 静态属性初始化|静态代码块——>普通属性初始化|普通代码块——>构造器 * 同一个类别的属性初始化和代码块的执行顺序取决于定义的先后顺序 * */ public class TestInit4 { public static void main(String[] args) { InitDemo4 id = new InitDemo4(); } } class InitDemo4{ public InitDemo4(){ System.out.println("我是构造器"); } String a=msg("普通属性初始化1"); public static String msg(String info){ System.out.println(info); return info; } static{ System.out.println("静态初始化块2"); } static String b=msg("静态属性初始化1"); { System.out.println("我是普通初始化块1"); } String c=msg("普通属性初始化2"); { System.out.println("我是普通初始化块2"); } static String d=msg("静态属性初始化2"); static{ System.out.println("静态初始化块1"); } }
输出:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
静态初始化块2
静态属性初始化1
静态属性初始化2
静态初始化块1
普通属性初始化1
我是普通初始化块1
普通属性初始化2
我是普通初始化块2
我是构造器
5、有父子类
执行顺序:
爷爷类的静态初始化块 | 静态属性初始化 >
父类静态初始化块 | 静态属性初始化 >
子类静态初始化块 | 静态属性初始化 >
爷爷类普通初始化块 | 普通属性初始化 > 构造器 >
父类普通初始化块 | 普通属性初始化 > 构造器 >
子类普通初始化块 | 普通属性初始化 > 构造器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * 此类用于演示初始化块的特点5: * 如果父子类中都有普通属性初始化、静态属性初始化、普通初始化块、静态初始化块、构造器, * 则执行顺序: * 爷爷类的静态属性初始化|静态代码块——>父类的静态属性初始化|静态代码块——>子类的静态属性初始化|静态代码块——> * 爷爷类的普通属性初始化|普通代码块——>爷爷类的构造器——>父类的普通属性初始化|普通代码块——>父类的构造器——>子类的普通属性初始化|普通代码块——>子类的构造器 * 同一个类别的属性初始化和代码块的执行顺序取决于定义的先后顺序 * */ public class TestInit5 { public static void main(String[] args) { InitDemo5 id = new InitDemo5(); } } class Grand{ public Grand(){ // System.out.println("爷爷类的普通代码块"); System.out.println("我是爷爷类的构造器"); } String a=fun("爷爷类的普通属性初始化"); public static String fun(String info){ System.out.println(info); return info; } static{ System.out.println("爷爷类的静态代码块"); } { System.out.println("爷爷类的普通代码块"); } static String d=fun("爷爷类的静态属性初始化"); } class Father extends Grand{ public Father(){ // System.out.println("爷爷类的普通代码块"); System.out.println("我是爷爷类的构造器"); // System.out.println("父类的普通代码块"); System.out.println("我是父类的构造器"); } String a=method("父类的普通属性初始化"); public static String method(String info){ System.out.println(info); return info; } static{ System.out.println("父类的静态代码块"); } { System.out.println("父类的普通代码块"); } static String d=method("父类的静态属性初始化"); } class InitDemo5 extends Father{ public InitDemo5(){ System.out.println("我是子类的构造器"); } String a=msg("子类的普通属性初始化"); public static String msg(String info){ System.out.println(info); return info; } static{ System.out.println("子类的静态代码块"); } { System.out.println("子类的普通代码块"); } static String d=msg("子类的静态属性初始化"); }
6、静态初始化块中遵循静态成员的特点,只能直接访问静态成员!
7、初始化位置
普通的常量属性,初始化必须在声明时或构造器或普通代码块中
静态的常量属性,初始化必须在声明时或静态代码块中
示例:
class A{ final int x; final static int y; public A(){ y=10; x=100; // × final变量初始化后不可重新赋值 } { x=100; y=10; // × final static修饰的静态常量只能在static静态初始化块中初始化 } }
第二种:
class A{ final int x; final static int y; public A(){ y=10; // × final变量初始化后不可重新赋值 x=100; } static{ x=100; // × static静态初始化块不可调用普通变量 y=10; } }
【总结】
类的对象创建包括:加载和创建两部分。
加载中将所有静态的属性、静态初始化块执行
创建对象中将所有普通属性、普通初始化块执行,再执行构造方法
【面试题一】
public class Teacher { public static void main(String[] args) { new Teacher("john"); // ⑥ } String name ; public Teacher(String string) { this.name=string; System.out.println("构造器:"+name); // ④ 构造器执行 // ⑧ 构造器执行 } static { System.out.println("嘿嘿"); // ① } static Teacher t = new Teacher("鸠摩智"); // ② 加载时发现还需要加载不会再加载 { System.out.println("哈哈哈:"+name); // ③ 此时name(鸠摩智)还没赋值,为null // ⑦ 加载结束,但name(john)没被赋值,为null } static{ System.out.println("呵呵"); // ⑤ 继续加载类,加载结束 } }
输出:
嘿嘿 哈哈哈:null 构造器:鸠摩智 呵呵 哈哈哈:null 构造器:john
【面试题二】
public class MyClass { static int x,y; // ① x=0, y=0 static{ int x=5; // ② x = 5(局部变量,与static无关) x--; // ③ x = 4(局部变量,与static无关) } static{ x--; // ④ x = -1 } public static void main(String[] args) { x--; // ⑤ x = -2 myMethod() ; // ⑥ System.out.println(x+y + ++x); // ⑧ 0+(-2)+1=-1 } public static void myMethod() { y=x++ + ++x; // ⑦ y = (-2) + (0)=-2 x = 0 } }
输出:
-1
【面试题三】
class A{ final int x; final static int y; public A(){ y=10; x=100; // × final变量初始化后不可重新赋值 } { x=100; y=10; // × final static修饰的静态常量只能在static静态初始化块中初始化 } }
class A{ final int x; final static int y; public A(){ y=10; // × final变量初始化后不可重新赋值 x=100; } static{ x=100; // × static静态初始化块不可调用普通变量 y=10; } }
【面试四】
public class Test { // 1.准备加载类 public static Test t1 = new Test(); // 2.加载静态变量, 这里比较特殊, 静态变量引用本类的实例 // 方法块 { System.out.println("AAA"); // 3.实例化时调用 } // 静态代码块 static { System.out.println("BBB"); // 4.加载静态方法 } public static void main(String[] args) { Test t2 = new Test(); // 5.实例化对象 -> 调用方法块 } }