字節(jié)二面被問到Java 中 this 和 super 的用法?區(qū)別?看完這篇,教你從容應(yīng)對!

Java 中 this 和 super 的用法詳解

前言

這次我們來回顧一下thissuper這兩個(gè)關(guān)鍵字的用法,作為一名Java程序員,我覺得基礎(chǔ)是最重要的,因?yàn)樗鼪Q定了我們的上限,所以我的文章大部分還是以分享Java基礎(chǔ)知識為主,學(xué)好基礎(chǔ),后面的知識我想學(xué)起來就變得簡單。廢話不多說,進(jìn)入正文。

this

this 關(guān)鍵字只能在方法內(nèi)部使用,表示對調(diào)用方法的那個(gè)對象的引用。

其實(shí)簡單來說 this 關(guān)鍵字就是表示當(dāng)前對象,下面我們來具體介紹 this 關(guān)鍵字在Java中的用法。

1、調(diào)用成員變量

在一個(gè)類的方法內(nèi)部,如果我們想調(diào)用其成員變量,不用 this,我們會怎么做?

public class ThisTest {

    private String name = "xiaoming";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        name = name;
    }
}

看上面的代碼,我們在 ThisTest 類中創(chuàng)建了一個(gè) name 屬性,然后創(chuàng)建了一個(gè) setName 方法,注意這個(gè)方法的形參也是 String name,那么我們通過 name = name 這樣賦值,會改變成員變量 name 的屬性嗎?

public static void main(String[] args) {
    ThisTest thisTest = new ThisTest();
    thisTest.setName("xiaoma");
    System.out.println(thisTest.getName());
}

打印結(jié)果是 xiaoming,而不是我們重新設(shè)置的 xiaoma,顯然這種方式是不能在方法內(nèi)部調(diào)用到成員變量的。因?yàn)樾螀⒌拿趾统蓡T變量的名字相同,setName 方法內(nèi)部的 name = name,根據(jù)最近原則,編譯器默認(rèn)是將這兩個(gè) name 屬性都解析為形參 name,從而導(dǎo)致我們設(shè)值操作和成員變量 name 完全沒有關(guān)系,當(dāng)然設(shè)置不了。

解決辦法就是使用 this 關(guān)鍵字。我們將 setName 方法修改如下:

public void setName(String name) {
    this.name = name;
}

在調(diào)用上面的 main 方法進(jìn)行賦值,打印的結(jié)果就是 xiaoma了。

this 表示當(dāng)前對象,也就是調(diào)用該方法的對象,對象.name 肯定就是調(diào)用的成員變量。

2、調(diào)用構(gòu)造方法

構(gòu)造方法是與類同名的一個(gè)方法,構(gòu)造方法沒有返回值,但是也不能用 void 來修飾。在一個(gè)類中,必須存在一個(gè)構(gòu)造方法,如果沒有,編譯器會在編譯的時(shí)候自動為這個(gè)類添加一個(gè)無參構(gòu)造方法。一個(gè)類能夠存在多個(gè)構(gòu)造方法,調(diào)用的時(shí)候根據(jù)參數(shù)來區(qū)分。

public class Student {

    private int age;

    private String name;

    public Student() {
        this("小馬",50);
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println(name + "今年" + age + "歲了");
    }

    public static void main(String[] args) {
        Student student01 = new Student();
        Student student02 = new Student("小軍",45);
    }
}

通過this("小馬",50)來調(diào)用另外一個(gè)構(gòu)造方法 Student(String name, int age)來給成員變量初始化賦值。

輸出結(jié)果:

小馬今年50歲了
小軍今年45歲了

Process finished with exit code 0

注意:通過 this 來調(diào)用構(gòu)造方法,只能將這條代碼放在構(gòu)造函數(shù)的第一行,這是編譯器的規(guī)定,如下所示:放在第二行會報(bào)錯。

image-20210729163733035

3、調(diào)用普通方法

this 表示當(dāng)前對象,那么肯定能夠調(diào)用當(dāng)前類的普通方法。

public Student() {
    this.say();
}

public void say(){
    System.out.println("小馬很會唱歌。");
}

4、返回當(dāng)前對象

public class ThisTest {

    public Object newObject(){
        return this;
    }
}

這表示的意思是誰調(diào)用 newObject() 方法,那么就返回誰的引用。

super

