java克隆 為什麼需要克隆 我們在很多時候需要使用一個對象去記錄另外一個對象的當前狀態,對象中可能會有很多屬性,如果我們一個一個去設置,不僅不方便,而且效率很低,我們看一個初學者可能遇到的問題 也許有的人認為Person p2=p1這樣的方式就可以克隆一個對象,這種想法是錯誤的,這種使用等號賦值的 ...
java克隆
為什麼需要克隆
我們在很多時候需要使用一個對象去記錄另外一個對象的當前狀態,對象中可能會有很多屬性,如果我們一個一個去設置,不僅不方便,而且效率很低,我們看一個初學者可能遇到的問題
class Person{
String name;
int age;
public Person() {}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
@Test
public void test2() {
Person p1=new Person("tom",20);
Person p2=p1;
System.out.println(p1);
System.out.println(p2);
System.out.println("---------");
p1.age=30;
System.out.println(p1);
System.out.println(p2);
}
輸出:
Person [name=tom, age=20]
Person [name=tom, age=20]
---------
Person [name=tom, age=30]
Person [name=tom, age=30]
也許有的人認為Person p2=p1這樣的方式就可以克隆一個對象,這種想法是錯誤的,這種使用等號賦值的方式只是將p1的地址賦值給了p2對象,那麼p1和p2都指向了堆中的同一個對象,所以,修改p1那麼p2也就變了
如果一個一個屬性的去手動設置不僅麻煩,而且屬性可能也有屬性,修改起來不容易
public void test2() {
Person p1=new Person("tom",20);
Person p2=new Person();
p2.age=p1.age;
p2.name=p1.name;
}
這裡Person的屬性層級很簡單,修改起來看起來很簡單,但是如果Person多了一個Address類型的屬性,那麼手動修改就必須要去new一個Address並賦值屬性,假如屬性的嵌套層級深了,就很難了
所以我們可以使用Object提供的clone方法來克隆對象,由於clone方法是用protected關鍵字修飾的,我們如果想在類外使用就需要重寫父類的方法,Object的clone方法是一個native關鍵字修飾的方法,即調用其他語言的方法,效率很高,值得註意的一點是要克隆對象的類必須實現Cloneable介面,這個介面是一個標記介面,裡面並沒有定義任何方法,如果沒事實現Cloneable介面使用clone方法,會拋出CloneNotSupportedException異常
淺克隆
如果原型對象的屬性是值類型,那麼複製一份給克隆對象,如果屬性是引用類型,那麼把屬性的引用賦值給克隆對象
class Person implements Cloneable{//必須實現Cloneable介面
String name;
int age;
public Person() {}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
protected Object clone(){
Person p=null;
try {
p=(Person)super.clone();
}catch(CloneNotSupportedException e) {//實現了Cloneable介面這個異常就不可能發生
e.printStackTrace();
}
return p;
}
}
@Test
public void test2() {
Person p1=new Person("tom",20);
Person p2=(Person)p1.clone();
System.out.println(p1);
System.out.println(p2);
System.out.println("---------");
p1.age=30;
System.out.println(p1);
System.out.println(p2);
}
輸出:
Person [name=tom, age=20]
Person [name=tom, age=20]
---------
Person [name=tom, age=30]
Person [name=tom, age=20]
這種Object提供的clone在類中的屬性全是值類型的時候不會出現問題,但是如果類屬性有引用類型就會出現問題
class Person implements Cloneable{
String name;
int age;
Address address;
public Person() {}
public Person(String name, int age,Address addr) {
super();
this.name = name;
this.age = age;
this.address=addr;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
@Override
protected Object clone(){
Person p=null;
try {
p=(Person)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
class Address{
String addr;
public Address(String addr) {
super();
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
}
@Test
public void test2() {
Address addr=new Address("成都市郫都區");
Person p1=new Person("tom",20,addr);
Person p2=(Person)p1.clone();
System.out.println(p1);
System.out.println(p2);
System.out.println("---------");
p1.address.addr="成都市金牛區";
System.out.println(p1);
System.out.println(p2);
}
輸出:
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛區]]
Person [name=tom, age=20, address=Address [addr=成都市金牛區]]
修改p1的address根據p1克隆出來的p2的address也改變了,這就是所說的當克隆對象的屬性是引用類型時,只會複製它的引用,而這種情況一般是我們不希望的,所以需要使用深克隆
深克隆
1.克隆對象所有的引用類型都重寫clone方法
這裡所說的引用類型重寫clone方法,是指克隆對象本身屬性是引用類型的必須重寫clone方法且引用類型中的引用類型也必要要重寫,且必須在clone方法中顯式條用
class Person implements Cloneable{
String name;
int age;
Address address;//這個屬性是引用類型,必須實現Cloneable介面重寫clone方法
public Person() {}
public Person(String name, int age,Address addr) {
super();
this.name = name;
this.age = age;
this.address=addr;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
@Override
protected Object clone(){
Person p=null;
try {
p=(Person)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
//顯式調用克隆引用數據類型
p.address=(Address)address.clone();
return p;
}
}
class Address implements Cloneable{
String addr;
public Address(String addr) {
super();
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
@Override
protected Object clone() {
Address addr=null;
try {
addr=(Address)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return addr;
}
}
@Test
public void test2() {
Address addr=new Address("成都市郫都區");
Person p1=new Person("tom",20,addr);
Person p2=(Person)p1.clone();
System.out.println(p1);
System.out.println(p2);
System.out.println("---------");
p1.address.addr="成都市金牛區";
System.out.println(p1);
System.out.println(p2);
}
輸出:
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛區]]
Person [name=tom, age=20, address=Address [addr=成都市金牛區]]
使用序列化方法
需要克隆的對象類必須實現Serializable介面,這個介面也是一個標記介面,介面中沒有任何方法,這個方法的實現類表示可以將這個類的對象寫入到IO流中,那麼久可以把對象在網路中發送或保存到本地磁碟
class Person implements Cloneable,Serializable{
/**
*
*/
private static final long serialVersionUID = 8990580911834489134L;
String name;
int age;
Address address;
public Person() {}
public Person(String name, int age,Address addr) {
super();
this.name = name;
this.age = age;
this.address=addr;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
@Override
protected Object clone(){
Person p=null;
try {
//將對象寫入流中
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(this);
//將對象從流中讀取出來
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bais);
p=(Person)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return p;
}
}
class Address implements Serializable{
/**
*
*/
private static final long serialVersionUID = 328854588872604721L;
String addr;
public Address(String addr) {
super();
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
}
@Test
public void test2() {
Address addr=new Address("成都市郫都區");
Person p1=new Person("tom",20,addr);
Person p2=(Person)p1.clone();
System.out.println(p1);
System.out.println(p2);
System.out.println("---------");
p1.address.addr="成都市金牛區";
System.out.println(p1);
System.out.println(p2);
}
輸出:
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]
---------
Person [name=tom, age=20, address=Address [addr=成都市金牛區]]
Person [name=tom, age=20, address=Address [addr=成都市郫都區]]