问题

this 和 super 关键字的作用是什么?

答案

核心概念

  • this:代表当前对象的引用,指向调用该方法的对象实例
  • super:代表父类对象的引用,用于访问父类的成员和构造方法

this 关键字的三大用法

1. 访问当前对象的成员变量

用于区分成员变量局部变量(特别是参数名相同时):

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        // this.name 是成员变量,name 是参数
        this.name = name;  // 将参数赋值给成员变量
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;  // 区分成员变量和参数
    }

    public void printInfo() {
        // 访问当前对象的成员变量(this 可省略)
        System.out.println("姓名:" + this.name);
        System.out.println("年龄:" + age);  // 等价于 this.age
    }
}

2. 调用当前对象的其他方法

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int addAndPrint(int a, int b) {
        int result = this.add(a, b);  // 调用当前对象的 add 方法
        System.out.println("结果:" + result);
        return result;
    }

    public void process() {
        this.addAndPrint(10, 20);  // this 可省略
        addAndPrint(30, 40);       // 等价写法
    }
}

3. 调用当前类的其他构造方法(构造器重载)

public class Student {
    private String name;
    private int age;
    private String school;

    // 构造方法 1
    public Student() {
        this("未知", 0);  // 调用构造方法 2
    }

    // 构造方法 2
    public Student(String name, int age) {
        this(name, age, "默认学校");  // 调用构造方法 3
    }

    // 构造方法 3
    public Student(String name, int age, String school) {
        this.name = name;
        this.age = age;
        this.school = school;
    }
}

// 使用示例
Student s1 = new Student();                    // 调用构造方法 1
Student s2 = new Student("张三", 20);          // 调用构造方法 2
Student s3 = new Student("李四", 22, "清华");  // 调用构造方法 3

重要规则

  • this() 必须是构造方法的第一条语句
  • 不能同时调用 this()super()(都要求第一条语句)
  • 避免构造方法之间的循环调用

super 关键字的三大用法

1. 访问父类的成员变量

当子类和父类有同名变量时,用 super 访问父类的变量:

class Parent {
    protected String name = "父类";
}

class Child extends Parent {
    private String name = "子类";

    public void printNames() {
        System.out.println("子类 name:" + this.name);   // 输出:子类
        System.out.println("父类 name:" + super.name);  // 输出:父类
    }
}

2. 调用父类的方法

当子类重写了父类方法,但仍需调用父类的原始实现时:

class Animal {
    public void eat() {
        System.out.println("动物在吃东西");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        super.eat();  // 先调用父类的 eat 方法
        System.out.println("狗在吃骨头");  // 再执行子类的逻辑
    }
}

// 输出:
// 动物在吃东西
// 狗在吃骨头

实际应用场景

class BaseService {
    public void save(Object obj) {
        System.out.println("执行通用保存逻辑");
        // 数据校验、日志记录等
    }
}

class UserService extends BaseService {
    @Override
    public void save(Object obj) {
        super.save(obj);  // 复用父类的通用逻辑
        System.out.println("执行用户特定的保存逻辑");
        // 用户相关的特殊处理
    }
}

3. 调用父类的构造方法

子类构造方法中必须调用父类构造方法(显式或隐式):

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Employee extends Person {
    private String company;

    public Employee(String name, int age, String company) {
        super(name, age);  // 显式调用父类构造方法
        this.company = company;
    }
}

重要规则

  • super() 必须是构造方法的第一条语句
  • 如果不显式调用 super(),编译器会自动插入 super()(调用父类无参构造)
  • 如果父类没有无参构造,子类必须显式调用父类的有参构造
class Parent {
    // 没有无参构造方法
    public Parent(String name) {
        System.out.println("父类构造:" + name);
    }
}

class Child extends Parent {
    public Child() {
        // ❌ 编译错误:父类没有无参构造
        // 编译器无法自动插入 super()
    }

    public Child(String name) {
        super(name);  // ✅ 必须显式调用父类有参构造
    }
}

this 和 super 的对比

