一、引入
例如下图中,在语句上下加一个单独的 大括号
,这个就叫做 代码块
。
根据代码块出现的位置不同,我们可以把它分为三类。
1、写在方法中的 局部代码块
2、写在成员位置(方法外,类里面)的 构造代码块
3、使用 static 修饰的 静态代码块
二、局部代码块
1)介绍
局部代码块
这个词语我们需要分为两部分进行解释。
局部
:在代码中 局部位置
指的是在 方法里面
。
代码块
:就是刚刚看到的单独的大括号。
因此 局部代码块
合起来就是:写在 方法里面
的一对 单独的大括号
,如下图所示。
![image-20240417162230849](https://img-blog.csdnimg.cn/img_convert/c8d7f58513533950f3c06b9d736ec1a2.png)
作用:提前结束变量的生命周期。在之前我们曾经讲过:变量的作用范围,它只在所属的大括号中有效。
而现在,变量 a
是定义在 局部代码块
中的,因此它只能在 局部代码块
里面才能使用 变量a
。
当代码执行到 左括号
时,变量a
就会从内存中消失了。
![image-20240417162625768](https://img-blog.csdnimg.cn/img_convert/7fb19ddfc131188461312d8cfae080e6.png)
这个技术听上去很牛逼,但是现在已经用不了了。我们细品,这个技术最本质的作用就是:节约内存,变量用不到了,我就立马让它消失。
在很久很久以前,计算机硬件不是很发达的时候才会用到 局部代码块
。例如以前的游戏机
![image-20240417162230849](https://img-blog.csdnimg.cn/img_convert/c8d7f58513533950f3c06b9d736ec1a2.png)
它的内存很小很小,其中有一个很经典的游戏,它的大小只有 128k
,这么小的内存为什么还有那么丰富的剧情呢?
![image-20240417162625768](https://img-blog.csdnimg.cn/img_convert/7fb19ddfc131188461312d8cfae080e6.png)
它里面用到了很多编程思想,其中有一个就是我们今天说的局部代码块
的思想,用完了就让你立马回收,不会让你浪费一丁点内存空间。
虽然这个游戏不是Java写的,但是用完后 立马回收
的思想 就跟我们现在的 局部代码块
是一致的。
但是,随着现在计算机的硬件发展的越来越好,这个技术已经渐渐的淘汰了。现在电脑内存,少有十几个G,服务器的内存就更大了,你多定义几个变量,浪费几个字节的空间,都无所谓。
因此局部代码块我们了解一下就可以了。
2)代码示例
{
int a = 10;
} // 当代码执行到这里时,变量a就从内存中消失了
System.out.println(a); // 一旦消失,这行代码就会报错
二、构造代码块
1)介绍
例如在下图中,有两个构造方法:第一个是空参构造
,第二个是带全部参数的构造
。
在这两个构造方法中都打印了一句话:"开始创建对象了"
,此时打印语句在两个构造方法中就重复了。
此时就可以将重复的代码抽取出来,将它写在构造代码块中。
此时你就会发现,构造代码块
其实就是写在 成员位置
的代码块。
当我们要创建 Student对象
的时候,它会优先于 构造方法
执行。所以在以前,我们可以把多个构造方法中重复的代码写在 构造代码块
中。
2)代码示例
下面这段代码,无论是使用 无参构造
还是 全参构造
创建对象,都会打印 "开始创建对象了"
//构造代码块:
//1.写在成员位置的代码块 就是 构造代码块
//2.作用:可以把多个构造方法中重复的代码抽取出来
//3.执行时机:我们在创建本类对象的时候会先执行 构造代码块,再执行 构造方法。构造代码块它是优先于构造方法执行的。
{
System.out.println("开始创建对象了");
}
public Student() {
System.out.println("空参构造");
}
public Student(String name, int age) {
System.out.println("有参构造");
this.name = name;
this.age = age;
}
这个技术现在其实也渐渐的淘汰了。
其实就是因为这种写法它不够灵活,假设现在写了第三个构造。
public Student(String name) {
System.out.println("有参构造");
this.name = name;
}
使用第三个构造方法创建对象的时候,我不想执行 "开始创建对象了"
这句话,是没有办法的,因为 构造代码块
必定会优先于 构造方法
执行。
3)解决问题
如果三个构造方法中,有两个构造方法中有重复的代码,我们该如何解决呢?
方法一:此时我们会把重复的代码写在一个构造方法中,然后在其他的构造方法中就不需要再写了,我们可以通过 this关键字
去调用本类的其他构造。如果不知道 name、age
的值是多少,只需要在构造的时候给它一个默认值就可以了。
![image-20240417165805998](https://img-blog.csdnimg.cn/img_convert/013478ecea4adc4ea82ca5e05224a4ac.png)
方法二:将重复的代码抽取成一个方法,在构造方法中想要用到,直接调用方法就行了。
因此,由于 构造代码块
不灵活,如果构造方法中有重复的代码,我们大部分使用的还是上面两种方式。对于 构造代码块
来说,我们只需要了解一下就行了。
三、静态代码块
静态代码块是一个重点,需要大家掌握,在以后会大量用到。
1)介绍
静态代码块
其实就是在 构造代码块
前面加上了 static
静态关键字。
特点:需要通过 static关键字
修饰,随着类的加载而加载,并且自动触发、只执行一次。
而构造代码块
是每次创建对象的时候,它一定都会执行。你创建一个对象,它执行1次;你创建十个对象,它执行10次。
但是静态代码块不一样,它只执行一次,而且它是随着类的加载而加载的。当这个类加载到内存中的时候,静态代码块
中的代码它就会自动的执行一次。
使用场景:在类加载的时候,做一些数据初始化的时候使用。
2)代码实现
//执行时机:
//随着类的加载而加载的,并且只执行一次。
static {
System.out.println("静态代码块执行了");
}
执行下段代码,可以发现创建了两个对象,但是只打印了一句 "静态代码块执行了"
,并且是在一开始的时候执行的。
//创建对象
Student s1 = new Student();
Student s2 = new Student("zhangsan",23);
3)使用场景
使用场景:在类加载的时候,做一些数据初始化的时候使用。
例如之前我们写的 学生管理系统
,我们发现,每次程序运行的时候,我们都要去注册用户名、添加学生信息。。。等很多操作,感觉太麻烦了。那么能不能在程序刚开始运行的时候,就有一些初始化的数据呢?
此时我们就可以通过静态代码块去完成。
// 由于静态中只能用静态,因此集合也需要使用 static 修饰
static ArrayList<User> list = new ArrayList<>();
static {
// 添加一些用户信息
list.add(new User("zhangsan", "1234qwer", "1322323", "234739817493749036"));
list.add(new User("lisi", "1234qwer", "1322323", "234739817493749036"));
...
}
4)解决问题
此时有的同学会说:这些功能我不使用 静态代码块
好像也能做到,如下代码。
public class App2 {
public static void main(String[] args) {
ArrayList<User> list = new ArrayList<>();
list.add(new User("zhangsan", "1234qwer", "1322323", "234739817493749036"));
list.add(new User("lisi", "1234qwer", "1322323", "234739817493749036"));
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("欢迎来到学生管理系统");
System.out.println("请选择操作:1登录 2注册 3忘记密码");
String choose = sc.next();
switch (choose) {
case "1" -> login(list);
case "2" -> register(list);
case "3" -> forgetPassword(list);
case "4" -> {
System.out.println("谢谢使用,再见");
System.exit(0);
}
default -> System.out.println("没有这个选项");
}
}
}
}
这种写法确实也可以实现数据初始化,但是有弊端,由于上面这样写是将代码写在方法中的,一旦是方法,就有可能会被别人反复的去调用,那么这个集合在内存中可能就会有多个,这些用户信息也可能会反复的进行添加。
我们可以来做一个测试。首先我们需要知道:main方法
其实也是可以被我们手动调用的。
public class App2 {
public static void main(String[] args) {
// 例如这里调用多次上面 App2 中的 main方法。这就是这种做法的弊端
App2.main(null);
App2.main(null);
App2.main(null);
App2.main(null);
App2.main(null);
}
}
因此,如果我们以后想要做一些 数据初始化
,并且这些数据初始化
的代码你只想执行一次,就将它写在静态代码块中就行了。
四、总结
1、代码块的分类
写在方法中的 局部代码块
写在成员位置(方法外,类里面)的 构造代码块
使用 static 修饰的 静态代码块
2、局部代码块的作用
提前结束变量的生命周期(已淘汰,了解即可)
3、构造代码块的作用
抽取构造方法中的重复代码(不够灵活,了解即可)
4、静态代码块的作用
数据的初始化(重点)