1.問題
/**
* 輸出: Mon Apr 26 10:54:10 CST 2010
* Mon Apr 26 10:54:10 CST 2010
*/
public static void main(String[] args){
Example test = new Example(new Date());
Date d = test.getDate();
double tenYearsInMillisSeconds = 10 * 365.25 * 24 * 3600 * 1000;
d.setTime((long) (d.getTime() - tenYearsInMillisSeconds));
System.out.println(d);
System.out.println(test.getDate());
}
public class Example {
private Date date;
public Example(Date date){
this.date = date;
}
public Date getDate() {
return date;
}
}
Date
類破壞了Example
的封裝,導致修改實例 d
時影響了 test
的值,原因是Date
類生成的對象是可變的。
2.對象與對象變量
Date birthday = new Date();
Date deadline = birthday;
這兩個變量引用同一個對象(請參見圖 4-4 )。
但一個對象變量並沒有實際包含一個對象,而僅僅引用一個對象。
在 Java 中,任何對象變量的值都是對存儲在另外一個地方的一個對象的引用。new
操作符的返回值也是一個引用。
Date birthday = new Date();
可以理解為new Date()
構造了一個 Date
類型的對象, 並且它的值是對新創建對象的引用。這個引用存儲在變量 birthday
中。
Java 對象變量與 C++ 的引用並不同
可以將 Java 的對象變量看作 C++ 的對象指針。例如,
Date birthday; // Java
實際上,等同於
Date* birthday; // C++
所有的 Java 對象都存儲在堆中。 當一個對象包含另一個對象變量時, 這個變量依然
包含着指向另一個堆對象的指針。
3.更改器方法與訪問器方法
上文還是沒有解釋清楚為什么Date
類的對象是可變對象,原因在這。
假設在上文中Example
類中使用Java中與Date
類相近的LocalDate
類便不會出現上述情況,測試可以自己去嘗試。
原因在於假設使用LocalDate
類中的plusDays
方法來修改對象變量,它會生成一個新的LocalDate
對象,然后把這個新對象賦值給調用者,原來的對象不做任何改動。
此類只訪問對象而不修改對象的方法有時稱為訪問器方法(accessor method)
而像Date
類中的setTime
方法會使得原對象的狀態發生改變,此類稱為更改器方法(mutator method)
4.解決方法
如果需要返回一個可變數據域的拷貝,就應該使用 clone。這樣會創建一個當前對象的副本,而不會對當前對象造成影響。
有關 clone 的進一步講解 Java 淺拷貝和深拷貝
public class Example {
private Date date;
public Example(Date date){
this.date = date;
}
public Date getDate() {
return (Date) date.clone();
}
}
5.不可變類
5.1什么是不可變類
不可變類指當類被實例化后,該類的成員變量均不可被改變。
如JDK內部自帶的很多不可變類Interger
、Long
、 Boolean
和String
等。
5.2優缺點
- 優點:1.線程安全 2.易於構造、使用和測試 3.可以被自由地共享
- 缺點:對於每一個不同的值都需要對應一個單獨的對象
5.3如何實現不可變類
- Class需要用
final
修飾,保證類不能被繼承 - 所有成員變量需要
private
修飾,保證成員變量不能直接被訪問 - 類中不允許提供
setter
方法,保證成員變量不會被改變 - 在
getter
方法中不能返回對象本身,返回對象的拷貝