特性 this super
指向 当前对象 父类对象
访问变量 当前类的成员变量 父类的成员变量
调用方法 当前类的方法 父类的方法
调用构造 当前类的其他构造方法 父类的构造方法
使用位置 构造方法第一行(调用构造时) 构造方法第一行(调用构造时)
能否共存 不能同时使用 this()super() 不能同时使用 this()super()

综合示例

class Vehicle {
    protected String brand;

    public Vehicle(String brand) {
        this.brand = brand;
        System.out.println("Vehicle 构造方法");
    }

    public void start() {
        System.out.println(brand + " 启动");
    }
}

class Car extends Vehicle {
    private String model;

    // 构造方法 1
    public Car(String brand, String model) {
        super(brand);  // 调用父类构造方法
        this.model = model;
        System.out.println("Car 构造方法");
    }

    // 构造方法 2
    public Car(String brand) {
        this(brand, "默认型号");  // 调用当前类的其他构造方法
    }

    @Override
    public void start() {
        super.start();  // 调用父类的 start 方法
        System.out.println(this.model + " 型号启动");  // 访问当前对象的成员
    }

    public void showInfo() {
        System.out.println("品牌:" + super.brand);  // 访问父类变量
        System.out.println("型号:" + this.model);   // 访问当前类变量
    }
}

// 测试
public class Test {
    public static void main(String[] args) {
        Car car = new Car("特斯拉", "Model 3");
        car.start();
        car.showInfo();
    }
}

// 输出:
// Vehicle 构造方法
// Car 构造方法
// 特斯拉 启动
// Model 3 型号启动
// 品牌:特斯拉
// 型号:Model 3

常见陷阱

陷阱 1:构造方法调用顺序错误

class Parent {
    public Parent(String name) {
        System.out.println("父类构造");
    }
}

class Child extends Parent {
    public Child() {
        System.out.println("子类逻辑");  // ❌ 错误:super() 必须是第一条语句
        super("test");
    }
}

陷阱 2:静态方法中使用 this/super

public class Example {
    public static void staticMethod() {
        // ❌ 错误:静态方法中不能使用 this 或 super
        this.instanceMethod();  // 编译错误
        super.parentMethod();   // 编译错误
    }
}

陷阱 3:构造方法循环调用

public class BadExample {
    public BadExample() {
        this(10);  // 调用构造方法 2
    }

    public BadExample(int x) {
        this();  // ❌ 错误:循环调用,导致栈溢出
    }
}

实际应用场景

场景 1:Builder 模式

public class User {
    private String name;
    private int age;
    private String email;

    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
    }

    public static class Builder {
        private String name;
        private int age;
        private String email;

        public Builder name(String name) {
            this.name = name;
            return this;  // 返回当前对象,支持链式调用
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

// 使用
User user = new User.Builder()
    .name("张三")
    .age(25)
    .email("zhangsan@example.com")
    .build();

场景 2:模板方法模式

abstract class AbstractProcessor {
    // 模板方法
    public final void process() {
        preProcess();
        doProcess();
        postProcess();
    }

    protected void preProcess() {
        System.out.println("通用前置处理");
    }

    protected abstract void doProcess();

    protected void postProcess() {
        System.out.println("通用后置处理");
    }
}

class ConcreteProcessor extends AbstractProcessor {
    @Override
    protected void doProcess() {
        super.preProcess();  // 可选:调用父类方法
        System.out.println("具体业务处理");
    }
}

面试答题要点

  1. this 的作用
    • 引用当前对象
    • 区分成员变量和局部变量
    • 调用当前类的其他构造方法(this()
  2. super 的作用
    • 引用父类对象
    • 访问父类被隐藏的成员变量
    • 调用父类被重写的方法
    • 调用父类构造方法(super()
  3. 关键规则
    • this()super() 必须是构造方法的第一条语句
    • 不能同时使用 this()super()
    • 静态方法中不能使用 this 和 super
  4. 实际应用
    • 构造方法重载(this)
    • 继承中复用父类逻辑(super)
    • Builder 模式(this)
    • 模板方法模式(super)

记忆技巧

  • this:我自己(当前对象)
  • super:我爸爸(父类对象)
  • 构造调用:必须第一个打招呼(第一条语句)
  • 不能同时:不能同时叫自己和爸爸(只能选一个)