Java this与super关键字

this关键字

this 关键字有很多种用法,其中最常用的一个是,它可以作为引用变量,指向当前对象。除此之外, this 关键字还可以完成以下工作。

  • 调用当前类的方法;
  • this() 可以调用当前类的构造方法;
  • this 可以作为参数在方法中传递;
  • this 可以作为参数在构造方法中传递;
  • this 可以作为方法的返回值,返回当前类的对象。

01、指向当前对象

public class WithoutThisStudent {
    String name;
    int age;

    WithoutThisStudent(String name, int age) {
        name = name;
        age = age;
    }

    void out() {
        System.out.println(name+" " + age);
    }

    public static void main(String[] args) {
        WithoutThisStudent s1 = new WithoutThisStudent("Hanserwei", 18);
        WithoutThisStudent s2 = new WithoutThisStudent("Hanserwei", 16);

        s1.out();
        s2.out();
    }
}

输出结果:

null 0
null 0

在上面的例子中,构造方法的参数名和实例变量名相同,由于没有使用 this 关键字,所以无法为实例变量赋值。从结果中可以看得出来,尽管创建对象的时候传递了参数,但实例变量并没有赋值。这是因为如果构造方法中没有使用 this 关键字的话,name 和 age 指向的并不是实例变量而是参数本身。那怎么解决这个问题呢?来看如下的代码:

public class WithThisStudent {
    String name;
    int age;

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

    void out() {
        System.out.println(name+" " + age);
    }

    public static void main(String[] args) {
        WithThisStudent s1 = new WithThisStudent("Hanserwei", 18);
        WithThisStudent s2 = new WithThisStudent("Hanserwei", 16);

        s1.out();
        s2.out();
    }
}

输出结果是:

Hanserwei 18
Hanserwei 16

02、调用当前类的方法

看如下代码

public class InvokeCurrentClassMethod {
    void method1() {}
    void method2() {
        method1();
    }

    public static void main(String[] args) {
        new InvokeCurrentClassMethod().method1();
    }
}

反编译一下InvokeCurrentClassMethod.class文件。得到如下结果:

public class InvokeCurrentClassMethod {
    public InvokeCurrentClassMethod() {
    }

    void method1() {
    }

    void method2() {
        this.method1();
    }

    public static void main(String[] args) {
        (new InvokeCurrentClassMethod()).method1();
    }
}

我们可以在一个类中使用 this 关键字来调用另外一个方法,如果没有使用的话,编译器会自动帮我们加上。在源代码中,method2() 在调用 method1() 的时候并没有使用 this 关键字,但通过反编译后的字节码可以看得到。

03、调用当前类的构造方法

看如下的代码:

public class InvokeConstrutor {
    InvokeConstrutor() {
        System.out.println("hello");
    }

    InvokeConstrutor(int count) {
        this();
        System.out.println(count);
    }

    public static void main(String[] args) {
        InvokeConstrutor invokeConstrutor = new InvokeConstrutor(10);
    }
}

在有参构造方法 InvokeConstrutor(int count) 中,使用了 this() 来调用无参构造方法 InvokeConstrutor()this() 可用于调用当前类的构造方法——构造方法可以重用了。

来看一下输出结果

hello
10

也可以在无参构造方法中使用 this() 并传递参数来调用有参构造方法。如如下代码

public class InvokeParamConstrutor {
    InvokeParamConstrutor() {
        this(10);
        System.out.println("hello");
    }

    InvokeParamConstrutor(int count) {
        System.out.println(count);
    }

    public static void main(String[] args) {
        InvokeParamConstrutor invokeConstrutor = new InvokeParamConstrutor();
    }
}

输出结果是:

10
hello

不过,需要注意的是,this() 必须放在构造方法的第一行,否则就报错了。

image-20250722115951684

04、作为参数在方法中传递

先看如下代码:

public class ThisAsParam {
    void method1(ThisAsParam p) {
        System.out.println(p);
    }

    void method2() {
        method1(this);
    }

    public static void main(String[] args) {
        ThisAsParam thisAsParam = new ThisAsParam();
        System.out.println(thisAsParam);
        thisAsParam.method2();
    }
}

this 关键字可以作为参数在方法中传递,此时,它指向的是当前类的对象。

输出结果:

com.itwanger.twentyseven.ThisAsParam@77459877
com.itwanger.twentyseven.ThisAsParam@77459877

method2() 调用了 method1(),并传递了参数 this,method1() 中打印了当前对象的字符串。 main() 方法中打印了 thisAsParam 对象的字符串。从输出结果中可以看得出来,两者是同一个对象。

