本篇主要学习内容及目标:
- ⭐️ 了解代码块的基本概念与用途;
- ⭐️ 掌握代码块的使用方法;
- ⭐️ 掌握静态与非静态代码块的区别;
- ⭐️ 理解并掌握在一个类中的代码块与成员的调用机制;
- ⭐️ 理解并掌握在多个类中的代码块与成员的调用机制;
文章目录
- 1 代码块简介
- 2 代码块有何用处?
- 3 代码块使用细节(重点!)
1 代码块简介
✈️ 代码块又称初始化块,属于类中的成员(属于类中的一部分),类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来。但是,它和方法不同,它没有方法名,没有返回值也没有参数,只有方法体。最重要的一点是,代码块不需要通过对象或者类显示调用,而是加载类时,或者创建对象时隐式调用。
代码块的基本语法如下:
【修饰符】{ 方法体 }; // 分号可以不写
说明:修饰符可不写,写上只可为 static,前者为普通代码块,后者为静态代码块。 分号可写可不写,依据编程者的使用习惯而定。
2 代码块有何用处?
相信不少小伙伴和博主一样,有这样的疑惑:既然代码块没有返回值没有参数,难道方法不够用吗,为何偏偏多此一举?
我们可以试想一个这样的场景,就拿我们熟悉的游戏来说——王者荣耀。当你选定英雄后,系统会进入游戏加载动画,最后开局播报语音:“欢迎来到王者荣耀,全军出击!”我们用最朴素的方法来模拟一下王者荣耀,来看代码:
public class KingGame { public static void main(String[] args) { Hero h1 = new Hero("伽罗", "射手"); Hero h2 = new Hero("花木兰", "战士"); Hero h3 = new Hero("李白", "刺客"); (); (); (); } } class Hero{ private String name; // 英雄姓名 private String job; // 英雄职位 public Hero(String name, String job) { = name; = job; } // 开始游戏 public void startGame(){ Sy("进入游戏加载动画~~~"); Sy("欢迎来到王者荣耀, 全军出击!"); } }
在代码中,模拟了三个英雄进入游戏的进程,运行结果为:
进入游戏加载动画~~~
欢迎来到王者荣耀, 全军出击!
进入游戏加载动画~~~
欢迎来到王者荣耀, 全军出击!
进入游戏加载动画~~~
欢迎来到王者荣耀, 全军出击!
但是,我们发现,每个英雄进入游戏的时候都是弹出这两句话。还需要我们在主方法去特意调用 startGame() 方法。而代码块,就是可以简化操作,帮助我们进行初始化,即我们不必显式调用方法,就可以完成进入游戏的操作:
public class KingGame { public static void main(String[] args) { new Hero("伽罗", "射手"); new Hero("花木兰", "战士"); new Hero("李白", "刺客"); } } class Hero{ private String name; // 英雄姓名 private String job; // 英雄职位 public Hero(String name, String job) { = name; = job; } // 开始游戏 { Sy("进入游戏加载动画~~~"); Sy("欢迎来到王者荣耀, 全军出击!"); } }
因此,我们对代码块的好处进行总结:
代码块相当于另一种形式的构造器,可以做初始化操作,相当于对构造器的补充;
如果多个构造器都有重复的语句,可以抽取到初始化代码块中, 提高代码的重用性。
3 代码块使用细节(重点!)
⭐️ Star 1: static 代码块也叫静态代码块,作用就是对类进行初始化,而且 随着类的加载而执行,并且只会执行一次;
实现结果:
我是A类的静态代码块!!!
⭐️ Star 2: 对于普通代码块来说,每创建一个对象就会执行一次。 在创建对象实例时,会隐式调用。 如果只是使用类的静态成员时(类名.成员名),则普通代码块不会执行;
实现结果:
我是A类的普通代码块!!!
我是A类的普通代码块!!!
我是A类的普通代码块!!!
实现结果:
静态成员
⭐️ Star 3: 类何时被加载?
创建对象实例时,即 new 时;
创建子类对象实例时,父类也会被加载;
使用类的静态成员时(包括静态属性与静态方法)
⭐️ Star 4: 创建一个对象时,在一个类的调用顺序(这里不涉及继承)
先调用静态代码块和静态属性初始化。静态代码块和静态属性初始化的调用优先级一样,如果 有多个静态代码块和多个静态变量初始化,则按照定义顺序调用。
再调用普通代码块和普通属性的初始化。普通代码块和普通属性初始化调用的优先级一样,如果 有多个普通代码块和多个普通属性初始化,则按照定义顺序调用。
最后调用构造方法。
示例代码:
public class CodeBlock { public static void main(String[] args) { new A(); new A(); } } class A{ // 普通成员 String message = setNormalMessage(); // 静态成员 static String name = setStaticName(); // 普通代码块 { Sy("我是A类的普通代码块!!!"); } // 静态代码块 static { Sy("我是A类的静态代码块!!!"); } // 构造方法 public A(){ Sy("我是A类的构造方法"); } public static String setStaticName(){ Sy("setStaticName()被调用"); return "静态成员"; } public String setNormalMessage(){ Sy("setNormalMessage()被调用"); return "普通成员"; } }
实现结果:
setStaticName()被调用
我是A类的静态代码块!!!
setNormalMessage()被调用
我是A类的普通代码块!!!
我是A类的构造方法
setNormalMessage()被调用
我是A类的普通代码块!!!
我是A类的构造方法
即,在同一类中,先静态成员初始化后普通成员初始化最后调用构造方法!!!需要注意的是,静态的成员只初始化一次!!!
⭐️ Star 5: 创建一个子类对象时,在多个类的调用顺序(满足继承关系):
父类的静态代码块与静态属性(优先级与定义顺序相同);
子类的静态代码块与静态属性(优先级与定义顺序相同);
父类的普通代码块与普通属性(优先级与定义顺序相同);
父类的构造方法;
子类的普通代码块与普通属性(优先级与定义顺序相同);
子类的构造方法。
顺序太复杂,不容易记忆?别担心!我们了解一下构造器的隐含条件即可:
class A{ public A(){ //(1)super(); //(2)调用代码块与成员初始化 //(3)该构造器的执行 }
下面的例子中,我们让 A类 作为父类, B类 作为子类,话不多说,上代码!
public class CodeBlock { public static void main(String[] args) { new B(); new B(); } } class A{ // 静态成员 static String name = setStaticName(); // 普通代码块 { Sy("我是A类的普通代码块!!!"); } // 静态代码块 static { Sy("我是A类的静态代码块!!!"); } // 构造方法 public A(){ Sy("我是A类的构造方法"); } public static String setStaticName(){ Sy("A 类的setStaticName()被调用"); return "静态成员"; } public String setNormalMessage(){ Sy("A 类的setNormalMessage()被调用"); return "普通成员"; } } class B extends A{ // 静态成员 static String name = setStaticName(); // 普通代码块 { Sy("我是B类的普通代码块!!!"); } // 静态代码块 static { Sy("我是B类的静态代码块!!!"); } // 构造方法 public B(){ Sy("我是B类的构造方法"); } public static String setStaticName(){ Sy("B 类的setStaticName()被调用"); return "静态成员"; } public String setNormalMessage(){ Sy("B 类的setNormalMessage()被调用"); return "普通成员"; } }
实现结果:
A 类的setStaticName()被调用
我是A类的静态代码块!!!
B 类的setStaticName()被调用
我是B类的静态代码块!!!
我是A类的普通代码块!!!
我是A类的构造方法
我是B类的普通代码块!!!
我是B类的构造方法
我是A类的普通代码块!!!
我是A类的构造方法
我是B类的普通代码块!!!
我是B类的构造方法
即,在多个类(满足继承关系)中,整体满足先静态成员初始化后普通成员初始化最后调用构造方法的顺序,但是在 每一部分 都满足 先父类后子类的原则! 同时,静态成员只初始化一次!
⭐️ Star 6: 静态代码块只能直接调用静态成员(静态属性和方法),普通代码块可以调用任意成员!
4 代码块练习题*
好啦,是时候检验一下学习成果啦!请阅读以下代码,思考控制台输出结果!
question 1:
注意类先加载后使用哦!
public class Test { public static void main(String[] args) { Sy); Sy); } } class Person{ static int total; static { total = 99; Sy("Person 静态代码块"); } }
Person 静态代码块
99
99
question 2:
注意类之间不是继承关系!但是存在调用!!!
public class Test { AA a1 = new AA("a1初始化"); static AA a2 = new AA("静态a2初始化"); static { Sy("Test 静态代码块执行"); if(a2 == null){ Sy("a2 == null"); } } Test(){ Sy("Test 无参构造器"); } public static void main(String[] args) { new Test(); } } class AA{ public AA() { Sy("AA 无参构造"); } public AA(String s){ Sy(s); } }
静态a2初始化
Test 静态代码块执行
a1初始化
Test 无参构造器
解析: 在该题目中,AA 与 Test并不存在继承关系。在 new Test() 时先进行 Test类 的静态成员初始化(按照静态成员的定义顺序),后进行非静态成员的初始化(普通成员与普通代码块),最后调用 Test 的无参构造器。
总结了很多有关于java面试的资料,希望能够帮助正在学习java的小伙伴。由于资料过多不便发表文章,创作不易,望小伙伴们能够给我一些动力继续创建更好的java类学习资料文章,
请多多支持和关注小作,别忘了点赞+评论+转发。右上角私信我回复【03】即可领取免费学习资料谢谢啦!