Java 代码块详解:控制流、方法、实例、静态及同步代码块
本文详细介绍了 Java 中的五种代码块:控制流语句代码块、方法代码块、实例初始化代码块、静态代码块和同步代码块。阐述了各类代码块的定义、语法特征、作用域及使用场景。重点讲解了实例代码块与构造方法的区别,静态代码块与构造方法的区别,以及它们在类加载和对象创建过程中的执行顺序。通过代码示例展示了同步代码块在多线程环境下的应用,并通过练习题巩固了静态变量与局部变量的概念。

本文详细介绍了 Java 中的五种代码块:控制流语句代码块、方法代码块、实例初始化代码块、静态代码块和同步代码块。阐述了各类代码块的定义、语法特征、作用域及使用场景。重点讲解了实例代码块与构造方法的区别,静态代码块与构造方法的区别,以及它们在类加载和对象创建过程中的执行顺序。通过代码示例展示了同步代码块在多线程环境下的应用,并通过练习题巩固了静态变量与局部变量的概念。

在学习各种语言的时候,有些语句需要使用 {} 将代码围起来,有些语句确不用,但是总体来说所有的代码被包围在一个 {} 中,这是为什么呢?
答:想一想我们学数学时的复杂运算,是不是有很多的括号?如:(), [], {}。有了这些括号就使得运算有条理。
换言之,大括号 {} 用于明确地定义代码块的开始和结束,使得代码的组织结构更加清晰。这有助于阅读和理解代码,尤其是在复杂的程序中,代码块可能嵌套在其他代码块内部。
学习 Java 编程语言时,理解代码块的概念是非常重要的。代码块是 Java 中的基本组成部分之一,它允许你将一组语句组织在一起,以便可以作为一个单元进行处理。
代码块的定义
代码块是一组用大括号 {} 包围的语句。在 Java 中,代码块可以出现在多个地方,比如在方法内部、循环结构中、条件语句中等。
代码块的类型
代码块的作用域
代码块中定义的变量只能在该代码块内部被访问,这称为局部变量。局部变量的作用域仅限于定义它们的代码块。
控制流语句代码块是普通代码块的一种特殊应用,它们出现在控制流语句(如 if、else、for、while、do-while、switch 等)中,用于指定在特定条件下或循环迭代中应该执行的代码。这些代码块没有特殊的关键字修饰,它们仅仅是由大括号 {} 包围的一组语句。
switch 语句中的代码块
int month = 4;
switch (month) {
case 1:
case 2:
case 3:
// 这是一个代码块,用于处理 1 月、2 月和 3 月
System.out.println("第一季度");
break;
case 4:
case 5:
case 6:
// 另一个代码块,用于处理 4 月、5 月和 6 月
System.out.println("第二季度");
break;
// 可以有更多的 case 和代码块
default:
// 默认情况下的代码块
System.out.println("其他季度");
}
while 循环代码块
int count = 0;
while (count < 3) {
// 这是一个代码块,用于循环直到 count 等于 3
System.out.println("计数:" + count);
count++;
}
for 循环中的代码块
// 使用 for 循环打印 1 到 5 的数字
for (int i = 1; i <= 5; i++) {
// 这是一个代码块,用于每次循环迭代
System.out.println(i);
}
if-else 语句中的代码块
int score = 85;
if (score >= 90) {
// 这是一个代码块,用于处理分数大于等于 90 的情况
System.out.println("优秀");
} else {
// 另一个代码块,用于处理分数小于 90 的情况
System.out.println("良好");
}
方法代码块是方法体的具体实现,它定义了方法的行为。方法代码块以一对大括号 {} 开始和结束,位于方法声明之后。
方法代码块的主要作用是实现方法的逻辑。它包含了方法执行时需要执行的所有操作,比如变量声明、条件判断、循环、数据计算等。
public class ControlFlowInMethod {
public static void main(String[] args) {
// 调用方法并传递参数
printMultiplesOfTen(5);
}
// 方法声明,用于打印 10 的倍数
public static void printMultiplesOfTen(int count) {
// 方法代码块开始
for (int i = 1; i <= count; i++) {
// 控制流语句的代码块
if (i % 2 == 0) {
// 检查是否为偶数
System.out.println("10 * " + i + " = " + (10 * i));
}
}
// 方法代码块结束
}
}
在这个例子中,printMultiplesOfTen 方法用于打印 10 的倍数。
方法代码块中包含了一个 for 循环,这是一个 控制流语句的代码块。在 for 循环内部,还有一个 if 语句,它 也是一个控制流语句的代码块,用于检查当前的迭代次数 i 是否为偶数,如果是偶数,则打印出 10 乘以当前迭代次数的结果。
当 main 方法被调用时,它会调用 printMultiplesOfTen 方法,并传递一个参数 5,这意味着方法会打印出 10 的 1 倍到 5 倍的偶数倍数。
实例初始化代码块(Instance Initializer Block),也简称为实例代码块,是在类体内部、方法外部定义的代码块,它不属于任何方法。实例初始化代码块主要用于初始化类的实例变量,可以看作是对象创建时的初始化设置。
它没有访问修饰符、返回类型、名称或参数列表,只有一对大括号 {} 包含的代码。
实例初始化代码块在对象被创建时执行,且在任何构造方法执行之前执行。如果有多个构造方法,实例初始化代码块在每个构造方法之前都会执行。
public class Example {
int number;
String name;
// 实例初始化代码块
{
number = 10;
// 初始化实例变量 number
name = "初始化数据";
// 初始化实例变量 name
System.out.println("实例代码块已经运行");
}
// 构造方法
public Example() {
System.out.println("构造函数已经被运行");
System.out.println("--------------------------------");
System.out.println("Constructor is called.");
}
public static void main(String[] args) {
Example obj = new Example();
System.out.println("Number: " + obj.number + ", Name: " + obj.name);
}
}
在这个例子中,实例初始化代码块在对象 obj 被创建时执行,它初始化了 number 和 name 两个实例变量,并且会打印出被使用的消息。随后,当构造方法 Example() 被调用时,它会打印出构造方法被调用的信息。最后,在 main 方法中,我们可以看到对象的实例变量已经被初始化。

