Java-代码块的使用细节

1. 代码块的基本介绍

代码块又称初始化块,属于类中的成员【是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过 {} 包围起来

但和方法不同,代码块没有方法名,也没有返回值和参数列表,仅仅只有一个方法体。代码块不能通过对象或者类去显示调用,而是在加载类时、或者创建对象时隐式调用。

2. 代码块的基本语法

[修饰符] {
      逻辑语句;
};

注意
(1) 修饰符可写可不写,若要写,仅仅能写 static
(2) 代码块可分为两类,使用 static 修饰的叫做静态代码块;没有 static 修饰的,叫普通代码块/非静态代码块
(3) 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
(4) ; 号可以省略

3. 使用代码块的好处

(1) 代码块相当于另一种形式的构造器,是对构造器的补充机制,可以做初始化的操作
(2) 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

下面通过一个案例,来展现代码块的一个使用场景:

class Movie {
	private String name;
	private double price;
	private String director;
	
	// 以下三个构造器都有相同的语句
	// 这样代码看起来会比较冗余
	public Movie() {
		System.out.println("电影屏幕打开...");
		System.out.println("广告开始...");
		System.out.println("电影正式开始...");
	}
	
	public Movie(String name) {
		System.out.println("电影屏幕打开...");
		System.out.println("广告开始...");
		System.out.println("电影正式开始...");
		this.name = name;
	}
	
	public Moive(String name, double price) {
		System.out.println("电影屏幕打开...");
		System.out.println("广告开始...");
		System.out.println("电影正式开始...");
		this.name = name;
		this.price = price;
	}

	public Movie(String name, double price, String director) {
		System.out.println("电影屏幕打开...");
		System.out.println("广告开始...");
		System.out.println("电影正式开始...");
		this.name = name;
		this.price = price;
		this.director = director;
	}
}

这时,我们可以将构造器中相同的部分,放入一个代码块中:

class Movie {
	private String name;
	private double price;
	private String director;
	
	// 使用代码块
	// 这样不管我们调用哪个构造器去创建对象,都会先调用代码块中的内容
	// 代码块调用的顺序优先于构造器
	{
		System.out.println("电影屏幕打开...");
		System.out.println("广告开始...");
		System.out.println("电影正式开始...");
	}
	
	public Movie() {
	}
	
	public Movie(String name) {
		this.name = name;
	}
	
	public Moive(String name, double price) {
		this.name = name;
		this.price = price;
	}

	public Movie(String name, double price, String director) {
		this.name = name;
		this.price = price;
		this.director = director;
	}
}

4. 使用代码块的细节

static 代码块也叫静态代码块,作用是对类进行初始化,而且它随着 类的加载 而执行,并且仅仅只会执行一次;但如果是普通代码块,每次使用 new 去创建一个对象,就会执行,可以这样记忆:普通代码块只有构造器被调用了才会调用,与类是否被加载无关。

类什么时候会被加载【重点】
(1) 使用 new 创建对象实例时
(2) 创建子类对象实例时,其父类会先被加载
(3) 使用类的某一个静态成员时(静态属性、静态方法),该类也会被加载

普通代码块在创建对象实例时,会被隐式的调用,每被创建一次,就会被调用一次,跟类是否被加载无关
但是如果是仅仅使用类的某个静态成员时,普通代码块并不会被执行,但是由于使用类的静态成员会引起类的加载,在加载时会调用该类的 static 代码块。

演示:因为加载类时,会先加载父类,再加载子类,因此父类的静态代码块会相较于子类的静态代码块先输出:

class Animal {
	static {
		System.out.println("Animal 的静态代码块被执行..."); // (1)
	}
}

class Cat extends Animal {
	public static int num = 999;	// 静态属性
	
	// 普通代码块,在new 对象被调用时,才会被执行
	{
		System.out.println("Cat 的普通代码块被执行..."); // 调用静态成员时,类的普通代码块不执行
	}
	
