代码块
1. 代码块基本概念
- 代码块:又称为初始化块,属于类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,用 {} 包围起来。
- 基本用法:
- 代码块的理解:
(1)相当于另外一种形式的构造器(是对构造器的补充机制),可以做初始化操作;
(2)使用场景,如果多个构造器中都有重复的语句,可以抽取到代码块中,提高代码的复用性,相当于对构造器的重载;
(3)代码块调用的顺序优先于构造器。
二、代码块使用注意事项和细节
- 注意事项:
1. 类什么时候被加载
(1)创建类的对象实例时(new),类会被加载。
(2)存在继承关系,当创建子类对象实例时,其所有上级父类也会被加载;并且父类先被加载,越上级的父类,越先被加载。
(3)当使用类的静态成员时(静态属性,静态方法),类会被加载。
(4)注意,类的加载是在内存的方法区中。
2. 什么是代码块
(1)static 关键字修饰的代码块也叫静态代码块,它的作用就是对类进行初始化;它伴随着类的加载而执行,并且只会执行一次。
(2)没有关键字修饰的代码块也叫普通代码块,它相当于是构造器的补充机制;每当我们创建一个类的对象时,普通代码块就会被调用一次;如果只是直接使用类的静态成员,没有创建对象,它不会被调用;注意,普通代码块的调用与类的加载没有关系。
3. 创建一个对象时,在一个类中各属性和方法被虚拟机调用的顺序
(1)首先是调用静态代码块和进行静态属性初始化 (注意,调用静态代码块和静态属性初始化的优先级一样,若有多个静态代码块和静态属性初始化同时存在,则按他们定义的顺序使用),这一步是在类的加载时进行的。
(2)其次是正式在堆内存中创建一个对象空间,并调用普通代码块和进行普通属性的初始化 (注意,调用普通代码块和普通属性初始化的优先级一样,若有多个普通代码块和普通属性初始化同时存在,则按他们定义的顺序使用)。
(3)最后是调用类中的构造方法。
(4)补充:对于普通的静态方法,不会被虚拟机自动执行,啥时候被调用,啥时候执行。
- 代码举例说明:
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();// 输出如下:
// (1) A 静态代码块01 (2) getN1被调用...
// (3) A 普通代码块01 (4) getN2被调用...
// (5) A() 构造器被调用
}
}
class A {
// 普通代码块
{
System.out.println("A 普通代码块01");
}
// 普通属性的初始化
private int n2 = getN2();
// 静态代码块
static {
System.out.println("A 静态代码块01");
}
// 静态属性的初始化
private static int n1 = getN1();
// 静态方法
public static int getN1() {
System.out.println("getN1被调用...");
return 100;
}
// 普通方法/非静态方法
public int getN2() {
System.out.println("getN2被调用...");
return 200;
}
// 无参构造器
public A() {
System.out.println("A() 构造器被调用");
}
}
4. 存在继承关系时,调用普通代码块和普通属性初始化的说明
(1)在构造器/构造方法的最前面其实隐含了 super()语句,还有调用普通代码块和普通属性的初始化的语句,最后才是构造器自己的语句。
(2)我们这里所说的普通属性的初始化,指的是属性在默认初始化后进行的,在构造器内的初始化(这一部分的概念是在面对对象基础部分学习的)。
- 代码举例说明:
public class CodeBlockDetail03 {
public static void main(String[] args) {
new BBB();
//(1) AAA的普通代码块 (2) AAA() 构造器被调用
//(3) BBB的普通代码块 (4) BBB() 构造器被调用
}
}
// 父类
class AAA {
// 普通代码块
{
System.out.println("AAA的普通代码块");
}
// 构造器
public AAA() {
//(1) super()
//(2) 调用本类的普通代码块和普通属性的初始化
System.out.println("AAA() 构造器被调用....");
}
}
// 子类
class BBB extends AAA {
// 普通代码块
{
System.out.println("BBB的普通代码块...");
}
public BBB() {
// (1)super()
// (2)调用本类的普通代码块和普通属性的初始化
System.out.println("BBB() 构造器被调用....");
}
}
5. 存在继承关系时,创建一个子类对象,各个方法和属性被虚拟机调用的顺序
- 首先是在类加载时,调用静态代码块和静态属性的初始化。
(1)父类的静态代码块和静态属性(优先级一样,按定义顺序执行)。
(2)子类的静态代码块和静态属性(优先级一样,按定义顺序执行)。- 其次是在堆内存中创建对象空间,调用普通代码块和普通属性的初始化,调用构造方法。
(3)在构造器中调用父类的普通代码块和普通属性的初始化(优先级一样,按定义顺序执行)。
(4)执行父类的构造器其余的方法。
(5)在构造器中调用子类的普通代码块和普通属性的初始化(优先级一样,按定义顺序执行)。
(6)执行子类的构造器其余的方法。
- 代码举例说明:
public class CodeBlockDetail04 {
public static void main(String[] args) {
// 说明
// (1) 进行类的加载
// 1.1 先加载 父类 A02 ;1.2 再加载子类 B02
// (2) 创建对象空间
// 2.1 从子类的构造器开始
new B02();// 创建对象
}
}
//父类
class A02 {
static {
System.out.println("A02的一个静态代码块..");//(2)
}
{
System.out.println("A02的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
//构造器
public A02() {
//隐藏
//super()
//普通代码和普通属性的初始化
System.out.println("A02的构造器");//(7)
}
}
//子类
class B02 extends A02 { //
static {
System.out.println("B02的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
// 构造器
public B02() {
//隐藏了
//super()
//普通代码块和普通属性的初始化
System.out.println("B02的构造器");//(10)
}
}
总结
- 本文是小白博主在学习B站韩顺平老师的Java网课时整理总结的学习笔记,在这里感谢韩顺平老师的网课,如有有兴趣的小伙伴也可以去看看。
- 本文详细解释了代码块的概念与使用,并深入解释了创建一个对象时的底层流程分析,非常非常重要,希望小伙伴们看后能有所收获!
- 最后,如果本文有什么错漏的地方,欢迎大家批评指正!一起加油!!我们下一篇博文见吧!