理论知识看目录,代码详解看内容
文章目录
- 一、代码块入门
- 二、代码块详解(超祥)
一、代码块入门
代码块又称为初始化块,属于类中的成员(类的一部分),类似于方法,可以将逻辑语句封装在方法体中,使用{}
包围。
和方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或者类显式调用,而是加载类或创建对象时隐式调用。
基本语法:
[修饰符] {
// 代码
};
代码块的作用:
相当于是另一种形式的构造器,可以做初始化操作。例如:如果多个构造器中有重复的语句,可以抽取到代码块中,提高代码的重用性。
代码块的调用顺序优先于构造器
创建一个有代码块的类:
public class Cook {
// 代码块
{
System.out.println("买菜");
System.out.println("洗菜");
System.out.println("切菜");
}
public Cook() {
System.out.println("炒白菜");
}
public Cook(String name) {
System.out.println("炒" + name);
}
}
执行测试方法可以看到以下结果:
可见,代码块的执行顺序是在构造方法之前
二、代码块详解(超祥)
1. static代码块也叫静态代码块,作用就是对类进行初始化,而它随着类的加载而执行,并且只会执行一次,如果是普通的代码块,每创建一个对象就执行。
创建一个类
class AAA {
// 静态代码块
static {
System.out.println("AAA的静态代码块执行了...");
}
// 普通代码块
{
System.out.println("AAA的普通代码块执行了...");
}
}
执行以下代码:
public static void main(String[] args) {
AA aa = new AA();
AA aa2 = new AA();
}
可以看到,类只会被加载一次,所以静态代码块也只会执行一次,而普通代码块会随着类实例的创建而执行
2. 类是么时候被加载?
① 创建对象实例时(new)
class AA {
// 静态代码块
static {
System.out.println("AA的静态代码块执行了...");
}
}
执行下面代码,可以看到如下结果,说明创建对象时类有被加载
public static void main(String[] args) {
// 创建对象实例时(new)
AA aa = new AA();
}
② 创建子类对象实例时,父类也会被加载
创建一个子类BB类,是AA类的子类
class BB extends AA {
// 静态代码块
static {
System.out.println("BB的静态代码块执行了...");
}
}
执行下面代码,可以看到如下结果
public static void main(String[] args) {
// 创建子类对象实例时,父类也会被加载
BB bb = new BB();
}
可以看到父类会先被加载,子类后被加载,继承时有说到过
③ 使用类的静态成员时(静态属性、静态方法)
创建一个带有静态成员的CC类
class CC {
public static int n1 = 100;
// 静态代码块
static {
System.out.println("CC的静态代码块执行了...");
}
}
执行以下代码:
public static void main(String[] args) {
// 使用类的静态成员时(静态属性、静态方法)
System.out.println(CC.n1);
}
可以看到如下结果:
因为在main方法中调用了CC类的静态属性,所以类会被加载
3. 创建一个对象时,在一个类中的调用顺序:
① 静态属性初始化和静态代码块
定义一个A类:
class A {
private static int n1 = getVal();
static {
System.out.println("A类的静态代码块执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
可以看到,静态变量初始化先被执行,因为静态变量初始化调用了getVal
方法,所以该方法会先执行,然后将值赋给n1
,然后代码继续向下执行,会继续执行静态代码块
② 普通属性初始化和普通代码块
增加A的代码:
class A {
{
System.out.println("A类的普通代码块执行了");
}
private static int n1 = getVal();
private int n2 = getVal2();
static {
System.out.println("A类的静态代码块执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
public int getVal2() {
System.out.println("getVal2() 方法执行了");
return 20;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
因为静态时随着类加载的,所以静态的会先执行,而非静态的也是根据顺序执行的。
③ 构造器
在A类上增加一个无参构造器:
class A {
{
System.out.println("A类的普通代码块执行了");
}
private static int n1 = getVal();
private int n2 = getVal2();
static {
System.out.println("A类的静态代码块执行了");
}
public A() {
System.out.println("A类的无参构造器执行了");
}
public static int getVal() {
System.out.println("getVal() 方法执行了");
return 10;
}
public int getVal2() {
System.out.println("getVal2() 方法执行了");
return 20;
}
}
执行以下代码:
public static void main(String[] args) {
A a = new A();
}
可以看到构造器时最后执行的
4. 构造器最前面隐含了super()和调用普通代码块
class 类名 extends 父类名 {
public 类名() { // 构造器
// 1. 这里隐藏了调用父类构造的super();
super(); // 继承有提到
// 2. 这里隐藏了调用普通代码块的语句
// 3. 我们自己写的构造器逻辑
// 如果有手动调用父类构造,则前面的super()方法将会被替代
...
}
}
所以到目前为止,一个类的执行顺序应该是:
因为在构造器中先有super再执行普通代码块的,所以会一直追溯到Object类,到Object类时没有父类了,就开始执行Object的普通代码块(虽然没有),然后执行Object类的构造方法,执行后会往下找下一级的普通代码块,然后是构造方法,以此类推,所以普通代码块的执行顺序是优先于构造方法的。
5. 有继承关系的静态代码块、静态属性初始化、普通代码块、普通代码初始化、构造方法的调用顺序:
① 父类静态代码块和静态属性初始化(优先级按代码顺序执行)
② 子类静态代码块和静态属性初始化(优先级按代码顺序执行)
③ 父类普通代码块和普通属性初始化(优先级按代码顺序执行)
如果父类属性初始化时调用了方法,并且子类重写了该方法,执行的是子类的方法
④ 父类构造方法
⑤ 子类普通代码块和普通属性初始化(优先级按代码顺序执行)
⑥ 子类构造方法
创建测试方法,看下执行什么:
package object05;
public class Test001 {
public static void main(String[] args) {
new Zi();
}
}
class Fu {
{
System.out.println("Fu类的普通代码块");
}
static {
System.out.println("Fu类的静态代码块");
}
private static int n1 = getInt1();
private int n2 = getInt2();
public Fu() {
System.out.println("Fu类的构造方法");
}
public static int getInt1() {
System.out.println("Fu类的初始化静态成员的方法");
return 10;
}
public int getInt2() {
System.out.println("Fu类的初始化普通成员的方法");
return 10;
}
}
class Zi extends Fu {
private static int n3 = getInt3();
private int n4 = getInt4();
{
System.out.println("Zi类的普通代码块");
}
static {
System.out.println("Zi类的静态代码块");
}
public Zi() {
System.out.println("Zi类的构造方法");
}
public static int getInt3() {
System.out.println("Zi类的初始化静态成员的方法");
return 10;
}
public int getInt4() {
System.out.println("Zi类的初始化普通成员的方法");
return 10;
}
}
因为父类会先被加载并且父类的静态代码块在上面,所以先执行父类的静态代码块再执行父类的初始化静态成员的方法。
然后加载子类,子类的静态成员在静态代码块的上面,所以会先被初始化,然后执行子类的静态代码块。
当类加载完后,会先执行父类的普通代码块和普通成员初始化,也是根据代码顺序执行的。
再然后执行父类的构造方法。
父类执行完毕后,就轮到子类了,也是根据顺序执行普通代码块和普通成员初始化,最后执行子类构造。
6. 静态代码块只能对用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
class C {
private int n1 = 10;
private static int n2 = 20;
{
System.out.println(n1);
System.out.println(n2);
}
static {
// System.out.println(n1); // 错误
System.out.println(n2);
}
}