夯实Java基础(八)----代码块
Java中代码块指的是使用”{}”括起来的代码,一共分为四种:
- 普通代码块:在类中方法的方法体中用{}括起来的代码,通常也叫局部代码块
- 构造代码块:类中用{}直接括起来的语句,每次创建对象都会被调用,并且先于构造函数执行
- 静态代码块:类中用static{}括起来的语句,jvm在加载类时执行,并且只执行一次,先于构造代码块块执行
- 同步代码块:类中synchronized(){}括起来的语句,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性。
1、普通代码块
普通代码块:也可以叫局部代码块,在方法、循环、判断等语句中出现的代码块就称为普通代码块。普通代码块和一般语句的执行顺序由他们在代码中出现的次序决定,先出现先执行。
public class CodeBlockTest {
public static void main(String[] args) {
int x = 1;
{
x = 10;
System.out.println("普通代码块内的变量 x=" + x);
int y = 20;
System.out.println("普通代码块内的变量 y=" + y);
int z = 30;
System.out.println("普通代码块内的变量 z=" + z);
}
int y = 40;
System.out.println("主方法内的变量 x=" + x);
System.out.println("主方法内的变量 y=" + y);
{
int z = 50;
System.out.println("普通代码块内的变量 z=" + z);
}
//Cannot resolve symbol 'z'
//System.out.println(z);
}
}
可以看到,代码块中与代码块中定义的变量互不干扰,但是代码块与方法中定义的变量则会冲突,如果先在方法中定义变量,那么在后面的代码块中不能再定义同名的,只能用原变量名接收,而先在代码块中定义变量,在方法中又可以重新定义一次,感兴趣的可以去研究一下。
2、构造代码块
直接在类中用"{}"定义且不加任何关键字的代码块称为构造代码块。构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。如果存在多个构造代码块,执行顺序由他们在代码中出现的次序决定,先出现先执行。
构造代码块的作用:可以用来初始化实例变量和实例环境(即创建对象的必要条件),这样可以减少代码量,同时也可以增强程序的可读性。
public class CodeBlockTest {
{
System.out.println("构造代码块1...");
}
public CodeBlockTest() {
System.out.println("无参构造器...");
}
{
System.out.println("构造代码块2...");
}
public static void main(String[] args) {
new CodeBlockTest();
new CodeBlockTest();
}
}
从运行结果容易看出,构造代码块中的语句比构造方法中语句先执行,即使将构造块写在构造方法后面也是一样的结果,事实上是这样子吗?
原理:其实构造代码块并不是真正的在构造方法之前执行的,而是在构造方法中执行的。JVM在编译的时候会把构造代码块插入到每个构造函数的最前面,构造代码块随着构造方法的执行而执行,如果某个构造方法调用了其他的构造方法,那么构造代码块不会插入到该构造方法中以免构造代码块执行多次。可以从编译后生成的class文件可以看出来。
或者直接查看代码的反编译文件:
3、静态代码块
在类中用static关键字修饰的代码块叫静态代码块。每个静态代码块只会执行一次,并且由于JVM在加载类时会加载静态代码块,所以静态代码块先于主方法执行。如果类中包含多个静态代码块,那么将按照"先定义的代码先执行,后定义的代码后执行"。
静态代码块的特点:
- 静态代码块不能存在于任何方法体内。
- 静态代码块不能直接访问实例变量和实例方法,需要通过类的实例对象来访问。
代码简单示例:
public class Person {
static {
System.out.println("Person类静态块");
}
public Person() {
System.out.println("Person类构造器");
}
}
class Son extends Person {
static {
System.out.println("Son类静态块");
}
public Son() {
System.out.println("Son类构造器");
}
}
class Main {
public static void main(String[] args) {
new Son();
System.out.println("-------");
new Son();
}
}
运行结果:
从运行结果分析:首先运行main()方法,然后JVM就会加载类, 首先是创建了一个Son类的对象,因为Son类继承了Person类,所以会先加载父类Person类,再去加载子类Son。由于静态代码块会随着类的加载而加载,所以先输出父类中静态代码块内容"Person类静态块",然后输出子类中静态代码块内容"Son类静态块"。加载完类之后执行main()方法内容,先new了第一个Son实例,由于子类构造器中默认调用了super(),所以先输出父类构造器的内容,再输出子类构造器的内容。之后又new了第二个Son实例,却是输出的构造器的内容,说明static静态块只加载了一次。
结论:静态代码块是先加载父类的静态代码块,然后再加载子类静态代码块,是随着类的加载而加载,而且只会加载一次。
补充:因为入口main()是个方法,也需要用类去调用,所以类的加载优先级>main()方法。
4、同步代码块
使用synchronized关键字修饰的普通代码块(其只能用在方法体内部)。其作用在多线程的环境下,对共享数据进行加锁, 从而实现线程同步,是一种多线程保护机制,但是注意同步代码块使用不当可能会造成“死锁”问题。具体使用请移至:Java多线程系列
public void show(){
synchronized (obj){
System.out.println("同步代码块");
}
}
5、代码块的执行顺序
上面讲述了四种代码块的基本信息,所以下面结合上面分析的各种情况结合在一起来看一下四种代码块的执行顺序。
public class CodeBlockTest {
static {
System.out.println("静态代码块...");
}
{
System.out.println("构造代码块...");
}
public CodeBlockTest() {
System.out.println("无参构造器...");
}
public void show() {
{
System.out.println("局部代码块...");
}
}
}
class Main {
public static void main(String[] args) {
System.out.println("main()方法运行");
new CodeBlockTest().show();
System.out.println("----------");
new CodeBlockTest().show();
}
}
打印结果:
执行顺序(优先级从高到低):静态代码块 > mian()方法 > 构造代码块 > 构造方法 > 局部代码块。其中静态代码块只执行一次。构造代码块和局部代码块再每次创建对象是都会执行。
TIPS:如果加上变量的话:静态变量 > 静态代码块 > mian()方法 > 普通变量 > 构造代码块 > 构造方法 > 局部代码块
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!