Java 中的 super 關(guān)鍵字則是表示 父類對象的引用。

我們分析這句話父類對象的引用,那說明我們使用的時(shí)候只能在子類中使用,既然是對象的引用,那么我們也可以用來調(diào)用成員屬性以及成員方法,當(dāng)然了,這里的 super 關(guān)鍵字還能夠調(diào)用父類的構(gòu)造方法。

具體有如下幾種用法

1、調(diào)用父類的構(gòu)造方法

Java中的繼承大家都應(yīng)該了解,子類繼承父類,我們是能夠用子類的對象調(diào)用父類的屬性和方法的,我們知道屬性和方法只能夠通過對象調(diào)用,那么我們可以大膽假設(shè)一下:在創(chuàng)建子類對象的同時(shí),也創(chuàng)建了父類的對象,而創(chuàng)建對象是通過調(diào)用構(gòu)造函數(shù)實(shí)現(xiàn)的,那么我們在創(chuàng)建子類對象的時(shí)候,應(yīng)該會調(diào)用父類的構(gòu)造方法。

下面我們看這段代碼:

public class Teacher {

   public Teacher(){
       System.out.println("我是一名人民教師。");
   }
}

class Student extends Teacher {

    public Student(){
        System.out.println("我是一名學(xué)生。");
    }
}

下面我們創(chuàng)建子類的對象:

public static void main(String[] args) {
    Student s = new Student();
}

輸出結(jié)果:

我是一名人民教師。
我是一名學(xué)生。

Process finished with exit code 0

通過打印結(jié)果看到我們在創(chuàng)建子類對象的時(shí)候,首先調(diào)用了父類的構(gòu)造方法,接著調(diào)用子類的構(gòu)造方法,也就是說在創(chuàng)建子類對象的時(shí)候,首先創(chuàng)建了父類對象,與前面我們猜想的一致。

那么問題又來了:是在什么時(shí)候調(diào)用的父類構(gòu)造方法呢?

可以參考Java官方文檔:https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#d5e14278

image-20210729185246748

紅色框內(nèi)的英文翻譯為:如果聲明的類是原始類Object,那么默認(rèn)的構(gòu)造函數(shù)有一個(gè)空的主體。否則,默認(rèn)構(gòu)造函數(shù)只是簡單地調(diào)用沒有參數(shù)的超類構(gòu)造函數(shù)。

也就是說:除了頂級類 Object.class 構(gòu)造函數(shù)沒有調(diào)用父類的構(gòu)造方法,其余的所有類都默認(rèn)在構(gòu)造函數(shù)中調(diào)用了父類的構(gòu)造函數(shù)(沒有顯式聲明父類的子類其父類是 Object)。

那么是通過什么來調(diào)用的呢?我們接著看官方文檔:

image-20210729185503815

上面的意思大概就是:超類構(gòu)造函數(shù)通過 super 關(guān)鍵字調(diào)用,并且是以 super 關(guān)鍵字開頭。

所以上面的 Student類的構(gòu)造方法實(shí)際上應(yīng)該是這樣的:

class Student extends Teacher {

    public Student(){
        super();//子類通過super調(diào)用父類的構(gòu)造方法
        System.out.println("我是一名學(xué)生。");
    }

    public static void main(String[] args) {
        Student s = new Student();
    }
}

子類默認(rèn)是通過 super() 調(diào)用父類的無參構(gòu)造方法,如果父類顯示聲明了一個(gè)有參構(gòu)造方法,而沒有聲明無參構(gòu)造方法,實(shí)例化子類是會報(bào)錯的。

image-20210729185801603

解決辦法就是通過 super 關(guān)鍵字調(diào)用父類的有參構(gòu)造方法:

class Student extends Teacher {

    public Student(){
        super("小馬");
        System.out.println("我是一名學(xué)生。");
    }

    public static void main(String[] args) {
        Student s = new Student();
    }
}

2、調(diào)用父類的成員屬性

public class Teacher {

    public String name = "小馬";

    public Teacher() {
        System.out.println("我是一名人民教師。");
    }
}

class Student extends Teacher {

    public Student() {
        System.out.println("我是一名學(xué)生。");
    }

    public void fatherName() {
        System.out.println("我的父類名字是:" + super.name);//調(diào)用父類的屬性
    }

    public static void main(String[] args) {
        Student student = new Student();
        student.fatherName();
    }
}

