Sao chép object là một khái niệm trong đó chúng ta sẽ tạo mới một object cùng kiểu dữ liệu với object hiện tại và khởi tạo tất cả các giá trị của những thuộc tính bên trong nó giống với các giá trị bên trong object hiện tại. Nói đơn giản chúng ta sẽ 2 object riêng biệt nhưng giá trị các trường dữ liệu bên trong chúng là hoàn toàn giống nhau.
Reference copy
Trong Java chúng ta không có một toán tử nào cho phép tạo ra một bản sao của một object. Nếu bạn sử dụng toán tử = để gán một một object cho một biến khác thì đơn giản nó chỉ sao chép một tham chiếu (địa chỉ đến object) chứ không phải là sao chép một object.
import java.io.*; // A test class whose objects are cloned class Test { int x, y; Test() { x = 10; y = 20; } } // Driver Class class Main { public static void main(String[] args) { Test ob1 = new Test(); System.out.println(ob1.x + " " + ob1.y); // Khởi tạo một biến ob2 tham chiếu đến object ob1 // ob2 và ob1 giờ đây cùng trỏ đến cùng một địa chỉ trong heap chứ object Test ob2 = ob1; // Bất kỳ thay đổi nào trên o2 sẽ ảnh hưởng đến o1 ob2.x = 100; System.out.println(ob1.x+" "+ob1.y); System.out.println(ob2.x+" "+ob2.y); } }
Output
10 20 100 20 100 20
Method clone() trong Java
Trong Java, việc copy một object sử dụng clone() method được gọi là Shallow Cloning. Đây là quá trình sao chép mặc định trong Java cho phép tạo ra một bản sao của một đối tượng có giá trị của từng trường giống nhau hoàn toàn. Tuy nhiên trong trường hợp giá trị của một trường là một tham chiếu đến một object khác thì clone() method chỉ tạo ra một bản sao tham chiếu đó.
import java.util.ArrayList; // An object reference of this class is // contained by Test2 class Test { int x, y; } // Contains a reference of Test and implements // clone with shallow copy. class Test2 implements Cloneable { int a; int b; Test c = new Test(); public Object clone() throws CloneNotSupportedException { return super.clone(); } } // Driver class public class Main { public static void main(String args[]) throws CloneNotSupportedException { Test2 t1 = new Test2(); t1.a = 10; t1.b = 20; t1.c.x = 30; t1.c.y = 40; Test2 t2 = (Test2)t1.clone(); // Creating a copy of object t1 and passing // it to t2 t2.a = 100; // Change in primitive type of t2 will not // be reflected in t1 field t2.c.x = 300; // Change in object type field will be // reflected in both t2 and t1(shallow copy) System.out.println(t1.a + " " + t1.b + " " + t1.c.x + " " + t1.c.y); System.out.println(t2.a + " " + t2.b + " " + t2.c.x + " " + t2.c.y); } }
Output
10 20 300 40 100 20 300 40
Chúng ta có thể thấy rằng trong t1 Object có 2 trường a, b là kiểu dữ liệu nguyên thủy và một trường c là một tham chiếu đến một Test Object. Do vậy trong bản sao t2 của t1, 2 trường a,b chứa giá trị mới tương ứng với t1, nhưng trường c chỉ là một bản sao chép tham chiếu đến một Test Object duy nhất. Do vậy khi một trong 2 t1 hoặc t2 chỉnh sửa giá trị trong Test Object đều sẽ ảnh hưởng đến cả 2.
Các bạn lưu ý để triển khai clone() method chúng ta cần implement Cloneable và sử dụng supper.clone() để triển khai trình mặc định của clone() method.
Deep copy
Shallow copy có thể trong một vài trường hợp sẽ không đủ nếu như chúng ta muốn sao chép theo chiều sâu, không muốn sao chép các tham chiếu. Để làm đươc điều này, mình sẽ override lại clone() method và thực hiện công việc tạo một một Test Object mới thay vì sao chép tham chiếu như mặc định.
import java.util.ArrayList; // An object reference of this class is // contained by Test2 class Test { int x, y; } // Contains a reference of Test and implements // clone with deep copy. class Test2 implements Cloneable { int a, b; Test c = new Test(); public Object clone() throws CloneNotSupportedException { // Assign the shallow copy to new reference variable t Test2 t = (Test2)super.clone(); t.c = new Test(); // Create a new object for the field c // and assign it to shallow copy obtained, // to make it a deep copy return t; } } public class Main { public static void main(String args[]) throws CloneNotSupportedException { Test2 t1 = new Test2(); t1.a = 10; t1.b = 20; t1.c.x = 30; t1.c.y = 40; Test2 t3 = (Test2)t1.clone(); t3.a = 100; // Change in primitive type of t2 will not // be reflected in t1 field t3.c.x = 300; // Change in object type field of t2 will not // be reflected in t1(deep copy) System.out.println(t1.a + " " + t1.b + " " + t1.c.x + " " + t1.c.y); System.out.println(t3.a + " " + t3.b + " " + t3.c.x + " " + t3.c.y); } }
Output
10 20 30 40 100 20 300 0
Lưu ý ở trên chỉ là trường hợp đơn giản, và mình chỉ tạo một Test Object mới chứ chưa hề sao chép dữ liệu bên trong. Trong thực tế chúng ta nên tránh việc deep copy vì nó ảnh hưởng nhiều đến hiệu xuất của chương trình. Gỉa sử một object có rất nhiều tầng tham chiếu thì việc deep copy sẽ rất vất vã.
Nguồn tham khảo