Nasljeđivanje u Javi, 2. dio: Objekt i njegove metode

Java nudi standardnu ​​knjižnicu klasa koja se sastoji od tisuća klasa i drugih referentnih tipova. Unatoč razlikama u svojim mogućnostima, ove vrste čine jednu masivnu hijerarhiju nasljeđivanja izravnim ili neizravnim proširivanjem Objectklase. To vrijedi i za sve klase i druge referentne vrste koje izradite.

Prva polovica ovog vodiča o nasljeđivanju Java pokazala vam je osnove nasljeđivanja, posebno kako koristiti Javu  extendsi superključne riječi za izvođenje podređene klase iz roditeljske klase, pozivanje konstruktora i metoda nadređene klase, nadjačavanje metoda i još mnogo toga. Sada ćemo okrenuti naš fokus na matični hijerarhije nasljeđivanja Java klase, java.lang.Object.

Proučavanje Objecti njegove metode pomoći će vam da steknete funkcionalnije razumijevanje nasljeđivanja i kako to funkcionira u vašim Java programima. Upoznavanje s tim metodama pomoći će vam općenito da shvatite više Java programa. 

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere aplikacija u ovom vodiču. Stvorio Jeff Friesen za JavaWorld.

Predmet: Javina superklasa

Objectje korijenska klasa ili krajnja superklasa svih ostalih Java klasa. Pohranjeni u java.langpaketu, Objectizjavljuju sljedeće metode koje nasljeđuju sve ostale klase:

  • 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)

Java klasa nasljeđuje ove metode i može nadjačati bilo koju metodu koja nije deklarirana final. Na primjer, ne- finaltoString()metodu je moguće poništiti, dok finalwait()metode ne mogu.

Razmotrit ćemo svaku od ovih metoda i kako vam omogućuju izvršavanje posebnih zadataka u kontekstu vaših Java satova. Prvo, razmotrimo osnovna pravila i mehanizme Objectnasljeđivanja.

Generički tipovi

Na gornjem ste popisu možda primijetili getClass()čiji je Classtip povrata primjer generičkog tipa . U sljedećem ću članku razgovarati o generičkim vrstama.

Proširivanje objekta: Primjer

Klasa se može eksplicitno proširiti Object, kao što je prikazano u Popisu 1.

Popis 1. Objekt koji se eksplicitno proširuje

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()); } }

Budući da možete proširiti najviše jednu drugu klasu (podsjetite iz dijela 1 da Java ne podržava višestruko nasljeđivanje na temelju klasa), niste prisiljeni eksplicitno proširiti Object; inače ne biste mogli produžiti nijedan drugi razred. Stoga biste produžili Objectimplicitno, kao što je prikazano u popisu 2.

Popis 2. Objekt koji se implicitno proširuje

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()); } }

Sastavite popis 1 ili popis 2 kako slijedi:

javac Employee.java

Pokrenite rezultirajuću aplikaciju:

java Employee

Trebali biste promatrati sljedeći rezultat:

John Doe

Doznajte o predavanju: getClass ()

getClass()Metoda vraća runtime klasu bilo koji objekt na koji se ona zove. Izvođenja klasa predstavljena je Classobjekt, koji se nalazi u java.langpaketu. Classje ulazna točka u Java Reflection API, o kojem ćete naučiti kad ulazimo u naprednije teme u Java programiranju. Za sada znajte da Java aplikacija koristi Classi ostatak Java Reflection API-ja za učenje o vlastitoj strukturi.

Objekti klase i statičke sinkronizirane metode

Vraćeni Classobjekt je objekt koji je zaključan static synchronizedmetodama predstavljene klase; npr static synchronized void foo() {}. (Uvest ću Java sinkronizaciju u sljedećem vodiču.)

Dupliciranje objekata: clone ()

clone()Način stvara i vraća kopiju objekta na koji se to zove. Budući da je clone()'return type' Object, referenca na objekt koja se clone()vraća mora biti prebačena na stvarni tip objekta prije dodjeljivanja te reference varijabli tipa objekta. Popis 3 predstavlja aplikaciju koja pokazuje kloniranje.

Popis 3. Kloniranje objekta

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); } }

CloneDemoKlasa Listing 3 implementira Cloneablesučelje koje se nalazi u java.langpaketu. Cloneableimplementira klasa (putem implementsključne riječi) kako bi se spriječilo Objectda clone()metoda metode baci instancu CloneNotSupportedExceptionklase (također pronađena u java.lang).

CloneDemodeklarira jedno- intosnovano polje instance imenovano xi main()metodu koja vježba ovu klasu. main()se deklarira throwsklauzulom koja prolazi CloneNotSupportedExceptionniz stek poziva-metoda.

main()prvo instancira CloneDemoi inicijalizira rezultirajuću kopiju instance xna 5. Zatim izbacuje vrijednost instance xi poziva je clone()na ovu instancu, lijevajući vraćeni objekt CloneDemoprije spremanja njegove reference. Konačno, on daje xvrijednost polja klona .

Sastavite popis 3 ( javac CloneDemo.java) i pokrenite aplikaciju ( java CloneDemo). Trebali biste promatrati sljedeći rezultat:

cd.x = 5 cd2.x = 5

Nadjačavanje klona ()

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.
  • Prevladavajuća clone()metoda ne baca CloneNotSupportedException. Ova se iznimka izbacuje samo iz Object' clone()metode koja se ne poziva. Stoga se s iznimkom ne mora rukovati niti joj se prosljeđuje niz poziva metoda putem klauzule throws.
  • Object„S clone()metoda ne zove (nema super.clone()poziva), jer plitko kopiranje nije potrebna za Addressklasu - postoji samo jedno polje za kopiranje.