Object
文檔中指出對象需要被clone
,則需要實現Cloneable
接口。Cloneable
接口只是個標記,沒有任何方法。
clone
約定
對於任何對象x
,
- x.clone() != x
返回爲true
- x.clone().getClass() == x.getClass()
返回爲true
- x.clone().equals(x)
返回爲true
但是約定同時指出,這些都不是絕對必須的
重寫clone
方法
- 對於類中只有原始類型或者不可變的變量,直接調用父類的
clone
方法即可
public class PhoneNumber implements Cloneable {
private short areaCode;
private short prefix;
private short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max) {
throw new IllegalArgumentException(name + ": " + arg);
}
}
@Override
public boolean equals(Object o) {
//==判斷
if (o == this) {
return true;
}
//instanceof判斷
if (!(o instanceof PhoneNumber)) {
return false;
}
//各屬性判斷
PhoneNumber pn = (PhoneNumber) o;
return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
}
@Override
protected PhoneNumber clone() throws CloneNotSupportedException {
return (PhoneNumber) super.clone();
}
}
- 對於類中的屬性爲引用類型的,除了調用父類的
clone
方法之外,引用類型的屬性也需要重新賦值
public class User implements Cloneable {
private String name;
private int sex;
private String phone;
private Address address;
public User() {
}
public User(String name, int sex, String phone, Address address) {
this.name = name;
this.sex = sex;
this.phone = phone;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.address = address.clone();
return user;
}
class Address implements Cloneable {
private String city;
private String area;
private String road;
public Address(String city, String area, String road) {
this.city = city;
this.area = area;
this.road = road;
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
}
}
爲什麼要慎用
- 從重寫
clone
方法章節中可以知道,默認的clone
方法是淺拷貝,需要深拷貝的話需要重寫clone
方法對屬性進行重賦值,使代碼看起來很繁瑣 - 接口表示的是客戶可以調用的方法,但是
Cloneable
接口沒有任何方法,僅僅起標記作用,且子類(需要深拷貝)修改了父類的默認拷貝行爲
所以一般在開發中,很少使用clone
方法。另在《阿里巴巴Java開發手冊》中,有條建議:【推薦】慎用Object
的clone
方法來拷貝對象。因此在日常開發中,我們應該儘量避免採用clone
的方法。
替換方法
常替換的方法有:
1. 拷貝構造器
public class PhoneNumber {
private short areaCode;
private short prefix;
private short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
//拷貝構造器
public PhoneNumber(PhoneNumber phoneNumber) {
this.areaCode = phoneNumber.getAreaCode();
this.prefix = phoneNumber.getPrefix();
this.lineNumber = phoneNumber.getLineNumber();
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max) {
throw new IllegalArgumentException(name + ": " + arg);
}
}
public short getAreaCode() {
return areaCode;
}
public void setAreaCode(short areaCode) {
this.areaCode = areaCode;
}
public short getPrefix() {
return prefix;
}
public void setPrefix(short prefix) {
this.prefix = prefix;
}
public short getLineNumber() {
return lineNumber;
}
public void setLineNumber(short lineNumber) {
this.lineNumber = lineNumber;
}
}
- 拷貝工廠
public class PhoneNumber {
private short areaCode;
private short prefix;
private short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
//拷貝工廠方法
public static PhoneNumber newInstance(PhoneNumber phoneNumber) {
return new PhoneNumber(phoneNumber.getAreaCode(), phoneNumber.getPrefix(), phoneNumber.getLineNumber());
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max) {
throw new IllegalArgumentException(name + ": " + arg);
}
}
public short getAreaCode() {
return areaCode;
}
public void setAreaCode(short areaCode) {
this.areaCode = areaCode;
}
public short getPrefix() {
return prefix;
}
public void setPrefix(short prefix) {
this.prefix = prefix;
}
public short getLineNumber() {
return lineNumber;
}
public void setLineNumber(short lineNumber) {
this.lineNumber = lineNumber;
}
}