java的數據類為什么需要get,set

對于初學java的同學,應該都有個疑惑,我們在定義一個數據類的時候,為什么不把字段直接寫成public的,硬是要把屬性定義成private的,然后給屬性加上getset方法,比如下面這兩種寫法

class Data{
    public String name="";
    public int age=1;
}


class Data{
    private String name="";
    private int age=1;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

之前一直以為是為了做方法封裝,我們可以在屬性的getset方法中加入一些邏輯,擴展性更好,所以沒有深究
直到后來學習了類的加載過程,看到了一道這樣的題

   public void main(){
        Father fa=new Son();
        System.out.println(fa.age);
        System.out.println(fa.name);
        System.out.println(fa.getName());
    }
class Father{
    public String name="張三";
    public int age=1;
    public String getName(){
        return name;
    }
}
class Son extends Father{
    public String name="李四";
    public int age=2;
    public String getName(){
        return name;
    }
}

這是一道典型的面向對象的多態的例子,我們當初接觸java的面向對象的時候,最開始接觸的就是這種寫法。
大家猜測一下輸出結果是什么

1
張三
李四

不知道大家猜對了沒有。
下面來解釋一下原因:
我們都知道每個類里面都有一個this關鍵字指向本類對象,用來告訴我們當前使用變量的屬于哪個實例對象,也就是所謂的上下文。
類初始化的時候,每個非靜態方法內部都會有一個變量this,這個可以通過查看自字節碼看出來,包括匿名內部類,也會持有當前類的this對象(這個就是android handler內存泄露的原因之一)。
所以在在方法內調用屬性,其實相當于這么寫

  public String getName(){
        return this.name;
    }

但是屬性是沒有this指針的,也就是說屬性是沒有多態的,我們調用的對象是什么,輸出之后就是哪個對象的屬性。

//大家可以試試這個例子,輸出結果都是=號前面的對象的屬性。
   public void main(){
        Son son=new Son();
        System.out.println(son.age);
        System.out.println(son.name);
        System.out.println(son.getName());
    Father fa=new Father();
        System.out.println(fa.age);
        System.out.println(fa.name);
        System.out.println(fa.getName());
    }

而這種多態的寫法是面向對象的核心所在,在一些大的工具框架內隨處可見,比如安卓的源碼中的Context類,Application,Activity,Service等一眾關鍵類都繼承自Context。
那為啥同樣編譯成字節碼的kotlin卻可以直接調用屬性呢,那是因為kotlin默認幫你實現了屬性的getset方法

//這時候編譯器會提示我們Redundant getter ,多余的getset方法
class Data{
    var name:String=""
    get() {return field}
    set(value) {
        field=value
    }
}

其實我們在刷面試題的時候有很多對應的面試題:比如下面這個,屬于經典面試題

   public void main() {
//        Father father = new Father();

      Father fa = new Son();
        
 //        Son son = new Son();
    }
class Father {
    public String name = "張三";

    public Father() {
        System.out.println(name);
        System.out.println(this.name);
        getName();
    }

    public void getName() {
        System.out.println("調用的Father");
        System.out.println(this.name);
    }
}

class Son extends Father {
    public String name = "李四";

    public Son() {
    }

    public void getName() {
        System.out.println("調用的son");
        System.out.println(this.name);
    }
}

大家可以想想輸出結果是啥

張三
張三
調用的son
null
李四

這道題其實和類的加載順序有關系,先加載父類的屬性(屬性加載會先把屬性初始化為基本值,比如int為0,String為null),再加載父類的構造方法,然后再是子類的屬性和子類的構造方法。這里最難理解的其實是這個null,所以我特意在getName中多加了一個打印。
子類初始化的時候,先調用父類的構造方法,父類調用構造方法的時候,去調用getName方法,被調用的被重寫的子類的getName方法,而這時候子類的屬性賦值操作還沒執行,只做了初始化操作,所以打印出來的name值就是null。

至于static的靜態變量和方法,都是屬于class對象本身,誰調用就是用誰的,相對來說簡單一點。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容