基本介绍
- 代码化块又称为初始化块,属于类中的成员(即是类的一部分),类似于方法中的方法体,通过{}包围起来。
- 但和方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不同通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
- 基本语法
[修饰符] {
// 代码 ...
};
说明:
(1)修饰符可以不写,如果要写的话只能写 static
(2)代码块分为两类:使用 static 修饰的叫静态代码块,否则就叫普通代码块/非静态代码块
(3)逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
(4)分号可以写上,也可以省略
代码块的好处
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 应用场景:如果多个构造器中都有重复的语句,可以抽取到代码块中,提高代码的重用性
public class Test {
public static void main(String[] args) {
/*
* 输出:
* 电影屏幕打开了...
* 广告开始了...
* 电影开始了...
* Movie(String name) 被调用了
* -------------------
* 电影屏幕打开了...
* 广告开始了...
* 电影开始了...
*/
Movie m1 = new Movie("前任");
System.out.println("-------------------");
Movie m2 = new Movie("前任2",50.9);
}
}
class Movie {
public String name;
public double price;
{
/*
* 把两个构造器中都有的重复代码提取出来,放在代码块中
* 在创建对象时,会先执行代码块中的语句,再执行构造方法中的代码
*/
System.out.println("电影屏幕打开了...");
System.out.println("广告开始了...");
System.out.println("电影开始了...");
};
public Movie(String name){
// System.out.println("电影屏幕打开了...");
// System.out.println("广告开始了...");
// System.out.println("电影开始了...");
System.out.println("Movie(String name) 被调用了");
this.name = name;
}
public Movie(String name, double price){
// System.out.println("电影屏幕打开了...");
// System.out.println("广告开始了...");
// System.out.println("电影开始了...");
System.out.println("Movie(String name, double price) 被调用了");
this.name = name;
this.price = price;
}
}
代码块的使用细节
- static 代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就会执行一次普通代码块
- 类加载的时机
- 创建对象实例时(new)
- 创建子类的对象实例时,父类也会被加载
- 使用类的静态成员时(静态属性、静态方法)
- 普通代码块在创建对象实例时,会被隐式地调用。被创建一次,就会调用一次普通代码块。如果只是使用类的静态成员,普通代码块不会被执行,因为使用类的静态成员时没有创建对象
- 创建一个对象时,在一个类中的调用顺序是:
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按它们定义的顺序调用)
- 调用普通代码块和普通属性初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义的顺序调用)
- 调用构造方法
public class Test {
public static void main(String[] args) {
/**
* 类中的调用顺序:调用静态属性初始化和静态代码块 ——> 调用普通属性初始化和普通代码块 ——> 调用构造器
* 1、静态代码块和静态属性初始化优先级一样,按照它们的定义顺序使用
* 2、普通代码块和普通属性初始化优先级一样,按照它们的定义顺序使用
* 3、调用构造器
* 输出:
* getN1 被调用....
* 类A 静态代码块被调用...
* 类A 普通代码块被调用...
* getN2 被调用....
* A 类无参构造器被调用...
*/
A a = new A();
}
}
class A {
{ // 普通代码块
System.out.println("类A 普通代码块被调用...");
}
private static int n1 = getN1(); // 静态属性初始化
private int n2 = getN2(); // 普通属性初始化
public A(){ // 构造器
System.out.println("A 类无参构造器被调用...");
}
static { // 静态代码块
System.out.println("类A 静态代码块被调用...");
}
public static int getN1() {
System.out.println("getN1 被调用....");
return 100;
}
public int getN2() {
System.out.println("getN2 被调用....");
return 200;
}
}
- 构造方法(构造器)的最前面其实隐含了 super() 和调用普通代码块和普通属性初始化。静态相关的代码块、属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
public class Test {
public static void main(String[] args) {
/**
* 构造方法(构造器)的最前面其实隐含了 super() 和调用普通代码块和普通属性初始化
* 静态相关的代码块、属性初始化,在类加载时,就执行完毕
* 因此是优先于构造器和普通代码块执行的
* 输出:
* // 调用 super() 的输出结果
* 类AAA 普通代码块被调用...
* AAA 类无参构造器被调用...
* 类BBB 普通代码块被调用... //调用本类的普通代码块
* BBB 类无参构造器被调用... // 执行 BBB 的构造器
*/
BBB bbb = new BBB();
}
}
class AAA { // 父类是 Object ,在 Object 的构造器中无输出,所以看不出效果
{ // 普通代码块
System.out.println("类AAA 普通代码块被调用...");
}
public AAA(){ // 构造器
/**
* 此处隐藏了两句代码的执行
* (1)super()
* (2)调用本类的普通代码块和普通属性初始化
*/
System.out.println("AAA 类无参构造器被调用...");
}
}
class BBB extends AAA {
{ // 普通代码块
System.out.println("类BBB 普通代码块被调用...");
}
public BBB(){ // 构造器
/**
* 此处隐藏了两句代码的执行
* (1)super()
* (2)调用本类的普通代码块和普通属性初始化
*/
System.out.println("BBB 类无参构造器被调用...");
}
}
- 创建一个子类对象时(继承关系),它们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造器的调用顺序如下:
- 父类的静态代码块和静态属性初始化(两者优先级一样,按定义的顺序执行)
- 子类的静态代码块和静态属性初始化(两者优先级一样,按定义的顺序执行)
- 父类的普通代码块和普通属性初始化(两者优先级一样,按定义的顺序执行)
- 父类的构造方法
- 子类的普通代码块和普通属性初始化(两者优先级一样,按定义的顺序执行)
- 子类的构造方法
- 总结:父类的静态 —> 子类的静态 —> 父类的普通和构造 —> 子类的普通和构造
public class Test {
public static void main(String[] args) {
/**
* 1、先进行类加载
* 1.1、加载父类 A:执行静态相关代码
* 输出:(1)getN1()... (2)类A 的第一个静态代码块被调用...
* 1.2、加载子类 B:执行静态相关代码
* 输出:(3)getN3()... (4)类B 的一个静态代码块...
* 2、创建对象
* 2.1、执行子类的构造方法,其中隐含 super(),所以执行父类的构造器
* 2.2、父类的构造器也隐含 super() 和 父类普通代码块和普通属性初始化的调用,
* 输出:(5)类A 的第一个普通代码块被调用... (6)getN2()...
* 2.3、然后执行父类的构造方法
* 输出:(7)A 类无参构造器被调用...
* 2.4、子类普通代码块和普通属性初始化的调用
* 输出:(8)getN4()... (9)类B 的第一个普通代码块...
* 2.5、然后才执行子类的构造方法
* 输出:(10)B 类无参构造器被调用...
*/
new B();
}
}
class A { // 父类是 Object ,在 Object 的构造器中无输出,所以看不出效果
private static int n1 = getN1();
static {
System.out.println("类A 的第一个静态代码块被调用...");
}
{ // 普通代码块
System.out.println("类A 的第一个普通代码块被调用...");
}
public int n3 = getN2();
public static int getN1() {
System.out.println("getN1()...");
return 10;
}
public int getN2() {
System.out.println("getN2()...");
return 10;
}
public A(){ // 构造器
/**
* 此处隐藏了两句代码的执行
* (1)super()
* (2)调用本类的普通代码块
*/
System.out.println("A 类无参构造器被调用...");
}
}
class B extends A {
public static int n3 = getN3();
static {
System.out.println("类B 的一个静态代码块...");
}
public int n5 = getN4();
{ // 普通代码块
System.out.println("类B 的第一个普通代码块...");
}
public static int getN3(){
System.out.println("getN3()...");
return 10;
}
public int getN4(){
System.out.println("getN4()...");
return 10;
}
public B(){ // 构造器
/**
* 此处隐藏了两句代码的执行
* (1)super()
* (2)调用本类的普通代码块
*/
System.out.println("B 类无参构造器被调用...");
}
}
- 静态代码块只能直接调用静态成员(静态方法和静态属性),普通代码块可以调用任意成员
class C {
private static int n1 = 100;
private int n2 = 200;
private static void m1() {}
private void m2() {}
static {
// 静态代码块,只能调用静态成员
System.out.println(n1); // ok
//System.out.println(n2); // 报错
m1(); // ok
//m2(); // 报错
}
{
// 普通代码块,可以调用任意成员
System.out.println(n1); // ok
System.out.println(n2); // ok
m1(); // ok
m2(); // ok
}
}