Dziedziczenie w Javie, Część 2: Obiekt i jego metody

Java udostępnia standardową bibliotekę klas składającą się z tysięcy klas i innych typów referencyjnych. Pomimo różnic w ich możliwościach, typy te tworzą jedną ogromną hierarchię dziedziczenia poprzez bezpośrednie lub pośrednie rozszerzenie Objectklasy. Dotyczy to również wszystkich klas i innych tworzonych typów odwołań.

W pierwszej połowie tego samouczka poświęconego dziedziczeniu w języku Java przedstawiono podstawy dziedziczenia, w szczególności sposób używania języka Java  extendsi supersłów kluczowych do wyprowadzania klasy potomnej z klasy nadrzędnej, wywoływania konstruktorów i metod klas nadrzędnych, zastępowania metod i nie tylko. Teraz będziemy zwrócić naszą uwagę na Mothership hierarchii dziedziczenia klasy Java java.lang.Object.

Studiowanie Objecti jego metody pomogą Ci lepiej zrozumieć dziedziczenie i jego działanie w programach Java. Znajomość tych metod pomoże ci ogólnie zrozumieć programy Java. 

pobierz Pobierz kod Pobierz kod źródłowy na przykład aplikacje w tym samouczku. Stworzone przez Jeffa Friesena dla JavaWorld.

Obiekt: nadklasa Javy

Objectjest klasą główną lub ostateczną nadklasą wszystkich innych klas Java. Przechowywany w java.langpakiecie Objectdeklaruje następujące metody, które dziedziczą wszystkie inne klasy:

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

Klasa Java dziedziczy te metody i może przesłonić każdą niezadeklarowaną metodę final. Na przykład finaltoString()metodę niebędącą metodą można przesłonić, podczas gdy finalwait()metody nie.

Przyjrzymy się każdej z tych metod i temu, jak umożliwiają one wykonywanie specjalnych zadań w kontekście klas Java. Najpierw rozważmy podstawowe zasady i mechanizmy Objectdziedziczenia.

Typy ogólne

Na powyższej liście mogłeś zauważyć getClass(), którego Classzwracany typ jest przykładem typu ogólnego . Omówię typy ogólne w przyszłym artykule.

Rozszerzanie obiektu: przykład

Klasa może jawnie rozszerzać Object, jak pokazano na liście 1.

Listing 1. Jawne rozszerzenie Object

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

Ponieważ możesz rozszerzyć co najwyżej jedną inną klasę (przypominając sobie z części 1, że Java nie obsługuje dziedziczenia wielokrotnego opartego na klasach), nie musisz jawnie rozszerzać Object; w przeciwnym razie nie można rozszerzyć żadnej innej klasy. W związku z tym należy rozszerzyć Objectniejawnie, jak pokazano na liście 2.

Listing 2. Niejawne rozszerzenie Object

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

Skompiluj Listing 1 lub Listing 2 w następujący sposób:

javac Employee.java

Uruchom wynikową aplikację:

java Employee

Należy zwrócić uwagę na następujące dane wyjściowe:

John Doe

Dowiedz się więcej o klasie: getClass ()

getClass()Metoda zwraca klasę wykonania dowolnego obiektu, na którym jest nazywany. Klasy wykonawcze jest reprezentowany przez Classobiekt, który znajduje się w java.langopakowaniu. Classto punkt wejścia do Java Reflection API, o którym dowiesz się, gdy przejdziemy do bardziej zaawansowanych tematów programowania w języku Java. Na razie wiedz, że aplikacja Java wykorzystuje Classi resztę interfejsu API Java Reflection do poznania własnej struktury.

Obiekty klas i statyczne metody synchronizowane

Zwracany Classobiekt to obiekt, który jest zablokowany static synchronizedmetodami reprezentowanej klasy; na przykład static synchronized void foo() {}. (Synchronizację Java przedstawię w przyszłym samouczku).

Powielanie obiektów: clone ()

clone()Metoda tworzy i zwraca kopię obiektu, na którym to się nazywa. Ponieważ clone()zwracanym typem jest Object, odwołanie do obiektu, które clone()zwraca, musi być rzutowane na rzeczywisty typ obiektu przed przypisaniem tego odniesienia do zmiennej typu obiektu. Listing 3 przedstawia aplikację, która demonstruje klonowanie.

Listing 3. Klonowanie obiektu

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 z listingu 3 implementuje Cloneableinterfejs, który znajduje się w java.langpakiecie. Cloneablerealizowany jest przez klasę (przez implementshasła) zapobieganie Objectjest clone()sposób wyrzucanie z instancją CloneNotSupportedExceptionklasy (również w java.lang).

CloneDemodeklaruje jedno- intbazowe pole wystąpienia o nazwie xi main()metodę, która wykonuje tę klasę. main()jest zadeklarowana z throwsklauzulą, która przekazuje CloneNotSupportedExceptionstos wywołań metody.

main()Pierwsze wystąpienie CloneDemoi inicjuje kopiowania wynikowej instancji z dnia xna 5. Następnie wyprowadza wartość wystąpienia xi wywołuje clone()to wystąpienie, rzucając zwrócony obiekt CloneDemoprzed zapisaniem jego odwołania. Na koniec wyprowadza xwartość pola klona .

Skompiluj Listing 3 ( javac CloneDemo.java) i uruchom aplikację ( java CloneDemo). Należy zwrócić uwagę na następujące dane wyjściowe:

cd.x = 5 cd2.x = 5

Zastępowanie clone ()

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.
  • Metoda nadpisująca clone()nie zgłasza CloneNotSupportedException. Ten wyjątek jest tylko z Object„s clone()sposobu, który nie jest wywoływany. W związku z tym wyjątek nie musi być obsługiwany ani przekazywany w górę stosu wywołań metody za pośrednictwem klauzuli throws.
  • Object„s clone()metoda nie jest wywoływany (nie ma super.clone()połączenia) bo płytkie kopiowanie nie jest wymagana dla Addressklasy - istnieje tylko jedno pole do skopiowania.