Part6:Java中的克隆 @[toc] Example01:Java對象的假克隆 對象的克隆是Java中的一項高級技術,獲得與其相同的對象。 基本數據類型可以使用“=”來進行克隆,此時兩個變數除了相等是沒有任何關係的。而對於引用類型數據不能簡單地使用“=”進行克隆,這與J ...
目錄
Part6:Java中的克隆
@
***
Example01:Java對象的假克隆
- 對象的克隆是Java中的一項高級技術,獲得與其相同的對象。
基本數據類型可以使用“=”來進行克隆,此時兩個變數除了相等是沒有任何關係的。而對於引用類型數據不能簡單地使用“=”進行克隆,這與Java的記憶體空間使用有關。
Java將記憶體空間分成兩塊,即棧和堆。在棧中保存基本類型和引用變數;在堆中保存對象。對於引用變數而言,使用“=”將修改引用,而不是複製堆中的對象。此時兩個引用變數將指向同一個對象。因此,如果一個變數對其修改則會改變另一個變數。
運行結果:
代碼實現:
public class Employee {
private String name;
private int age;
//省略set()和get()方法
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
System.out.println("-----克隆之前:--------");
Employee employee1 = new Employee();
employee1.setName("hyn");
employee1.setAge(20);
System.out.println("員工1的信息:\n"+employee1);
System.out.println("-----克隆之後:--------");
Employee employee2 = employee1; //將employee1賦值給employee2
employee2.setName("azw");
employee2.setAge(21);
System.out.println("員工1的信息:\n"+employee1);
System.out.println("員工2的信息:\n"+employee2);
}
}
Example02:Java對象的淺克隆
在克隆對象時,如果對象的成員變數是基本數據類型,則使用淺克隆即可完成。如果對象的成員變數包括可變引用類型,則需要深克隆。
運行結果:
代碼實現:
//Address.java
public class Address {
private String state; //所在國家
private String province; //所在省
private String city; //所在城市
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}
//省略set()和get()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("國家:"+state+",");
sb.append("省:"+province+",");
sb.append("市:"+city);
return sb.toString();
}
}
//Employee.java
public class Employee implements Cloneable{
private String name;
private int age;
private Address address;
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
//省略set()和get()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年齡:"+age+",");
sb.append("\n地址:"+address);
return sb.toString();
}
@Override
public Employee clone() throws CloneNotSupportedException { //實現淺克隆
Employee employee = (Employee) super.clone();
return employee;
}
}
測試代碼:
class Test {
public static void main(String[] args) throws CloneNotSupportedException {
System.out.println("*****克隆之前:******");
Address address = new Address("中國", "湖北", "武漢");
Employee employee1 = new Employee("azw", 20, address);
System.out.println("員工1的信息:\n" + employee1); //employee1的信息
System.out.println("*****克隆之後:******");
Employee employee2 = employee1.clone(); //使用克隆創建Employee2
employee2.getAddress().setState("中國"); //修改地址
employee2.getAddress().setProvince("黑龍江");
employee2.getAddress().setCity("哈爾濱");
employee2.setName("hyn");
employee2.setAge(21);
System.out.println("員工1的信息:\n" + employee1);
System.out.println("員工2的信息:\n" + employee2);
}
}
- 如果引用類型是不可變的,如String類對象,則不必進行深克隆。
***
Example03:Java對象的深克隆
- 如果類的成員變數中包括可變引用類型,則需進行深克隆。
運行結果:
代碼實現:
//Address.java
public class Address implements Cloneable{
private String state; //所在國家
private String province; //所在省
private String city; //所在城市
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}
//省略set()和get()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("國家:"+state+",");
sb.append("省:"+province+",");
sb.append("市:"+city);
return sb.toString();
}
//---------------------------
@Override
public Address clone() throws CloneNotSupportedException {
//Address類中的域不是基本類型就是不可變類型,所以可以直接使用淺克隆
Address address = (Address) super.clone();
return address;
}
//---------------------------
}
//Employee.java
public class Employee implements Cloneable{
private String name;
private int age;
private Address address;
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
//省略set()和get()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年齡:"+age+",");
sb.append("\n地址:"+address);
return sb.toString();
}
@Override
public Employee clone() throws CloneNotSupportedException { //實現深克隆
Employee employee = (Employee) super.clone();
//---------------------------------
employee.address = address.clone();
//---------------------------------
return employee;
}
}
//測試代碼同Example02測試代碼.
- 要點:通常情況下,需要用到克隆對象時都需要使用深克隆。
***
Example04:序列化與對象克隆
如果類的成員變數比較複雜,例如使用了多個可變的引用類型,使用clone()方法是非常麻煩的,所以可以考慮序列化的方式完成克隆。
運行結果:
代碼實現:
import java.io.Serializable;
public class Employee implements Serializable {
//同Example04中Employee.java的代碼
}
public class Address implements Serializable {
//同Example04中Assress.java的代碼
}
測試代碼:
class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("*****序列化之前:******");
Address address = new Address("中國", "湖北", "武漢");
Employee employee1 = new Employee("azw", 20, address);
System.out.println("員工1的信息:\n" + employee1); //employee1的信息
System.out.println("*****序列化之後:******");
Employee employee2 = null;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\employee.txt"));
out.writeObject(employee1); //將對象寫入到本地文件中
ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:\\employee.txt"));
employee2 = (Employee)in.readObject(); //從本地文件中讀取對象
if (employee2 != null) {
employee2.getAddress().setState("中國"); //修改地址
employee2.getAddress().setProvince("黑龍江");
employee2.getAddress().setCity("哈爾濱");
employee2.setName("hyn");
employee2.setAge(21);
System.out.println("員工1的信息:\n" + employee1);
System.out.println("員工2的信息:\n" + employee2);
}
}
}
要點:進行序列化的類需要實現Serializable介面,該介面中並沒有定義任何方法,是一個標識介面。如果類中有可變的引用類型成員變數,則該變數需要實現Serializable介面。本實例採用將對象寫入本地文件的方式完成序列化。
***
Example05:深克隆和序列化的效率比較
- 通過使用這兩種方式克隆100000個對象,並輸出花費的時間來比較這兩種方法的效率。
運行結果:
代碼實現:
import java.io.Serializable;
public class Employee implements Cloneable,Serializable {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年齡:"+age+",");
return sb.toString();
}
@Override
public Employee clone() throws CloneNotSupportedException { //使用父類的clone()方法實現深克隆
Employee employee = (Employee) super.clone();
return employee;
}
}
測試代碼:
import java.io.*;
import java.util.ArrayList;
import java.util.List;
class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
List<Employee> employees = new ArrayList<Employee>(); //創建列表保存對象
Employee employee = new Employee("azw", 20); //創建對象
long currentTime = System.currentTimeMillis(); //獲得當前系統時間
//使用克隆方式獲得對象
for (int i = 0;i<100000;i++){
employees.add(employee.clone());
}
System.out.println("克隆花費的時間:"+(System.currentTimeMillis()-currentTime)+"毫秒");
currentTime = System.currentTimeMillis(); //獲得當前系統時間
for (int i = 0;i<100000;i++){
ByteArrayOutputStream bout = new ByteArrayOutputStream(); //創建位元組數組輸出流
ObjectOutputStream out = new ObjectOutputStream(bout); //創建對象輸出流
out.writeObject(employee); //將對象寫入到輸出流中
//獲得位元組輸出流內容
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin); //創建對象輸入流
employees.add((Employee) in.readObject()); //讀取對象
}
System.out.println("序列化花費的時間:"+(System.currentTimeMillis()-currentTime)+"毫秒");
}
}
要點:使用ByteArrayOutputStream和ByteArrayInputStream可以將對象保存在記憶體中,這樣就不必產生一個本地文件來完成序列化的功能。
***
假克隆、淺克隆和深克隆的應用範圍
假克隆 | 基本數據類型 |
---|---|
淺克隆 | 基本數據類型、不可變引用類型 |
深克隆 | 可變引用類型 |