輸出結(jié)果:

我是一名人民教師。
我是一名學(xué)生。
我的父類名字是:小馬

Process finished with exit code 0

3、調(diào)用父類的方法

public class Teacher {

    public String name;

    public Teacher() {
        System.out.println("我是一名人民教師。");
    }

    public void setName(String name){
        this.name = name;
    }
}

class Student extends Teacher {

    public Student() {
        super();//調(diào)用父類的構(gòu)造方法
        System.out.println("我是一名學(xué)生。");
    }

    public void fatherName() {
        super.setName("小軍");//調(diào)用父類普通方法
        System.out.println("我的父類名字是:" + super.name);//調(diào)用父類的屬性
    }

    public static void main(String[] args) {
        Student student = new Student();
        student.fatherName();
    }
}

輸出結(jié)果:

我是一名人民教師。
我是一名學(xué)生。
我的父類名字是:小軍

Process finished with exit code 0

4、this 和 super 出現(xiàn)在同一個(gè)構(gòu)造方法中?

假設(shè) super()this() 關(guān)鍵字的前面

首先通過 super() 調(diào)用父類構(gòu)造方法,對父類進(jìn)行一次實(shí)例化。接著調(diào)用 this()this() 方法會調(diào)用子類的構(gòu)造方法,在子類的構(gòu)造方法中又會對父類進(jìn)行一次實(shí)例化。也就是說我們對子類進(jìn)行一次實(shí)例化,對造成對父類進(jìn)行兩次實(shí)例化,所以顯然編譯器是不允許的。

image-20210729191452110

反過來 this()super() 之前也是一樣。而且編譯器有限定 this()super() 這兩個(gè)關(guān)鍵字都只能出現(xiàn)在構(gòu)造方法的第一行,將這兩個(gè)關(guān)鍵字放在一起,總有一個(gè)關(guān)鍵字在第二行,編譯是不能通過的。

image-20210729191530142

this和super異同

  • super(參數(shù))調(diào)用基類中的某一個(gè)構(gòu)造函數(shù)(應(yīng)該為構(gòu)造函數(shù)中的第一條語句)。
  • this(參數(shù))調(diào)用本類中另一種形成的構(gòu)造函數(shù)(應(yīng)該為構(gòu)造函數(shù)中的第一條語句)。
  • super: 它引用當(dāng)前對象的直接父類中的成員(用來訪問直接父類中被隱藏的父類中成員數(shù)據(jù)或函數(shù),基類與派生類中有相同成員定義時(shí)如:super.變量名、 super.成員函數(shù)據(jù)名(實(shí)參)
  • this:它代表當(dāng)前對象名(在程序中易產(chǎn)生二義性之處,應(yīng)使用 this 來指明當(dāng)前對象;如果函數(shù)的形參與類中的成員數(shù)據(jù)同名,這時(shí)需用 this 來指明成員變量名)。
  • 調(diào)用super()必須寫在子類構(gòu)造方法的第一行,否則編譯不通過。每個(gè)子類構(gòu)造方法的第一條語句,都是隱含地調(diào)用 super(),如果父類沒有這種形式的構(gòu)造函數(shù),那么在編譯的時(shí)候就會報(bào)錯。
  • super()this() 類似,區(qū)別是,super() 從子類中調(diào)用父類的構(gòu)造方法,this() 在同一類內(nèi)調(diào)用其它方法。
  • super()this() 均需放在構(gòu)造方法內(nèi)第一行。
  • 盡管可以用this調(diào)用一個(gè)構(gòu)造器,但卻不能調(diào)用兩個(gè)。
  • thissuper 不能同時(shí)出現(xiàn)在一個(gè)構(gòu)造函數(shù)里面,因?yàn)?code>this必然會調(diào)用其它的構(gòu)造函數(shù),其它的構(gòu)造函數(shù)必然也會有 super 語句的存在,所以在同一個(gè)構(gòu)造函數(shù)里面有相同的語句,就失去了語句的意義,編譯器也不會通過。
  • this()super() 都指的是對象,所以,均不可以在 static 環(huán)境中使用。包括:static 變量,static 方法,static 語句塊。
  • 從本質(zhì)上講,this 是一個(gè)指向本對象的指針, 然而 super 是一個(gè) Java 關(guān)鍵字。

標(biāo)簽: [Java]


在這里插入圖片描述
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容