Warisan di Jawa, Bagian 2: Objek dan metodenya

Java menyediakan pustaka kelas standar yang terdiri dari ribuan kelas dan tipe referensi lainnya. Terlepas dari perbedaan kemampuan mereka, tipe ini membentuk satu hierarki pewarisan besar-besaran dengan secara langsung atau tidak langsung memperluas Objectkelas. Ini juga berlaku untuk semua kelas dan jenis referensi lain yang Anda buat.

Paruh pertama dari tutorial ini tentang pewarisan Java menunjukkan kepada Anda dasar-dasar pewarisan, khususnya cara menggunakan Java  extendsdan superkata kunci untuk mendapatkan kelas anak dari kelas induk, memanggil konstruktor dan metode kelas induk, mengganti metode, dan banyak lagi. Sekarang, kita akan mengalihkan fokus kita ke induk dari hierarki pewarisan kelas Java java.lang.Object,.

Mempelajari Objectdan metodenya akan membantu Anda mendapatkan pemahaman yang lebih fungsional tentang pewarisan dan cara kerjanya di program Java Anda. Mengenal metode tersebut akan membantu Anda memahami program Java secara umum. 

unduh Dapatkan kodenya Unduh kode sumber untuk aplikasi contoh dalam tutorial ini. Dibuat oleh Jeff Friesen untuk JavaWorld.

Objek: superclass Java

Objectadalah kelas root, atau superclass ultimate, dari semua kelas Java lainnya. Tersimpan dalam java.langpaket, Objectmendeklarasikan metode berikut, yang diwarisi oleh semua kelas lainnya:

  • protected Object clone()
  • boolean equals(Object obj)
  • protected void finalize()
  • Class getClass()
  • int hashCode()
  • void notify()
  • void notifyAll()
  • String toString()
  • void wait()
  • void wait(long timeout)
  • void wait(long timeout, int nanos)

Kelas Java mewarisi metode ini dan dapat mengganti metode apa pun yang tidak dideklarasikan final. Misalnya, non- finaltoString()metode dapat diganti, sedangkan finalwait()metode tidak bisa.

Kami akan melihat masing-masing metode ini dan bagaimana metode tersebut memungkinkan Anda untuk melakukan tugas khusus dalam konteks kelas Java Anda. Pertama, mari pertimbangkan aturan dasar dan mekanisme Objectpewarisan.

Jenis generik

Dalam daftar di atas, Anda mungkin telah memperhatikan getClass(), yang Classtipe kembaliannya adalah contoh tipe generik . Saya akan membahas tipe generik di artikel mendatang.

Memperluas Objek: Contoh

Sebuah kelas dapat diperluas secara eksplisit Object, seperti yang ditunjukkan pada Listing 1.

Kode 1. Memperluas Objek secara eksplisit

public class Employee extends Object { private String name; public Employee(String name) { this.name = name; } public String getName() { return name; } public static void main(String[] args) { Employee emp = new Employee("John Doe"); System.out.println(emp.getName()); } }

Karena Anda dapat memperluas paling banyak satu kelas lain (ingat dari Bagian 1 bahwa Java tidak mendukung warisan berganda berbasis kelas), Anda tidak dipaksa untuk memperluas secara eksplisit Object; jika tidak, Anda tidak dapat memperluas kelas lain. Oleh karena itu, Anda akan memperluas Objectsecara implisit, seperti yang ditunjukkan pada Listing 2.

Kode 2. Secara implisit memperluas Objek

public class Employee { private String name; public Employee(String name) { this.name = name; } public String getName() { return name; } public static void main(String[] args) { Employee emp = new Employee("John Doe"); System.out.println(emp.getName()); } }

Susun Daftar 1 atau Daftar 2 sebagai berikut:

javac Employee.java

Jalankan aplikasi yang dihasilkan:

java Employee

Anda harus mengamati keluaran berikut:

John Doe

Cari tahu tentang kelas: getClass ()

The getClass()Metode mengembalikan kelas runtime dari setiap objek yang disebut. Kelas runtime diwakili oleh sebuah Classobjek, yang ditemukan dalam java.langpaket. Classadalah titik masuk ke dalam Java Reflection API, yang akan Anda pelajari saat kita membahas topik yang lebih canggih dalam pemrograman Java. Untuk saat ini, ketahuilah bahwa aplikasi Java menggunakan Classdan Java Reflection API lainnya untuk mempelajari strukturnya sendiri.