05、作为参数在构造方法中传递

还是先看代码:

public class ThisAsConstrutorParam {
    int count = 10;

    ThisAsConstrutorParam() {
        Data data = new Data(this);
        data.out();
    }

    public static void main(String[] args) {
        new ThisAsConstrutorParam();
    }
}

class Data {
    ThisAsConstrutorParam param;
    Data(ThisAsConstrutorParam param) {
        this.param = param;
    }

    void out() {
        System.out.println(param.count);
    }
}

在构造方法 ThisAsConstrutorParam() 中,我们使用 this 关键字作为参数传递给了 Data 对象,它其实指向的就是 new ThisAsConstrutorParam() 这个对象。

来看一下输出结果

10

06、作为方法返回值

看一下如下的代码

public class ThisAsMethodResult {
    ThisAsMethodResult getThisAsMethodResult() {
        return this;
    }
    
    void out() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        new ThisAsMethodResult().getThisAsMethodResult().out();
    }
}

getThisAsMethodResult() 方法返回了 this 关键字,指向的就是 new ThisAsMethodResult() 这个对象,所以可以紧接着调用 out() 方法——达到了链式调用的目的,这也是 this 关键字非常经典的一种用法。链式调用的形式在 JavaScript 代码更加常见。

hello

super关键字

super 关键字的用法主要有三种。

  • 指向父类对象;
  • 调用父类的方法;
  • super() 可以调用父类的构造方法。

其实和 this 有些相似,只不过用意不大相同。每当创建一个子类对象的时候,也会隐式的创建父类对象,由 super 关键字引用。如果父类和子类拥有同样名称的字段,super 关键字可以用来访问父类的同名字段。

看如下的代码:

public class ReferParentField {
    public static void main(String[] args) {
        new Dog().printColor();
    }
}

class Animal {
    String color = "白色";
}

class Dog extends Animal {
    String color = "黑色";

    void printColor() {
        System.out.println(color);
        System.out.println(super.color);
    }
}

父类 Animal 中有一个名为 color 的字段,子类 Dog 中也有一个名为 color 的字段,子类的 printColor() 方法中,通过 super 关键字可以访问父类的 color。

输出结果

黑色
白色

当子类和父类的方法名相同时,可以使用 super 关键字来调用父类的方法。换句话说,super 关键字可以用于方法重写时访问到父类的方法。

public class ReferParentMethod {
    public static void main(String[] args) {
        new Dog().work();
    }
}

class Animal {
    void eat() {
        System.out.println("吃...");
    }
}

class Dog extends Animal {
    @Override
    void eat() {
        System.out.println("吃...");
    }

    void bark() {
        System.out.println("汪汪汪...");
    }

    void work() {
        super.eat();
        bark();
    }
}

父类 Animal 和子类 Dog 中都有一个名为 eat() 的方法,通过 super.eat() 可以访问到父类的 eat() 方法。

再看如下代码:

public class ReferParentConstructor {
    public static void main(String[] args) {
        new Dog();
    }
}

class Animal {
    Animal(){
        System.out.println("动物来了");
    }
}

class Dog extends Animal {
    Dog() {
        super();
        System.out.println("狗狗来了");
    }
}

输出:

动物来了
狗狗来了

当然了,在默认情况下,super() 是可以省略的,编译器会主动去调用父类的构造方法。也就是说,子类即使不使用 super() 主动调用父类的构造方法,父类的构造方法仍然会先执行。

public class ReferParentConstructor {
    public static void main(String[] args) {
        new Dog();
    }
}

class Animal {
    Animal(){
        System.out.println("动物来了");
    }
}

class Dog extends Animal {
    Dog() {
        System.out.println("狗狗来了");
    }
}

输出结果和之前一样。

动物来了
狗狗来了

super() 也可以用来调用父类的有参构造方法,这样可以提高代码的可重用性。

class Person {
    int id;
    String name;

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

class Emp extends Person {
    float salary;

    Emp(int id, String name, float salary) {
        super(id, name);
        this.salary = salary;
    }

    void display() {
        System.out.println(id + " " + name + " " + salary);
    }
}

public class CallParentParamConstrutor {
    public static void main(String[] args) {
        new Emp(1, "Hanserwei", 20000f).display();
    }
}

Emp 类继承了 Person 类,也就继承了 id 和 name 字段,当在 Emp 中新增了 salary 字段后,构造方法中就可以使用 super(id, name) 来调用父类的有参构造方法。

输出结果

1 Hanserwei 20000.0