首页 > 编程笔记

Java super用法详解

在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。

需要注意的是,在子类中重写的方法需要和父类中被重写的方法具有相同的方法名、参数列表及返回值类型。

【实例】重写继承的方法。
class Human {
    // 定义人类吃东西的方法
    void eat() {
        System.out.println("人类吃东西");
    }
}

// 定义 Sportsman 类继承 Human 类
class Sportsman extends Human {
    // 重写 eat() 方法
    void eat() {
        System.out.println("运动员吃健身餐");
    }
}

// 定义测试类
public class Test {
    public static void main(String[] args) {
        // 创建一个 Sportsman 实例对象
        Sportsman s = new Sportsman();
        // 调用 s 对象重写的 eat() 方法
        s.eat();
    }
}
运行结果为:

运动员吃健身餐

由实例的运行结果可知,当子类重写父类的方法后,子类对象将无法访问父类被重写的方法。如果在子类中访问父类的被隐藏的属性和重写的方法,那么需要使用关键字 super。

关键字 super 可用于访问父类中的构造方法、属性和方法。下面介绍关键字 super 的具体用法:
//访问父类的构造方法
super(参数列表)
//访问父类的属性
super.属性
//访问父类的方法
super.方法名()
下面通过一个实例来介绍 super() 方法的应用。
/**
* 定义父类 Human2
*/
class Human2 {
    // 使用构造方法初始化属性 meal
    public String meal;
   
    Human2(String meal) {
        this.meal = meal;
        System.out.println("人类吃" + meal);
    }
}

/**
* 定义子类 Sportsman2
*/
class Sportsman2 extends Human2 {
    // 子类的构造方法通过关键字 super 调用父类的构造方法,并传递参数
    // 此处必须调用,否则编译报错
    Sportsman2(String meal) {
        super(meal);
        System.out.println("运动员吃东西");
    }
}

public class Test {
    public static void main(String[] args) {
        // 子类实例化,调用父类和子类的构造方法
        Sportsman2 b = new Sportsman2("早餐");
    }
}
运行结果为:

人类吃早餐
运动员吃东西

在子类 Sportsman2 实例化对象时,必然调用该子类的构造方法,这样就可以通过子类的构造方法调用父类的构造方法,并对属性 meal 进行初始化。

下面通过一个实例来介绍使用关键字 super 访问父类的属性和方法。
class Human3 {
    String name = "人类";
    // 定义人类吃东西的方法
    void eat() {
        System.out.println("人类吃东西");
    }
}

// 定义 Sportsman3 类继承 Human3 类
class Sportsman3 extends Human3 {
    String name = "一个人"; // 子类可以有自己的属性,这里覆盖了父类的属性
    // 重写 eat() 方法
    void eat() {
        // 访问父类的成员方法
        super.eat();
        // 这里可以添加运动员特有的吃东西行为
    }
   
    // 定义打印 name 的方法
    void printName() {
        // 访问父类的成员变量
        System.out.println("name = " + super.name);
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建一个 Sportsman3 实例对象
        Sportsman3 x = new Sportsman3();
        // 调用 x 对象重写的 eat() 方法
        x.eat();
        // 调用 x 对象的 printName() 方法
        x.printName();
    }
}
运行结果为:

人类吃东西
name = 人类

实例的代码中定义了一个继承 Human3 类的 Sportsman3 类,并重写了 Human3 类的 eat() 方法。在子类 Sportsman3 的 eat() 方法中使用 super.eat() 调用父类被重写的方法,在 printName() 方法中使用 super.name 访问父类的成员变量。

由运行结果可知,子类通过关键字 super 可以成功地访问父类的成员变量和成员方法。

实际上,当父类中有无参构造方法时,编译器默认在子类的所有构造方法中的第一行自动添加 super() 方法,隐式调用父类的无参构造方法。

【实例】隐式调用父类的构造方法。
/**
* 定义父类 Human4
*/
class Human4 {
    public int a;

    // 定义无参构造方法
    Human4() {
        System.out.println("人类吃东西");
    }

    // 构造方法,初始化 a
    Human4(int eat) {
        this.a = eat;
    }
}

/**
* 定义 Sportsman4 类
*/
class Sportsman4 extends Human4 {
    // 可以不写父类的构造方法的调用,默认隐式调用父类的无参构造方法
    Sportsman4() {
        System.out.println("运动员吃东西");
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建 Sportsman4 的实例
        Sportsman4 b = new Sportsman4();
    }
}
运行结果为:

人类吃东西
运动员吃东西

由运行结果可知,Human4 类的构造方法被调用。这是因为 Sportsman4 类的构造方法中隐含了对父类构造方法的调用,如下所示:
class Sportsman4 extends Human4 {
    Sportsman4() {
        super(); // 隐含了这行代码
        System.out.println("运动员吃东西");
    }
}
假如 Sportsman4 类的构造方法没有参数,并且只调用 Human4 类的无参构造方法,正如上面例子所示,那么子类的构造方法也可以省略不写,如下所示:
class Sportsman4 extends Human4 {
}
如果执行上面的代码,实际上就是编译器默认添加了 Sportsman4 类的无参构造方法,并且在其中调用了 Human4 类的无参构造方法。但是,如果父类没有无参构造方法,那么在子类中必须使用关键字 super 显式调用父类的有参构造方法。这种隐式调用的方式使用得比较多,为了方便继承,在设计父类时都会提供一个无参构造方法。

构造方法的规则包括以下几点:
  1. 如果一个类中没有声明任何构造方法,那么编译器默认为该类添加一个无参的、空方法体的构造方法;
  2. 如果一个类中已声明有参构造方法,那么编译器不再默认添加无参构造方法;
  3. 如果一个构造方法的第一行没有显式声明语句 this(); 或 super();,那么编译器会自动添加 super(); 语句,隐式调用父类的无参构造方法。

推荐阅读