目录
代码块是什么
普通代码块
构造块
静态块
同步代码块
代码块是什么
代码块:使用 {} 定义的一段代码
根据代码块 定义的位置 和 关键字,代码块可分为以下 四种:
普通代码块
构造块
静态块
同步代码块
普通代码块
普通代码块:定义在方法中的代码块
public class Test { public static void main(String[] args) { { int a = 5; System.out.println("a: " + a); } int a = 10; System.out.println("a: " + a); }}
运行结果:
在 普通代码块 中定义的变量 a,不能在 方法 中使用,即 a 在代码块执行完毕时就被销毁了
普通代码块的执行顺序为 顺序执行(在方法中从上往下,先出现,先执行)
public class Test { public static void main(String[] args) { int a = 10; System.out.println("a: " + a); { a = 5; System.out.println("a: " + a); } }}
运行结果:
在方法中定义的变量可以在代码块中进行访问和修改
构造块
构造块:定义在类中的代码块,也叫做实例代码块
构造代码块一般用于初始化实例成员变量
public class Student { private int id; private String name; private int age; public Student() { System.out.println("init..."); } // 构造代码块 { id = 1; name = "张三"; age = 19; System.out.println("执行构造代码块"); } public void info() { System.out.println("id: " + id + " name: " + name + " age: " + age); } public static void main(String[] args) { Student student = new Student(); student.info(); }}
运行结果:
构造代码块在构造方法之前执行,且与构造代码块的位置无关,无论构造代码块放在哪里,都会先执行构造代码块,再执行构造方法
public class Student { private int id; private String name; private int age; public Student() { System.out.println("init..."); } // 构造代码块 { id = 1; name = "张三"; age = 19; System.out.println("执行构造代码块"); } { id = 2; System.out.println("再次执行构造方法"); } public void info() { System.out.println("id: " + id + " name: " + name + " age: " + age); } public static void main(String[] args) { Student student = new Student(); student.info(); }}
运行结果:
当有多个构造代码块时, 编译器会按照定义的先后顺序,将这些构造代码块进行合并,然后按照顺序执行
使用构造方法就可以初始化实例成员变量,为什么还需要使用构造代码块呢?
构造代码块相当于是对构造器的补充
还是上述例子:
public class Student { private int id; private String name; private int age; public Student() { } public Student(int id) { this.id = id; } public Student(int id, String name) { this.id = id; this.name = name; } public Student(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } // 构造代码块 { id = 1; name = "张三"; age = 19; System.out.println("执行构造代码块"); } public void info() { System.out.println("id: " + id + " name: " + name + " age: " + age); } public static void main(String[] args) { Student student = new Student(); student.info(); Student student1 = new Student(2, "李四"); student1.info(); Student student2 = new Student(3, "王五", 18); student2.info(); }}
运行结果:
无论调用哪个构造方法,在每次创建实例时,都会先执行构造代码块,再执行构造方法,因此,若多个构造器中都有重复的语句,就可以将其抽取到构造代码块中,或是在构造代码块中为变量赋默认值
静态块
静态代码块:使用 static 定义的代码块
一般用于初始化静态成员变量
public class Student { private int id; private String name; private int age; private static String classRoom; public Student() { } public Student(int id) { this.id = id; } public Student(int id, String name) { this.id = id; this.name = name; } public Student(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } // 构造代码块 { id = 1; name = "张三"; age = 19; System.out.println("执行构造代码块"); } // 静态代码块 static { classRoom = "一班"; System.out.println("执行静态代码块"); } public void info() { System.out.println("id: " + id + " name: " + name + " age: " + age); } public static void main(String[] args) { Student student = new Student(); student.info(); Student student1 = new Student(2, "李四"); student1.info(); Student student2 = new Student(3, "王五", 18); student2.info(); }}
运行结果:
我们可以发现:静态代码块在构造代码块之前执行,且只执行了一次
为什么静态代码块只执行了一次呢?
静态代码块一般用于初始化静态成员变量,而静态成员变量是类的属性,因此,静态成员变量是在 JVM 进行 类加载 时就为其开辟了空间并进行了初始化,也就是说静态代码块在类加载时就被执行了,后续也就不会被执行了
public class Student { private int id; private String name; private int age; private static String classRoom; public Student() { } public Student(int id) { this.id = id; } public Student(int id, String name) { this.id = id; this.name = name; } public Student(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } // 构造代码块 { id = 1; name = "张三"; age = 19; System.out.println("执行构造代码块"); } // 静态代码块 static { classRoom = "一班"; System.out.println("执行静态代码块"); } static { classRoom = "二班"; System.out.println("再次执行静态代码块"); } public void info() { System.out.println("id: " + id + " name: " + name + " age: " + age + " class: " + classRoom); } public static void main(String[] args) { Student student = new Student(); student.info(); Student student1 = new Student(2, "李四"); student1.info(); Student student2 = new Student(3, "王五", 18); student2.info(); }}
运行结果:
若类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后顺序,将这些静态代码块进行合并,然后按照顺序执行
即:
静态代码块无论创建多少个对象,都只会执行一次(在类加载时执行)
构造代码块每次创建对象时都会执行(在创建对象时执行)
同步代码块
什么是同步?
在 Java 中,允许多个线程并发执行,当多个线程需要访问和操作同一个资源(共享资源)时,多个线程之间就可能会产生冲突,出现数据不一致或破坏数据完整性的问题。此时就可以使用 同步代码块(synchronized block)来控制多个线程对共享资源的访问,将代码块标记为同步(也就是进行 加锁 操作),保证同一时间只有一个线程能够执行该代码块
在 Java 中一般使用 synchronized 来实现同步代码块
synchronized (Object locker) {
...
}
其中 locker 表示加锁的对象, 只有获取到该对象的锁的线程才能继续执行代码块,这个对象可以是任意的 Java 对象
例如:
public class Test { private static int count = 0; public static void main(String[] args) throws InterruptedException { Object locker = new Object(); Thread t1 = new Thread(() -> { for (int i = 0; i < 5000; i++) { synchronized (locker) { count++; } } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 5000; i++) { synchronized (locker) { count++; } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count: " + count); }}
同步代码块涉及到很多知识,例如 如何进行加锁、有哪些注意事项、加锁过程中可能会产生哪些问题...
更多关于同步代码块的知识,在这里就不展开讲解了,可以参考之前的文章:
线程安全问题_线程安全的例子-CSDN博客