首頁 > 軟體

java中的this參照及物件構造初始化

2022-08-12 22:01:01

1. this 參照

1.1 為什麼要有this參照

先來寫一個日期類的例子:

public class classCode {
    public int year;
    public int month;
    public int day;
    public void setDay(int y, int m, int d){
        year = y;
        month = m;
        day = d;
    }
    public void printDate(){
        System.out.println(year + "-" + month + "-" + day);
    }
    public static void main(String[] args) {
        // 構造三個日期型別的物件 d1 d2 d3
        classCode Date1 = new classCode();
        classCode Date2 = new classCode();
        classCode Date3 = new classCode();

        // 對d1,d2,d3的日期設定
        Date1.setDay(2022, 8, 9);
        Date2.setDay(2023, 8, 9);
        Date3.setDay(2024, 8, 9);

        //列印日期的內容
        Date1.printDate();
        Date2.printDate();
        Date3.printDate();
    }
}

以上程式碼定義了一個日期類,然後main方法中建立了三個物件,並通過classCode類中的成員方法對物件進行設定和列印,程式碼整體邏輯非常簡單,沒有任何問題。

有兩個需要注意的地方:

1.形參名不小心與成員變數名相同:

public void setDay(int year, int month, int day){ 
    year = year;
    month = month;
    day = day; 
}

那函數體中到底是誰給誰賦值?成員變數給成員變數?引數給引數?引數給成員變數?成員變數引數?

2.三個物件都在呼叫setDate和printDate函數,但是這兩個函數中沒有任何有關物件的說明,setDate和printDate函數如何知道列印的是哪個物件的資料呢?

可以看到如果形參名和成員變數名的話,賦值以後變數的值為0,說明並沒有賦值成功。

那應該怎麼做呢?往下面看。

1.2 什麼是this參照

this參照指向當前物件(成員方法執行時呼叫該成員方法的物件),在成員方法中所有成員變數的操作,都是通過該參照去存取只不過所有的操作對使用者是透明的,即使用者不需要來傳遞,編譯器自動完成。

改進之後的程式碼:

public void setDay(int year, int month, int day){
    this.year = year;
    this.month = month;
    this.day = day;
}

可以看到新增 this 參照後,賦值成功。
this 是預設新增的。即使是不加 this,也會有一個預設新增的 this。但是不新增的話,如果形參名和成員變數名相同就會帶來問題。

在下面程式碼中,可以看到三個物件都在呼叫 setDateprintDate 函數,而且也沒有說明,那該怎麼知道列印的是哪個物件的資料?

public static void main(String[] args) {
     // 構造三個日期型別的物件 Date1 Date2 Date3
     Date Date1 = new Date();
     Date Date2 = new Date();
     Date Date3 = new Date();

     // 對Date1,Date2,Date3的日期設定
     Date1.setDay(2022, 8, 9);
     Date2.setDay(2023, 8, 9);
     Date3.setDay(2024, 8, 9);

     //列印日期的內容
     Date1.printDate();
     Date2.printDate();
     Date3.printDate();
}

可以通過下面兩點兩點來判斷列印哪個物件:

  • 前面的物件是哪個物件,列印哪個物件的資料
  • 隱藏的引數。
public void setDay(Date this, int y, int m, int d){
    this.year = y;
    this.month = m;
    this.day = d;
}
public void printDate(Date this){
    System.out.println(this.year +"-"+ this.month +"-"+ this.day);
}

this 的三種使用方式:

  • this.成員變數
  • this.存取成員方法
  • this();存取構造方法

1.3 this參照的特性

  • this的型別:對應類型別參照,即哪個物件呼叫就是哪個物件的參照類。
  • this只能在"成員方法"中使用。
  • 在"成員方法"中,this只能參照當前物件,不能再參照其他物件。
  • this是“成員方法”第一個隱藏的引數,編譯器會自動傳遞,在成員方法執行時,編譯器會負責將呼叫成員方法。

物件的參照傳遞給該成員方法,this負責來接收。

就算成員變數名與形參名不相同也建議把 this 寫上,這相當於一種保護措施,而且也是一種好的程式設計規範。

1.4 this參照練習題

寫一個學術類,有姓名、年齡等屬性,然後通過一個方法來設定這些屬性的值,其次通過寫兩個方法,在一個方法當中使用this呼叫另一個方法。

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

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

    public void printStudent() {
        System.out.println(this.name + "->" + this.age);
    }

    public static void main(String[] args) {
        Student student = new Student();
        student.setStudent("zhangsan", 19);
        student.printStudent();
    }
}

2. 物件的構造及初始化

2.1 如何初始化物件

通過前面知識點的學習知道,在Java方法內部定義一個區域性變數時,必須要初始化,否則會編譯失敗。

public static void main(String[] args) {
    int a;
    System.out.println(a);
}// Error:(26, 28) java: 可能尚未初始化變數a.

