天天看點

java set get通路器了解

簡述

java中習慣将類的成員變量屬性設定為私有(private),并通過設定setXXX和getXXX方法來完成對成員變量的指派和取值操作。在剛開始學習java時一直很疑惑為什麼不将成員變量設為公有(public),直接操作成員變量不是更友善嗎?其實這樣設計是源于java的三大特性(封裝,繼承,多态)中封裝的概念。

封裝

封裝是把過程和資料包圍起來,對資料的通路隻能通過已定義的接口(該接口并非指java中interface,而是泛指隻關心方法而不關心具體實作的方法,在此處可了解為set,get通路器)。面向對象計算始于這個基本概念,即現實世界可以被描繪成一系列完全自治、封裝的對象,這些對象通過一個受保護的接口通路其他對象。封裝是一種資訊隐藏技術,在java中通過關鍵字private實作封裝。什麼是封裝?封裝把對象的所有組成部分組合在一起,封裝定義程式如何引用對象的資料,封裝實際上使用方法将類的資料隐藏起來,控制使用者對類的修改和通路資料的程度。

上述解釋來自百度百科,紅字部分為自己了解

public VS private

我們來看下為什麼不采用public标記執行個體域的理由:

我們可以用public 标記執行個體域,但是這是一種極為不提倡的做法。public資料域允許程式中的任何方法對其進行讀取和修改。這就完全破壞了封裝。任何類的任何方法都可以修改public域,從我們經驗來看,某些代碼将使用這種存取權限。而這并不是我們所希望的,是以,這裡強烈建議将執行個體域标記為private。

上述解釋摘自 《java核心技術 卷一 (第九版)》 p109

作者隻從破壞封裝的角度談了一下public标記執行個體域的缺點,其實我們可以從另一方面考慮。我們發現set get通路器和成員方法本質是一樣(邏輯上不同),那麼我們可以考慮下調用方法和直接通路屬性的不同。

隻有修改權限

這絕對是用public标記執行個體域無法辦到的(反射除外),而通路器可以通過暴露set方法來實作。

成員變量類型改變

public class Person {
	private String age;

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}
}
           
這是我們需要将age類型改為int
public class Person {
	private int age;

	public String getAge() {
		return ""+age;
	}

	public void setAge(String age) {
		this.age = Integer.parseInt(age);
	}
}
           
所有依賴person的類都無需修改代碼,如果是public标記執行個體域就必須将所有對依賴person對象age屬性的代碼進行修改。假如依賴100個。這還不是做可怕的,如果依賴類下面還要用到這個屬性那該行代碼同樣可能需要修改。

傳回引用可變對象

這裡我們建立一個Person類,該類有一個birthday(生日)屬性,正常情況下,人的生日在出生(初始化)的時候就已經固定,并且不會放生改變。
public class Person {
    private Date birthday;
    public Person(Date birthday){
        this.birthday=birthday;
    }
    public Date getBirthday() {
        return birthday;
    }
    public static void main(String[] args) {
        Person john=new Person(new GregorianCalendar(1991,6,5).getTime());
        Date d=john.getBirthday();
        System.out.println(john.getBirthday()); //=>Fri Jul 05 00:00:00 CDT 1991
        d.setMonth(0);
        System.out.println(john.getBirthday()); //=>Sat Jan 05 00:00:00 CST 1991
    }
}
           

出錯的原因很微妙。d和john.getBirthday引用同一對象.對d調用更改器方法就可以自動修改生日這個私有狀态。這是我們其實是要将get方法中return birthday修改為return (Date) birthday.clone();即可。

當然public标記執行個體域也可以實作,隻不過每次擷取成員變量是都需要手動複制一遍。顯示增加代碼的複雜度,而且增加了代碼的重複率。

而從關注點分離的角度來看,複制這個行為和調用者的業務無關,身為調用者我隻是想拿到john的生日,并将這個日期進行一些處理,而不應該去關心你傳遞的是一個引用是以我需要複制一下。這本身就是技術對于業務邏輯的污染,應該是我們盡量去避免的。