	static {
		System.out.println("Cat 的静态代码块被执行..."); // (2)
	}
}

public class Main {
	public static void main(String[] args) {
		// 输出顺序见注释
		System.out.println(Cat.num); // (3)
	}
}

创建一个对象时,在一个类内部调用的顺序【重点、难点】
调用顺序如下:

(1) 调用静态代码块和静态属性的初始化;
注意:静态代码块和静态属性初始化调用的优先级一致,如果有多个静态代码块和多个静态变量初始化,则按照他们定义的顺序依次调用。

(2) 调用普通代码块和普通属性的初始化;
注意:普通代码块和普通属性初始化调用的优先级一致,如果有多个普通代码块和多个普通属性初始化,则按照他们定义的顺序依次调用。

(3) 调用类的构造方法。

练习:

class A {
	// 这里使用方法来进行属性初始化,完全是为了打印调用顺序演示
	private static n1 = getVal();
	private int n2 = getVal2();
	
	{
		System.out.println("A的普通代码块被调用..."); // (4)
	}

	static {
		System.out.println("A的静态代码块被调用..."); // (2)
	}

	public A() {
		// 隐藏:调用 super();
		// 隐藏:调用 {} 普通方法块和普通属性初始化
		System.out.println("A的构造器被调用..."); // (5)
	}
	
	public static int getVal() {
		System.out.println("getVal() 方法被执行..."); // (1)
		return 10;
	}

	public int getVal2() {
		System.out.println("getVal2() 方法被执行..."); // (3)
		return 30;
	}
}

public class Main {
	public static void main(String[] args) {
		new A(); // 打印顺序参考备注
	}
}

由上面的例题中的注释:每一个类的构造器(构造方法)的方法体中,其实隐含了 super()调用普通代码块和普通属性初始化

class A {
      // (1) super();
      // (2) 调用普通代码块和普通属性初始化;
      (3) 构造器自己的方法…
};

当包含继承关系,创建子类时代码块、构造器的调用顺序【重点、难点、面试题常考】
调用顺序如下:

(1) 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
(2) 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
(3) 父类的普通代码块和普通属性初始化(优先级一样,按照定义顺序执行)
(4) 父类的构造方法
(5) 子类的普通代码块和普通属性初始化(优先级一样,按照定义的顺序执行)
(5) 子类的构造方法

例题:

class A {
	private static int n1 = getVal01();
	static {
		System.out.println("A 的一个静态代码块..."); // (2)
	}
	{
		System.out.println("A 的一个普通代码块..."); // (5)
	}
	public int n2 = getVal02();
	public static int getVal01() {
		System.out.println("A 的静态属性初始化..."); // (1)
		return 10;
	}
	public int getVal02() {
		System.out.println("A 的普通属性初始化..."); // (6)
		return 10;
	}
	public A() {
		// super()
		// {} 和 普通属性初始化
		System.out.println("A 的构造器..."); // (7)
	}
}

class B extends A {
	private static int n3 = getVal03();
	static {
		System.out.println("B 的一个静态方法块..."); // (4)
	}
	public int n4 = getVal04();
	{
		System.out.println("B 的一个普通代码块..."); // (9)
	}
	public static int getVal03() {
		System.out.println("B 的静态属性初始化..."); // (3)
		return 10;
	}
	public int getVal04() {
		System.out.println("B 的普通属性初始化..."); // (8)
		return 10;
	}
	public B() {
		// super()
		// {} 和 普通属性初始化
		System.out.println("B 的构造器..."); // (10)
	}
}

public class Main {
	public static void main(String[] args) {
		new B();	// 备注中显示顺序
	}
}

运行结果如下图所示:
在这里插入图片描述

此外,还需要注意的是:
静态代码块和静态方法一致,只能使用静态属性和静态方法。
普通代码块和普通方法一致,不仅可以使用普通属性和普通方法,也可以使用静态方法和静态属性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁头娃撞碎南墙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值