? 欢迎讨论:如对文章内容有疑问或见解,欢迎在评论区留言,我需要您的帮助!
? 点赞、收藏与分享:如果这篇文章对您有所帮助,请不吝点赞、收藏或分享,谢谢您的支持!
? 传播技术之美:期待您将这篇文章推荐给更多对需要学习Java语言、低代码开发感兴趣的朋友,让我们共同学习、成长!
1. 什么是代码块?
在学习各种语言的时候,有些语句需要使用{}
将代码围起来,有些语句确不用,但是总体来说所有的代码被包围在一个{}
中,这是为什么呢?
答:想一想我们学数学时的复杂运算,是不是有很多的括号?如:()
, []
, {}
。有了这些括号就使得运算有条理。
换言之,大括号{}
用于明确地定义代码块的开始和结束,使得代码的组织结构更加清晰。这有助于阅读和理解代码,尤其是在复杂的程序中,代码块可能嵌套在其他代码块内部。
学习Java编程语言时,理解代码块的概念是非常重要的。代码块是Java中的基本组成部分之一,它允许你将一组语句组织在一起,以便可以作为一个单元进行处理。
代码块的定义
代码块是一组用大括号{}
包围的语句。在Java中,代码块可以出现在多个地方,比如在方法内部、循环结构中、条件语句中等。
代码块的类型
控制流语句代码块:定义在控制流语句中(如if、else、for、while、do-while、switch等)中,用于指定在特定条件下或循环迭代中应该执行的代码。方法代码块:定义在方法内部的代码块,通常用于执行特定的任务。实例初始化代码块:没有static关键字的代码块,用于初始化实例变量。静态代码块:用static关键字标记的代码块,通常用于初始化类变量。同步代码块:使用synchronized关键字,用于多线程编程中的同步操作。代码块的作用域
代码块中定义的变量只能在该代码块内部被访问,这称为局部变量。局部变量的作用域仅限于定义它们的代码块。
2. 控制流代码块
2.1 定义
控制流语句代码块是普通代码块的一种特殊应用,它们出现在控制流语句(如if、else、for、while、do-while、switch等)中,用于指定在特定条件下或循环迭代中应该执行的代码。这些代码块没有特殊的关键字修饰,它们仅仅是由大括号{}
包围的一组语句。
2.2 示例
if-else
语句中的代码块
int score = 85;if (score >= 90) { // 这是一个代码块,用于处理分数大于等于90的情况 System.out.println("优秀");} else { // 另一个代码块,用于处理分数小于90的情况 System.out.println("良好");}
for
循环中的代码块
// 使用for循环打印1到5的数字for (int i = 1; i <= 5; i++) { // 这是一个代码块,用于每次循环迭代 System.out.println(i);}
while
循环代码块
int count = 0;while (count < 3) { // 这是一个代码块,用于循环直到count等于3 System.out.println("计数: " + count); count++;}
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("其他季度");}
3. 方法代码块
3.1 定义
方法代码块是方法体的具体实现,它定义了方法的行为。方法代码块以一对大括号{}
开始和结束,位于方法声明之后。
方法代码块的主要作用是实现方法的逻辑。它包含了方法执行时需要执行的所有操作,比如变量声明、条件判断、循环、数据计算等。
3.2 示例及说明
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倍的偶数倍数。
4. 实例代码块(构造代码块)
4.1 定义
实例初始化代码块(Instance Initializer Block),也简称为实例代码块,是在类体内部、方法外部定义的代码块,它不属于任何方法。实例初始化代码块主要用于初始化类的实例变量,可以看作是对象创建时的初始化设置。
它没有访问修饰符、返回类型、名称或参数列表,只有一对大括号{}
包含的代码。
实例初始化代码块在对象被创建时执行,且在任何构造方法执行之前执行。如果有多个构造方法,实例初始化代码块在每个构造方法之前都会执行。
4.2 示例及说明
public class Example { int number; String name; // 实例初始化代码块 { number = 10; // 初始化实例变量number name = "Code哈哈笑"; // 初始化实例变量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方法中,我们可以看到对象的实例变量已经被初始化。
4.3 实例代码块与构造方法的区别
虽然构造方法
和实例代码块
都可以用于初始化对象,但它们之间存在一些区别:
this
关键字调用其他构造方法,而实例代码块不能。实例代码块在任何构造方法执行之前执行,而构造方法在对象创建时执行。 5. 静态代码块
5.1 定义
静态代码块(Static Initializer Block),也称为静态初始化块,是Java类中的一个特殊代码块,它使用static
关键字声明。静态代码块主要用于初始化类的静态变量或执行只需要执行一次的操作。
静态代码块定义在类的成员变量声明之后、方法定义之前。它使用static
关键字标记,并且没有访问修饰符、返回类型、名称或参数列表,只有一对大括号{}
包含的代码。
静态代码块在类被Java虚拟机(JVM)加载时执行,且仅执行一次。这意味着静态代码块的执行与类的任何对象的创建无关,它只与类的加载有关。
5.2 示例及说明
public class Example { static int staticNumber; static String staticName; // 静态代码块 static { staticNumber = 10; // 初始化静态变量staticNumber staticName = "Code哈哈笑"; // 初始化静态变量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
方法被调用时,它打印出这两个静态变量的值。
5.3 静态代码块与构造方法的区别
静态代码块与构造方法的主要区别在于:
静态代码块用于初始化静态变量,而构造方法用于初始化实例变量。静态代码块在类加载时执行,而构造方法在对象创建时执行。静态代码块中的代码不能访问类的实例变量或调用实例方法,因为这些实例成员依赖于具体的对象实例。5.4 使用场景
静态代码块适用于以下场景:
初始化静态常量。执行只需要执行一次的资源密集型操作,如加载配置文件、连接数据库等。在类加载时执行一些必要的初始化操作,这些操作与任何对象的创建无关。6. 同步代码块
6.1 定义
同步代码块(Synchronized Block)是Java中用于实现线程同步的一种机制。它允许多个线程在访问共享资源时,保证同一时间只有一个线程可以执行特定的代码段,从而避免发生线程安全问题,如数据不一致、竞态条件等。
同步代码块可以通过synchronized
关键字来定义。它可以出现在任何方法中,或者作为一个独立的代码块出现在方法内部。当使用synchronized
关键字时,你需要指定一个锁对象(lock object),代码块内的代码只有在获得该对象的锁之后才能执行。
同步代码块的主要作用是确保在多线程环境中,当一个线程访问同步代码块时,其他线程将被阻塞,直到锁被释放。这样可以防止多个线程同时修改同一个资源,确保数据的一致性和线程安全。
6.2 示例及说明
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 Thread(() -> example.methodOne()); Thread threadTwo = new Thread(() -> example.methodTwo()); threadOne.start(); threadTwo.start(); }}
在这个例子中,methodOne
和methodTwo
都包含了一个同步代码块,它们使用相同的锁对象lock。当main方法启动两个线程时,它们将尝试执行这两个方法。由于同步代码块使用了相同的锁对象,所以这两个方法不会同时执行,从而避免了线程安全问题。
6.3 同步代码块与同步方法的区别
同步代码块与同步方法的主要区别在于:
同步方法使用synchronized
关键字修饰整个方法,而同步代码块只同步方法中的特定部分。同步代码块允许更细粒度的控制,你可以只同步需要同步的部分,而不是整个方法。 同步代码块适用于以下场景:
当只需要同步方法中的一小部分代码时。当多个方法需要同步同一个资源,但不是整个方法时。当需要提高性能,减少不必要的同步开销时。7. 实例代码块(构造代码块)、静态代码块和构造函数的运行顺序
7.1 在同一个类中
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(); }}
显然,代码块运行的顺序和代码块的位置无关,先运行静态代码块,再运行实例代码块,最后运行构造函数。
7.2 在继承关系中
同学们思考一下将会打印出什么呢?
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(); }}
打印结果:
8. 练习题
下列代码运行后会输出什么?
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。
下面的代码运行后会发生什么?
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
解析:局部代码块中不能有静态变量。只有成员变量才能是静态变量。