Daily Archives: 10/08/2017

Java 8 – Lớp Object

Lớp Object trong gói java.lang là lớp nằm tại gốc của cây phân cấp lớp. Tất cả các lớp khác được tạo ra đều là hậu duệ trực tiếp hoặc gián tiếp của lớp Object. Và do đó sẽ kế thừa tất cả các phương thức của lớp Object, mặc dù chúng ta cũng ít khi dùng các phương thức của lớp này, tuy nhiên nếu phải dùng thì chúng ta nên override lại các phương thức đó. Một trong các phương thức của lớp Object là:

  • protected Object clone() throws CloneNotSupportedException
    Tạo và trả về một đối tượng bản sao của đối tượng này.
  • public boolean equals(Object obj)
    Cho biết một đối tượng có “bằng” đối tượng này hay không.
  • protected void finalize() throws Throwable
    Trình thu dọn rác sẽ gọi phương thức này khi không còn bất cứ một tham chiếu nào đến đối tượng này.
  • public final Class getClass()
    Lấy lớp của đối tượng.
  • public int hashCode()
    Lấy mã băm của đối tượng
  • public String toString()
    Trả về một chuỗi String mô tả về đối tượng

Ngoài ra còn có các phương thức notify(), notifyAll() và wait() dùng trong các hoạt động đồng bộ luồng chương trình, chúng ta sẽ không tìm hiểu về các phương thức này.

Phương thức clone()

Nếu một lớp hoặc các lớp cha của lớp đó có implement giap diện Cloneable thì chúng ta có thể sử dụng phương thức clone() để tạo ra một bản sao của đối tượng đó, để làm việc này thì chúng ta chỉ cần gọi phương thức clone() là được:

object.clone();

Phương thức này sẽ kiểm tra xem đối tượng gọi phương thức này có implement giao diện Cloneable hay không, nếu không thì sẽ giải phóng lỗi CloneNotSupportedException, chúng ta sẽ tìm hiểu về lỗi exception sau.

Nếu đối tượng có implement giao diện Cloneable thì phương thức clone() sẽ tạo một đối tượng có cùng tên lớp, cùng các thuộc tính và giá trị của đối tượng gốc.

Trong hầu hết các trường hợp thì chúng ta có thể gọi phương thức clone() mà không có bất cứ chuyện gì xảy ra. Tuy nhiên nếu trong đối tượng đó có tham chiếu tới một đối tượng khác thì chúng ta nên override lại phương thức clone(), vì khi chúng ta thay đổi giá trị của đối tượng được tham chiếu thì các đối tượng gốc và đối tượng được tạo ra từ clone() sẽ có cùng giá trị của đối tượng được tham chiếu, tức là đối tượng gốc và đối tượng được tạo ra từ clone() vẫn không thật sự là 2 đối tượng khác nhau 100%.

Phương thức equals()

Phương thức equals() sẽ so sánh xem 2 đối tượng có giống nhau hay không và trả về giá trị true nếu giống.

Cách thức hoạt động của phương thức này sẽ khác nhau tùy theo kiểu dữ liệu, nếu kiểu dữ liệu là các kiểu cơ bản như int, float, String…v.v thì phương thức này sẽ trả về true hoặc false bằng cách đơn giản là so sánh theo toán tử ==. Tuy nhiên khi so sánh 2 đối tượng với nhau thì phương thức này sẽ so sánh địa chỉ tham chiếu của chúng, tức là 2 đối tượng sẽ “bằng nhau” khi chúng cùng tham chiếu vê một địa chỉ bộ nhớ.

Trên thực tế thì chúng ta nên override lại phương thức equals() để nó hoạt động theo như ý chúng ta muốn, và khi override phương thức equals() thì chúng ta cũng phải override phương thức hashCode().

Phương thức finalize()

Lớp Object có một phương thức thuộc dạng callback là finalize(), phương thức này sẽ được gọi khi đối tượng trở thành “rác”. Phương thức finalize() trong lớp Object có công dụng là… chẳng làm gì cả, chúng ta có thể override lại trong các lớp con để thực hiện các công việc dọn dẹp tài nguyên theo ý chúng ta thay vì để việc đó cho trình dọn rác của Java.

Và bản thân phương thức finalize() khi được gọi cũng thực hiện các công việc rất không ổn định, do đó chúng ta cũng không nên quá dựa dẫm vào phương thức này mà nên tự xử lý các công việc dọn dẹp bằng tay, chẳng hạn như đóng file, đóng kết nối mạng…v.v.

Phương thức getClass()

Phương thức getClass() có một điểm đặc biệt là không thể override được.

Phương thức này sẽ trả về một đối tượng Class có chứa các phương thức mà chúng ta có thể sử dụng để lấy thông tin về lớp đó, chẳng hạn như getSimpleName(), getSuperclass(), getInterfaces()…v.v Ví dụ:

void printClassName(Object obj) {
    System.out.println("Class name: " + obj.getClass().getSimpleName());
}

Đoạn code trên sẽ in tên lớp của đối tượng obj bằng cách lấy đối tượng Class từ phương thức getClass(), sau đó lấy tên lớp thông qua phương thức getSimpleName(). Lớp Class trong gói java.lang chứa khoảng hơn 50 phương thức hỗ trợ chúng ta lấy gần như toàn bộ thông tin về một lớp cụ thể.

Phương thức hashCode()

Phương thức hashCode() sẽ trả về mã băm của một đối tượng, đó là địa chỉ bộ nhớ của đối tượng đó trong RAM, lưu theo dạng số thập lục phân.

Như bình thường thì khi 2 đối tượng “bằng nhau” thì mã băm của chúng cũng bằng nhau, khi chúng ta thay đổi phương thức equals() thì chúng ta cũng nên thay đôi phương thức hashCode(), bởi vì phương thức equals() sẽ lấy giá trị từ hashCode() và so sánh 2 đối tượng với nhau.

Phương thức toString()

Hầu như chúng ta sẽ luôn luôn cần override phương thức toString().

Phương thức toString() sẽ trả về một đối tượng String chứa chuỗi mô tả về đối tượng đó, đây là phương thức rất hữu ích khi debug. Chuỗi mô tả của đối tượng nào thì sẽ mô tả đối tượng đó, do dó thường chúng ta sẽ phải override phương thức toString() để cho ra chuỗi theo ý chúng ta.

Thông thường chúng ta có thể truyền giá trị của toString() vào trong phương thức System.out.println() luôn.

System.out.println(obj.toString());