画师:竹取工坊画师:竹取工坊
大佬们好!我是Mem0rin!现在正在准备自学转码。
如果我的文章对你有帮助的话,欢迎关注我的主页Mem0rin,欢迎互三,一起进步!

一、子类构造方法

考虑下面的代码:

public class Base {
    int b;
    public Base(int b) {
        this.b = b;
        System.out.println("Base构造方法运行中。");
    }
}
public class Derived extends Base{
    private int a;
    public Derived(int a) {
        //需要在第一行加上父类的构造语句,比如:super(1),否则无法运行
        //如果参数列表没有b不报错的原因是,子类的构造方法会默认有super(),因此如果参数列表为空编译器会自己构造。
        this.a = a;
        System.out.println("Derived构造方法运行中。");
    }
}
public class Test {
    public static void main(String[] args) {
        Derived d = new Derived(1);
    }

}

尝试运行会发现程序会报错。
这是因为子类在运行构造函数之前要先完成父类的构造,也就是说在Derived的构造方法内部,第一行必须是super的构造方法,比如上面的代码第一行就必须是super(1)

二、代码块执行顺序:

1.回顾

现在我们先回顾一下在一个类的初始化中,代码块的执行顺序。

public class TestDemo {
    public static String name = "Tim";
    public int age;
    public double weight;


    // 实例代码块1
    {
        this.age = 18;
        this.weight = 60;
        System.out.println("实例代码块1运行中");
    }

    // 静态代码块1
    static {
        name = "Timi";
        System.out.println("静态代码块1运行中");
    }


    // 构造方法
    public TestDemo(int age, double weight) {
        System.out.println("构造方法开始执行");
        this.age = age;
        this.weight = weight;
        System.out.println("构造方法结束执行");
    }

    // 实例代码块2
    {
        System.out.println("实例代码块2运行中");
    }

    // 静态代码块2
    static {
        System.out.println("静态代码块2运行中");
    }

    public void show() {
    		 //普通代码块
        {
            System.out.println("普通代码块(局部代码块)运行中");
        }
        System.out.println("姓名:" + name + " 年龄:" + age + " 体重:" + weight);
    }
}

2.继承下的代码块运行顺序

在这段代码中,类初始化的时候,代码块的执行顺序是:静态代码块 -> 实例代码块 -> 构造函数。
那么在子类和父类的初始化中,代码块的执行顺序是怎么样的呢?

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }
    {
        System.out.println("Person:实例代码块执行");
    }
    static {
        System.out.println("Person:静态代码块执行");
    }
}
class Student extends Person{
    public Student(String name,int age) {
        super(name,age);
        System.out.println("Student:构造方法执行");
    }
    {
        System.out.println("Student:实例代码块执行");
    }
    static {
        System.out.println("Student:静态代码块执行");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("张三",19);
        System.out.println("===========================");
        Student student2 = new Student("李四",20);

    }
}

在如上所示的代码中,结果为:

Person:静态代码块执行
Student:静态代码块执行
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行
===========================
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行

在这里代码块的运行顺序是:
(父类静态代码块 -> 子类静态代码块) -> 父类的实例和构造 -> 子类的实例和构造。
这样的原因是因为我们知道,静态代码块是在程序加载类的时候就运行了的,并且先有父后有子,因此最早运行的分别是父类和子类的静态代码块。
之后调用构造函数的时候,因为调用子类构造函数的第一步是运行父类的构造函数super,因此,要先运行父类的实例和构造,然后运行子类的实例和构造。
在第二次运行的时候,由于静态代码块只会运行一次,因此只会先后运行父类的实例和构造以及子类的实例和构造。

final关键词

final关键词修饰成员变量的时候,变量视为常量,不能被修改。
修饰类的时候,类不能被继承。
修饰方法的时候,方法不能被重写。

final class Person {
    
}

class Student extends Person {//final修饰的类不能被继承
    
}



public class Test {
    public static void main(String[] args) {
        final int a = 1;
        a = 2;//final修饰的成员变量视为常量,不能被更改。
    }
}

下一章讲多态。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