目錄
對象拷貝
Person類定義如下:
/**
* Created by leboop on 2020/5/23.
*/
public class Person {
private String name;
private Integer age;
private City city;
public Person(String name, Integer age, City city) {
this.name = name;
this.age = age;
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", city=" + city +
'}';
}
}
City類定義如下:
/**
* Created by leboop on 2020/5/23.
*/
public class City {
private String cityName;
public City(String cityName) {
this.cityName = cityName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
@Override
public String toString() {
return "City{" +
"cityName='" + cityName + '\'' +
'}';
}
}
客戶端代碼如下:
/**
* Created by leboop on 2020/5/23.
*/
public class ProtoMain {
public static void main(String[] args) {
City city=new City("shanghai");
Person person = new Person("zs", 30, city);
}
}
現在有一個需求:創建一個與person對象具有相同屬性值的新對象p2,能不能直接將person對象賦值給p2呢?代碼如下:
/**
* Created by leboop on 2020/5/23.
*/
public class ProtoMain {
public static void main(String[] args) {
City city=new City("shanghai");
Person person = new Person("zs", 30, city);
Person p2 = person;
System.out.println(p2);
}
}
輸出結果如下:
Person{name='zs', age=30, city=City{cityName='shanghai'}}
從結果來看好像複製成功了。但是現在修改p2對象的age屬性值,如下:
/**
* Created by leboop on 2020/5/23.
*/
public class ProtoMain {
public static void main(String[] args) {
City city=new City("shanghai");
Person person = new Person("zs", 30, city);
Person p2 = person;
p2.setAge(100);
System.out.println(person);
}
}
輸出結果如下:
Person{name='zs', age=100, city=City{cityName='shanghai'}}
發現person對象的age屬性值也被修改爲100,這是不允許的。那如何解決這個問題呢?這就需要淺拷貝技術。
淺拷貝
JDK給我們提供了現成的拷貝技術:Cloneable接口和Object類的clone方法。Cloneable接口源碼如下:
public interface Cloneable {
}
Cloneable接口沒有任何方法和屬性,它僅僅表示一種克隆標記。Object類的clone方法源碼如下:
protected native Object clone() throws CloneNotSupportedException;
它由protected和native關鍵字修飾,表示該方法在子類實現,其他類中不可見。native表示該方法是C++底層方法。現在可以將Person類改爲如下形式:
/**
* Created by leboop on 2020/5/23.
*/
public class Person implements Cloneable {
private String name;
private Integer age;
private City city;
public Person(String name, Integer age, City city) {
this.name = name;
this.age = age;
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", city=" + city +
'}';
}
}
Person類現在實現了Cloneable接口和重寫了Object類的clone方法。客戶端代碼改爲:
/**
* Created by leboop on 2020/5/23.
*/
public class ProtoMain {
public static void main(String[] args) throws CloneNotSupportedException{
City city=new City("shanghai");
Person person = new Person("zs", 30, city);
Person p2 = (Person) person.clone();
p2.setAge(100);
System.out.println(p2);
System.out.println(person);
}
}
輸出結果如下:
Person{name='zs', age=100, city=City{cityName='shanghai'}}
Person{name='zs', age=30, city=City{cityName='shanghai'}}
現在對p2的age屬性值修改已經不影響原來person對象的屬性值了,完成了屬性值的淺拷貝。有人會有疑問:既然Cloneable接口沒有任何方法,Person是否可以不實現該接口,答案是不行的,如果Person類不實現Cloneable接口,編譯時不會有問題,但是運行客戶端代碼時會拋出java.lang.CloneNotSupportedException異常,如下:
Exception in thread "main" java.lang.CloneNotSupportedException:
那麼現在是不是已經沒有問題了呢?還不行。如果我們修改p2對象的引用類型屬性city就會出現問題。這是深拷貝技術。
深拷貝
現在我們將p2對象的city屬性值修改爲beijing,如下:
/**
* Created by leboop on 2020/5/23.
*/
public class ProtoMain {
public static void main(String[] args) throws CloneNotSupportedException{
City city=new City("shanghai");
Person person = new Person("zs", 30, city);
Person p2 = (Person) person.clone();
p2.setAge(100);
p2.getCity().setCityName("beijing");
System.out.println(p2);
System.out.println(person);
}
}
輸出結果如下:
Person{name='zs', age=100, city=City{cityName='beijing'}}
Person{name='zs', age=30, city=City{cityName='beijing'}}
發現person對象的city屬性值也被修改爲了beijing,這也是不允許的。如何解決呢?需要對City對象進行克隆。修改兩個地方
(1)City類實現Cloneable接口,重寫Object類的clone方法;
(2)Person類對拷貝普通屬性時,需要對引用類型屬性進行拷貝;
如下:
/**
* Created by leboop on 2020/5/23.
*/
public class City {
private String cityName;
public City(String cityName) {
this.cityName = cityName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
@Override
public String toString() {
return "City{" +
"cityName='" + cityName + '\'' +
'}';
}
}
/**
* Created by leboop on 2020/5/23.
*/
public class Person implements Cloneable {
private String name;
private Integer age;
private City city;
public Person(String name, Integer age, City city) {
this.name = name;
this.age = age;
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person) super.clone();
p.city = (City) city.clone();
return p;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", city=" + city +
'}';
}
}
此時客戶端代碼輸出結果如下:
Person{name='zs', age=100, city=City{cityName='beijing'}}
Person{name='zs', age=30, city=City{cityName='shanghai'}}
原型模式
原型模式指的就是對象深拷貝,也就是創建同原對象具有相同屬性值的新對象,並且對新對象的操作不影響原對象。它如下實現
(1)需要拷貝的對象(比如person)所對應的類(比如Person類)必須實現Cloneable接口和重寫Object的clone方法;
(2)如果Person類有引用類型的屬性(比如city),該引用類型對應的類(比如City)也需要實現Cloneable接口和重寫Object的clone方法;