Menu

Sao chép đối tượng (clone) trong Java

Sao chép đối tượng (cloning) là một vấn đề khá quan trọng đối với lập trình viên JAVA. Mới đây thôi, trong dự án của công ty, chúng tôi cũng phải đối mặt với một vấn đề và chúng tôi giải quyết nó bằng việc sử dụng phương thức clone. Post này tôi sẽ lược dịch các nội dung liên quan đến phương thức clone mà tôi cho là thú vị và hữu ích.

Trước hết thì clone là gì? Rất đơn giản: clone là việc tạo ra một đối tượng mới có nội dung giống hết đối tượng được clone.

Phương thức clone() trong Java là gì

Phương thức clone được định nghĩa trong lớp Object. Phương thực này được sử dụng để tạo ra một đối tượng giống hệt đối tượng được sao chép. Nghĩa là, nó tạo ra một đối tượng mới và sao chép toàn bộ dữ liệu của đối tượng ban đầu.

Hãy xem đoạn code sau:

protected native Object clone() throws CloneNotSupportedException;

Sao chép (cloning) trong Java là gì?

Khi nào chúng ta nghĩ tới khái niệm Sao chép trong Java? Là khi chúng ta có một đối tượng với nhiều thuộc tính và chúng ta muốn tạo một đối tượng mới có cùng thuộc tính và giá trị. Một số người vẫn hiểu nhầm giữa sao chép đối tượng và phép gán (=). Java không cung cấp bất kỳ phép (toán tử) sao chép một đối tượng. Nếu bạn sử dụng phép gán, nghĩa là bạn sao chép một biến tham chiếu, không phải là tạo ra một đối tượng mới.

Hãy xem đoạn code dưới đây

class Student
{
int code;
String name;
}
public class Data
{
public static void main(String[] args)
{
Student object1 = new Student();
object1.name = "Ravi";
object1.code = 1;
Student object2 = object1;
// Changing the value in object2
object2.name = "Ram";
object2.code = 2;
System.out.println("Printing values from Object1:");
System.out.println("Name:"+object1.name + " code:"+ object1.code);
System.out.println("Printing values from Object2:");
System.out.println("Name:"+object2.name + " code:"+ object2.code);
}
}

Kết quả khi chạy:

Output: Printing values from Object1:
Name:Ram Code:2
Printing values from Object2:
Name:Ram Code:2

Như bạn có thể thấy, cả 2 đối tượng (object1 và object2) đều có cùng giá trị. Bởi vì chúng cùng trỏ tới một đối tượng.

Ta có thể giải quyết vấn đề trên bằng cách cài đặt phương thức clone. Nó sẽ tạo ra một đối tượng mới và cả 2 đối tượng sẽ độc lập với nhau (nghĩa là việc thay đổi các giá trị trong đối tượng này không ảnh hưởng đến đối tượng kia và ngược lại).

class Student implements Cloneable
{
int code;
String name;
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
public class Data
{
public static void main(String[] args) throws CloneNotSupportedException
{
Student object1 = new Student();
object1.name = "Ravi";
object1.code = 1;
Student object2 = (Student) object1.clone();
System.out.println("Printing values from Object1:");
System.out.println("Name:"+object1.name + " Code:"+ object1.code);
System.out.println("Printing values from Object2:");
System.out.println("Name:"+object2.name + " Code:"+ object2.code);
// Changing the value in object2
object2.name = "Ram";
object2.code = 2;
System.out.println("After changes in values");
System.out.println("Printing values from Object1:");
System.out.println("Name:"+object1.name + " Code:"+ object1.code);
System.out.println("Printing values from Object2:");
System.out.println("Name:"+object2.name + " Code:"+ object2.code);
}
}

Kết quả khi chạy: 

Output: Printing values from Object1: Name:Ravi Code:1
Printing values from Object2:Name:Ravi Code:1
After changes in values
Printing values from Object1: Name:Ravi Code:1
Printing values from Object2: Name:Ram Code:2

Phương thức clone() tạo ra đối mới và sao chép các trường dữ liệu của đối tượng cũ. Tuy nhiên, việc sao chép này chỉ áp dụng cho các trường dữ liệu nguyên thuỷ, các trường dữ liệu tham chiếu của đối tượng mới vẫn trỏ đến các vùng dữ liệu tương ứng của đối tượng cũ. Chúng ta gọi đây là sao chép nông (shallow copy).

Sử dụng phương thức clone() như thế nào?