如果是一個物件即使是沒賦值也不會報錯,因為這是一個參照變數。

 public static void main(String[] args) {
        // 構造一個日期型別的物件 
        Date date = new Date();
        date.setDay(2022, 8, 9);
        //列印日期的內容
        date.printDate();
    }//程式碼可以正常通過編譯

通過上述例子發現兩個問題:

  • 每次物件建立好後呼叫setDate方法設定具體日期,比較麻煩,那物件該如何初始化?
  • 區域性變數必須要初始化才能使用,為什麼欄位宣告之後沒有給值依然可以使用?

這就引入了構造方法。接著往下看。

2.2 構造方法

2.2.1 概念

構造方法(也稱為構造器)是一個特殊的成員方法,名字必須與類名相同,在建立物件時,由編譯器自動呼叫,並且在整個物件的生命週期內只呼叫一次。

public class Student {
    public String name;
    public int age;
    public Student(){//這是一個構造方法
        System.out.println("不帶引數的構造方法");
    }
    public Student(String name, int age) {//這是一個構造方法
        System.out.println("帶引數的構造方法");
        this.name = name;
        this.age = age;
    }
    public void setStudent(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void printStudent() {
        System.out.println(this.name + "->" + this.age);
    }
    public static void main(String[] args) {
        Student student = new Student();//這一行是構造方法的呼叫
    }
}

Student student = new Student();
new在範例化物件,而範例化物件一定會呼叫構造方法。

注意:當我們沒有提供構造方法時,編譯器會自動提供一個不帶引數的構造方法。

2.2.2 特性

  • 名字必須與類名相同。
  • 沒有返回值型別,設定為void也不行。
  • 建立物件時由編譯器自動呼叫,並且在物件的生命週期內只呼叫一次。
  • 構造方法可以過載(使用者根據自己的需求提供不同引數的構造方法。
 public Student(){//不帶引數的構造方法
        System.out.println("不帶引數的構造方法");
    }
    public Student(String name, int age) {//帶兩個引數的構造方法
        System.out.println("帶引數的構造方法");
        this.name = name;
        this.age = age;

    }

上述兩個構造方法:名字相同,參數列不同,因此構成了方法過載。

如果使用者沒有顯式定義,編譯器會生成一份預設的構造方法,生成的預設構造方法一定是無參的。

public class Work {
    public int one = 1;
    public int three = 3;
    public int two = 2;

    public void printWork() {
        System.out.println(one + "-" + two + "-" + three);
    }

    public static void main(String[] args) {
        Work work = new Work();
        work.printWork();
    }
}

上述Work類中,沒有定義任何構造方法,編譯器會預設生成一個不帶引數的構造方法。

那如何呼叫帶引數的構造方法呢?

public class Work {
    public int one = 1;
    public int three = 3;
    public int two = 2;

    public Work(int one, int two, int three) {
        System.out.println("帶引數的構造方法");
        this.one = one;
        this.two = two;
        this.three = three;
    }

    public void printWork() {
        System.out.println(one + "-" + two + "-" + three);
    }

    public static void main(String[] args) {
        Work work = new Work(3, 6, 9);
        work.printWork();
    }
}

注意:一旦使用者定義,編譯器則不再生成。

構造方法中,可以通過this呼叫其他構造方法來簡化程式碼。

 public Work() {
        this(10, 20, 30);//呼叫本類當中其他的構造方法
        System.out.println("不帶引數的的構造方法");
    }

    public Work(int one, int two, int three) {
        System.out.println("帶引數的構造方法");
        this.one = one;
        this.two = two;
        this.three = three;
    }

注意:

  • this呼叫必須在構造方法裡面,
  • 要在在第一行,
  • 不能寫成迴圈呼叫。

絕大多數情況下使用public來修飾,特殊場景下會被private修飾(後序講單例模式時會遇到)

2.3 預設初始化

為什麼使用成員變數不需要初始化呢?

在程式層面只是簡單的一條語句,在JVM(以後講)層面需要做好多事情,下面簡單介紹下:

  • 檢測物件對應的類是否載入了,如果沒有載入則載入
  • 為物件分配記憶體空間
  • 處理並行安全問題

比如:多個執行緒同時申請物件,JVM要保證給物件分配的空間不衝突初始化所分配的空間
即:物件空間被申請好之後,物件中包含的成員已經設定好了初始值

比如:

設定物件頭資訊(關於物件記憶體模型後面會介紹)呼叫構造方法,給物件中各個成員賦值

2.4 就地初始化

定義成員變數的時候就已經賦值好了。

public class HardWork {
    public int a = 10;//就地初始化
    public int b = 20;//就地初始化
    public String c = "zhangsan";//就地初始化

    public void setWork(int a,  int b, String c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public void printWork() {
        System.out.println(a + "-" + b + "-" + c);
    }
    public static void main(String[] args) {
        HardWork work = new HardWork();
        work.printWork();
        System.out.println();
    }
}

注意:程式碼編譯完成後,編譯器會將所有給成員初始化的這些語句新增到各個建構函式中。

到此這篇關於java中的this參照及物件構造初始化的文章就介紹到這了,更多相關java this參照 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com