Objek kelas dan metode tersinkronisasi statis

ClassObjek yang dikembalikan adalah objek yang dikunci oleh static synchronizedmetode kelas yang diwakili; misalnya static synchronized void foo() {},. (Saya akan memperkenalkan sinkronisasi Java di tutorial mendatang.)

Objek duplikat: clone ()

The clone()Metode menciptakan dan mengembalikan salinan dari objek yang itu disebut. Karena clone()tipe pengembaliannya adalah Object, referensi objek yang clone()dikembalikan harus dilemparkan ke tipe objek sebenarnya sebelum menetapkan referensi itu ke variabel tipe objek. Kode 3 menyajikan aplikasi yang menunjukkan kloning.

Kode 3. Kloning objek

class CloneDemo implements Cloneable { int x; public static void main(String[] args) throws CloneNotSupportedException { CloneDemo cd = new CloneDemo(); cd.x = 5; System.out.println("cd.x = " + cd.x); CloneDemo cd2 = (CloneDemo) cd.clone(); System.out.println("cd2.x = " + cd2.x); } }

CloneDemoKelas Listing 3 mengimplementasikan Cloneableantarmuka, yang ditemukan dalam java.langpaket. Cloneablediimplementasikan oleh kelas (melalui implementskata kunci) untuk mencegah Object's clone()metode dari melemparkan sebuah instance dari CloneNotSupportedExceptionkelas (juga ditemukan di java.lang).

CloneDemomendeklarasikan intkolom instance berbasis tunggal bernama xdan main()metode yang melatih kelas ini. main()dideklarasikan dengan throwsklausa yang melewatkan CloneNotSupportedExceptiontumpukan metode-panggilan.

main()pertama membuat CloneDemodan menginisialisasi salinan instance yang dihasilkan dari xto 5. Ini kemudian mengeluarkan nilai instance xdan memanggil clone()instance ini, mentransmisikan objek yang dikembalikan CloneDemosebelum menyimpan referensinya. Akhirnya, ini mengeluarkan nilai xbidang klon .

Kompilasi Listing 3 ( javac CloneDemo.java) dan jalankan application ( java CloneDemo). Anda harus mengamati keluaran berikut:

cd.x = 5 cd2.x = 5

Mengganti klon ()

The previous example didn't need to override clone() because the code that calls clone() is located in the class being cloned (CloneDemo). If the call to clone() were located in a different class, however, then you would need to override clone(). Because clone() is declared protected, you would receive a "clone has protected access in Object" message if you didn't override it before compiling the class. Listing 4 presents a refactored Listing 3 that demonstrates overriding clone().

Listing 4. Cloning an object from another class

class Data implements Cloneable { int x; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } class CloneDemo { public static void main(String[] args) throws CloneNotSupportedException { Data data = new Data(); data.x = 5; System.out.println("data.x = " + data.x); Data data2 = (Data) data.clone(); System.out.println("data2.x = " + data2.x); } }

Listing 4 declares a Data class whose instances are to be cloned. Data implements the Cloneable interface to prevent a CloneNotSupportedException from being thrown when the clone() method is called. It then declares int-based instance field x, and overrides the clone() method. The clone() method executes super.clone() to call its superclass's (that is, Object's) clone() method. The overriding clone() method identifies CloneNotSupportedException in its throws clause.

Listing 4 also declares a CloneDemo class that: instantiates Data, initializes its instance field, outputs the value of the instance field, clones the Data object, and outputs its instance field value.

Compile Listing 4 (javac CloneDemo.java) and run the application (java CloneDemo). You should observe the following output:

data.x = 5 data2.x = 5

Shallow cloning

Shallow cloning (also known as shallow copying) refers to duplicating an object's fields without duplicating any objects that are referenced from that object's reference fields (if there are any reference fields). Listings 3 and 4 actually demonstrated shallow cloning. Each of the cd-, cd2-, data-, and data2-referenced fields identifies an object that has its own copy of the int-based x field.