虽然 构造方法 和 实例代码块 都可以用于初始化对象,但它们之间存在一些区别:
this 关键字调用其他构造方法,而实例代码块不能。静态代码块(Static Initializer Block),也称为静态初始化块,是 Java 类中的一个特殊代码块,它使用 static 关键字声明。静态代码块主要用于初始化类的静态变量或执行只需要执行一次的操作。
静态代码块定义在类的成员变量声明之后、方法定义之前。它使用 static 关键字标记,并且没有访问修饰符、返回类型、名称或参数列表,只有一对大括号 {} 包含的代码。
静态代码块在类被 Java 虚拟机(JVM)加载时执行,且仅执行一次。这意味着静态代码块的执行与类的任何对象的创建无关,它只与类的加载有关。
public class Example {
static int staticNumber;
static String staticName;
// 静态代码块
static {
staticNumber = 10;
// 初始化静态变量 staticNumber
staticName = "初始化数据";
// 初始化静态变量 staticName
System.out.println("Static block is executed.");
}
public static void main(String[] args) {
System.out.println("Static number: " + staticNumber);
System.out.println("Static name: " + staticName);
}
}
在这个例子中,静态代码块在 Example 类被加载时执行,它初始化了两个静态变量 staticNumber 和 staticName,并打印了一条消息。当 main 方法被调用时,它打印出这两个静态变量的值。