  1. Trong Java, nếu bạn muốn sử dụng sao chép, bạn phải ghi đè phương thức clone() trong lớp mà bạn tạo ra.
  2. Để sử dụng phương thức clone, bạn phải cài đặt giao diện Cloneable. Nếu không cài đặt giao diện, JVM sẽ ném ra ngoại lệ CloneNoSupportedException.
  3. Khi bạn cài đặt phương thức clone(), bạn phải gọi đến phương thức clone của lớp cha. 

Bạn cần chú ý những điều quan trọng sau đây:

  1. Đối tượng trả về từ phương thức clone() là giống hệt đối tượng cũ nhưng sẽ trỏ đến vùng nhờ khác nhau trong HEAP. Do đó, phép so sanh sau sẽ có kết quả là false
    // It returns false, because both are different object in heap memory
    a.clone() == a;
  2. Dữ liệu bên trong đối tượng mới được tạo ra sẽ giống hệt đối tượng cũ. Do đó, phương thức so sánh giữa 2 đối tượng sẽ trả về kết quả là true.
    // It returns true, because both object contains same values
    x.clone().equals(x);

Phương thức clone() làm việc như thế nào?

Mặc định, lớp Object cài đặt nội dung của phương thức clone().

Bước 1: JVM kiểm tra xem lớp của bạn đã cài đặt giao diện Cloneable chưa? Nếu chưa, sẽ có ngoại lệ CloneNotSupportedException được ném ra.

Bước 2: JVM sẽ kiểm tra các ngoại lệ khác, cái mà cần phải xử lý trong quá trình sao chép.

Bước 3: Phương thức clone sẽ tạo ra một bản sao (shallow copy) và sau đó sao chép tất cả nội dung từ đối tượng cũ sang đối tượng mới được tạo.

Mặc định, lớp Object cài đặt nội dung của phương thức clone().

JVM làm việc thế nào với phương thức sao chép mặc định?

JVM gọi phương thức sao chép (shallow cloning), nghĩa là nó thực hiện các bước sau:

  1. Nếu lớp chỉ chứa các dữ liệu nguyên thuỷ, thì nó sẽ sao chép hết các dữ liệu trong đối tượng cũ sang đối tượng mới và trả về một tham chiếu đến đối tượng vừa được tạo.
  2. Nếu lớp chứa các dữ liệu là đối tượng của lớp khác, thì nó tạo ra đối tượng mới và một tham chiếu đến đối tượng vừa tạo. Nhưng các thành phần trong nó sẽ không được sao chép mà nó chỉ sao chép các tham chiếu của các thành phần (nghĩa là các thành phần bên trong đối tượng cũ và mới đều trỏ đến cùng một vùng bộ nhớ).

Các loại sao chép

Có 2 loại sao chép là:

  • Sao chép nông
  • Sao chép sâu.

Mình sẽ viết thêm các bài viết về 2 chủ đề này sau.

Các lợi ích của việc sao chép

Sau đây là một số lợi ích của việc so chép đối tượng

  1. Nếu muốn tạo một đối tượng mới với nội dung giống hết cái cũ thì bạn không cần phải viết lại code, nghĩa là tránh được trùng lặp code. Bạn chỉ cần cài đặt giao diện Cloneable và ghi đè phương thức clone() trong lớp của bạn.
  2. Nếu bạn là người bảo trì code cũ, bạn chỉ cần copy đối tượng cũ và thực hiện các thay đổi mong muốn. Sao chép là cách đơn giản và hiệu quả nhất
  3. Sao chép là cách nhất để sao chép một mảng
  4. Hiệu quả hơn so với các constructor sao chép. Trong constructor sao chép, bạn phải copy tất cả dữ liệu một cách rõ ràng. Nghĩa là bạn phải gán lại tất cả dữ liệu (các trường). Nhưng với việc sử dụng phương thức clone(), bạn có thể thực hiện việc sao chép với vài dòng code.

Tạm kết

Theo tôi thì đây là chủ đề đơn giản nhưng khá thú vị. Việc sử dụng phương thức clone sẽ làm cho code của bạn trở lên dễ đọc hơn. Bạn có thể đọc bài viết gốc tại đây.

Không có nhận xét nào:

Đăng nhận xét