Shallow cloning works well when all fields are of the primitive type and (in many cases) when any reference fields refer to immutable (unchangeable) objects. However, if any referenced objects are mutable, changes made to any one of these objects can be seen by the original object and its clone(s). Listing 5 demonstrates.

Listing 5. The problem with shallow cloning in a reference field context

class Employee implements Cloneable { private String name; private int age; private Address address; Employee(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } Address getAddress() { return address; } String getName() { return name; } int getAge() { return age; } } class Address { private String city; Address(String city) { this.city = city; } String getCity() { return city; } void setCity(String city) { this.city = city; } } class CloneDemo { public static void main(String[] args) throws CloneNotSupportedException { Employee e = new Employee("John Doe", 49, new Address("Denver")); System.out.println(e.getName() + ": " + e.getAge() + ": " + e.getAddress().getCity()); Employee e2 = (Employee) e.clone(); System.out.println(e2.getName() + ": " + e2.getAge() + ": " + e2.getAddress().getCity()); e.getAddress().setCity("Chicago"); System.out.println(e.getName() + ": " + e.getAge() + ": " + e.getAddress().getCity()); System.out.println(e2.getName() + ": " + e2.getAge() + ": " + e2.getAddress().getCity()); } }

Listing 5 presents Employee, Address, and CloneDemo classes. Employee declares name, age, and address fields; and is cloneable. Address declares an address consisting of a city and its instances are mutable. CloneDemo drives the application.

CloneDemo's main() method creates an Employee object and clones this object. It then changes the city's name in the original Employee object's address field. Because both Employee objects reference the same Address object, the changed city is seen by both objects.

Compile Listing 5 (javac CloneDemo.java) and run this application (java CloneDemo). You should observe the following output:

John Doe: 49: Denver John Doe: 49: Denver John Doe: 49: Chicago John Doe: 49: Chicago

Deep cloning

Deep cloning (also known as deep copying) refers to duplicating an object's fields such that any referenced objects are duplicated. Furthermore, the referenced objects of referenced objects are duplicated, and so forth. Listing 6 refactors Listing 5 to demonstrate deep cloning.

Listing 6. Deep cloning the address field

class Employee implements Cloneable { private String name; private int age; private Address address; Employee(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; } @Override public Object clone() throws CloneNotSupportedException { Employee e = (Employee) super.clone(); e.address = (Address) address.clone(); return e; } Address getAddress() { return address; } String getName() { return name; } int getAge() { return age; } } class Address { private String city; Address(String city) { this.city = city; } @Override public Object clone() { return new Address(new String(city)); } String getCity() { return city; } void setCity(String city) { this.city = city; } } class CloneDemo { public static void main(String[] args) throws CloneNotSupportedException { Employee e = new Employee("John Doe", 49, new Address("Denver")); System.out.println(e.getName() + ": " + e.getAge() + ": " + e.getAddress().getCity()); Employee e2 = (Employee) e.clone(); System.out.println(e2.getName() + ": " + e2.getAge() + ": " + e2.getAddress().getCity()); e.getAddress().setCity("Chicago"); System.out.println(e.getName() + ": " + e.getAge() + ": " + e.getAddress().getCity()); System.out.println(e2.getName() + ": " + e2.getAge() + ": " + e2.getAddress().getCity()); } }

Listing 6 shows that Employee's clone() method first calls super.clone(), which shallowly copies the name, age, and address fields. It then calls clone() on the address field to make a duplicate of the referenced Address object. Address overrides the clone() method and reveals a few differences from previous classes that override this method:

  • Address doesn't implement Cloneable. It's not necessary because only Object's clone() method requires that a class implement this interface, and this clone() method isn't being called.
  • clone()Metode penggantian tidak melempar CloneNotSupportedException. Pengecualian ini dilemparkan hanya dari Object's clone()metode, yang tidak disebut. Oleh karena itu, pengecualian tidak harus ditangani atau diteruskan tumpukan metode-panggilan melalui klausa throws.
  • Object's clone()metode tidak disebut (tidak ada super.clone()panggilan) karena dangkal menyalin tidak diperlukan untuk Addresskelas - hanya ada satu bidang untuk menyalin.