静态代码块与构造方法的主要区别在于:
静态代码块适用于以下场景:
同步代码块(Synchronized Block)是 Java 中用于实现线程同步的一种机制。它允许多个线程在访问共享资源时,保证同一时间只有一个线程可以执行特定的代码段,从而避免发生线程安全问题,如数据不一致、竞态条件等。
同步代码块可以通过 synchronized 关键字来定义。它可以出现在任何方法中,或者作为一个独立的代码块出现在方法内部。当使用 synchronized 关键字时,你需要指定一个锁对象(lock object),代码块内的代码只有在获得该对象的锁之后才能执行。
同步代码块的主要作用是确保在多线程环境中,当一个线程访问同步代码块时,其他线程将被阻塞,直到锁被释放。这样可以防止多个线程同时修改同一个资源,确保数据的一致性和线程安全。
public class Example {
private static final Object lock = new Object();
public void methodOne() {
synchronized (lock) {
// 同步代码块
System.out.println("Method One is executing.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method One finished.");
}
}
public void methodTwo() {
synchronized (lock) {
// 同步代码块
System.out.println("Method Two is executing.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method Two finished.");
}
}
public static void main(String[] args) {
final Example example = new Example();
Thread threadOne = new (() -> example.methodOne());
(() -> example.methodTwo());
threadOne.start();
threadTwo.start();
}
}
在这个例子中,methodOne 和 methodTwo 都包含了一个同步代码块,它们使用相同的锁对象 lock。当 main 方法启动两个线程时,它们将尝试执行这两个方法。由于同步代码块使用了相同的锁对象,所以这两个方法不会同时执行,从而避免了线程安全问题。
同步代码块与同步方法的主要区别在于:
synchronized 关键字修饰整个方法,而同步代码块只同步方法中的特定部分。同步代码块适用于以下场景:
public class Example {
// 静态代码块
static {
System.out.println("静态代码块已经运行");
}
// 实例初始化代码块
{
System.out.println("实例代码块已经运行");
}
// 构造方法
public Example() {
System.out.println("构造函数已经被运行");
}
public static void main(String[] args) {
Example obj = new Example();
}
}
运行结果:

有同学疑问,运行顺序是不是和代码块写的顺序有关呢?
那打乱顺序再次运行:
public class Example {
// 构造方法
public Example() {
System.out.println("构造函数已经被运行");
}
// 实例初始化代码块
{
System.out.println("实例代码块已经运行");
}
// 静态代码块
static {
System.out.println("静态代码块已经运行");
}
public static void main(String[] args) {
Example obj = new Example();
}
}

显然,代码块运行的顺序和代码块的位置无关,先运行静态代码块,再运行实例代码块,最后运行构造函数。
同学们思考一下将会打印出什么呢?
class Father {
static {
System.out.println("父类的静态代码块");
}
{
System.out.println("父类的实例代码块");
}
public Father() {
System.out.println("父类的构造函数");
}
}
public class Son extends Father {
static {
System.out.println("子类的静态代码块");
}
{
System.out.println("子类的实例代码块");
}
public Son() {
// super();
System.out.println("子类的构造函数");
}
public static void main(String[] args) {
Son son = new Son();
}
}
运行结果

下面的代码又会打印出什么呢?是不是上面的内容打印两边呢?
class Father {
static {
System.out.println("父类的静态代码块");
}
{
System.out.println("父类的实例代码块");
}
public Father() {
System.out.println("父类的构造函数");
}
}
public class Son extends Father {
static {
System.out.println("子类的静态代码块");
}
{
System.out.println("子类的实例代码块");
}
public Son() {
// super();
System.out.println("子类的构造函数");
}
public static void main(String[] args) {
Son son1 = new Son();
System.out.println("=======================");
Son son2 = new Son();
}
}
打印结果:

下面的代码运行后会发生什么?
public class Test {
public int aMethod() {
static int i = 0;
i++;
return i;
}
public static void main(String[] args) {
Test test = new Test();
test.aMethod();
int j = test.aMethod();
System.out.println(j);
}
}

答:D 解析:局部代码块中不能有静态变量。只有成员变量才能是静态变量。
下列代码运行后会输出什么?
public class Test {
static int cnt = 6;
static {
cnt += 9;
}
public static void main(String[] args) {
System.out.println("cnt = " + cnt);
}
static {
cnt /= 3;
}
}

答案:A 解析: (1)静态变量初始化:static int cnt = 6; 在类加载时进行初始化,因此 cnt 的初始值为 6。 (2)第一个静态代码块先被执行。在第一个静态代码块中,cnt 的值被增加了 9,即:cnt = 6 + 9 = 15。 (3)紧接着,第二个静态代码块执行,此时 cnt = 15。在第二个静态代码块中,cnt 的值被除以 3,即:cnt = 15 / 3 = 5。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online