Clone method trong Java

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

https://www.geeksforgeeks.org/clone-method-in-java-2/

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x