Java Language
Metody klasy obiektowej i konstruktor
Szukaj…
Wprowadzenie
Ta strona dokumentacji służy do pokazania szczegółów z przykładem konstruktorów klas Java i metod klasy obiektowej, które są automatycznie dziedziczone z Object
nadklasy każdej nowo utworzonej klasy.
Składnia
- publiczna końcowa natywna klasa <?> getClass ()
- publiczne ostateczne natywne powiadomienie nieważne ()
- public final native void notifyAll ()
- publiczne końcowe natywne oczekiwanie na anulowanie (długi limit czasu) zgłasza InterruptedException
- publiczne końcowe void wait () zgłasza InterruptedException
- publiczne końcowe anulowanie nieważności (długi limit czasu, int nanos) zgłasza InterruptedException
- public native int hashCode ()
- public boolean equals (Object obj)
- public String toString ()
- chroniony natywny Object clone () zgłasza CloneNotSupportedException
- protected void finalize () rzuca Throwable
Metoda toString ()
toString()
wykorzystywana jest do utworzenia String
reprezentacji przedmiotu za pomocą object's treści. Ta metoda powinna zostać zastąpiona podczas pisania zajęć. toString()
jest wywoływana niejawnie, gdy obiekt jest konkatenowany z łańcuchem, jak w "hello " + anObject
.
Rozważ następujące:
public class User {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return firstName + " " + lastName;
}
public static void main(String[] args) {
User user = new User("John", "Doe");
System.out.println(user.toString()); // Prints "John Doe"
}
}
Tutaj toString()
z klasy Object
jest zastępowane w klasie User
aby zapewnić znaczące dane dotyczące obiektu podczas jego drukowania.
Podczas korzystania z println()
toString()
obiektu toString()
jest domyślnie wywoływana. Dlatego te stwierdzenia robią to samo:
System.out.println(user); // toString() is implicitly called on `user`
System.out.println(user.toString());
Jeśli toString()
nie jest zastąpione w wyżej wspomnianej klasie User
, System.out.println(user)
może zwrócić User@659e0bfd
lub podobny ciąg bez prawie żadnych użytecznych informacji oprócz nazwy klasy. Stanie się tak, ponieważ wywołanie użyje implementacji toString()
podstawowej klasy Java Object
która nie wie nic o strukturze klasy User
ani regułach biznesowych. Jeśli chcesz zmienić tę funkcjonalność w swojej klasie, po prostu zastąp metodę.
metoda równa się ()
TL; DR
==
testy równości odniesienia (czy są to ten sam obiekt )
.equals()
sprawdza równość wartości (czy są one logicznie „równe” )
equals()
to metoda używana do porównywania dwóch obiektów pod kątem równości. Domyślna implementacja metody equals()
w klasie Object
zwraca true
, tylko wtedy, gdy oba odwołania wskazują na to samo wystąpienie. Dlatego zachowuje się tak samo jak porównanie przez ==
.
public class Foo {
int field1, field2;
String field3;
public Foo(int i, int j, String k) {
field1 = i;
field2 = j;
field3 = k;
}
public static void main(String[] args) {
Foo foo1 = new Foo(0, 0, "bar");
Foo foo2 = new Foo(0, 0, "bar");
System.out.println(foo1.equals(foo2)); // prints false
}
}
Mimo że foo1
i foo2
są tworzone z tymi samymi polami, wskazują one na dwa różne obiekty w pamięci. Domyślna implementacja equals()
zatem wartość false
.
Aby porównać zawartość obiektu pod kątem równości, należy zastąpić equals()
.
public class Foo {
int field1, field2;
String field3;
public Foo(int i, int j, String k) {
field1 = i;
field2 = j;
field3 = k;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 &&
field2 == f.field2 &&
(field3 == null ? f.field3 == null : field3.equals(f.field3));
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + this.field1;
hash = 31 * hash + this.field2;
hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
return hash;
}
public static void main(String[] args) {
Foo foo1 = new Foo(0, 0, "bar");
Foo foo2 = new Foo(0, 0, "bar");
System.out.println(foo1.equals(foo2)); // prints true
}
}
W tym przypadku przesłonięta metoda equals()
decyduje, że obiekty są równe, jeśli ich pola są takie same.
Zauważ, że metoda hashCode()
również została nadpisana. Umowa dla tej metody stanowi, że gdy dwa obiekty są równe, ich wartości skrótu również muszą być takie same. Dlatego prawie zawsze trzeba zastąpić hashCode()
i equals()
razem.
Zwróć szczególną uwagę na typ argumentu metody equals
. To Object obj
, a nie Foo obj
. Jeśli umieścisz ten drugi w swojej metodzie, nie będzie to przesłonięcie metody equals
.
Pisząc własną klasę, będziesz musiał napisać podobną logikę przy nadpisywaniu equals()
i hashCode()
. Większość IDE może wygenerować to automatycznie.
Przykład implementacji equals()
można znaleźć w klasie String
, która jest częścią podstawowego API Java. Zamiast porównywania wskaźników, klasa String
porównuje zawartość String
.
W Javie 1.7 wprowadzono klasę java.util.Objects
która zapewnia wygodną metodę equals
, która porównuje dwa potencjalnie null
odwołania, dzięki czemu można jej użyć do uproszczenia implementacji metody equals
.
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 && field2 == f.field2 && Objects.equals(field3, f.field3);
}
Porównanie klas
Ponieważ metoda equals może działać na dowolnym obiekcie, jedną z pierwszych rzeczy, które często robi (po sprawdzeniu null
), jest sprawdzenie, czy klasa porównywanego obiektu odpowiada bieżącej klasie.
@Override
public boolean equals(Object obj) {
//...check for null
if (getClass() != obj.getClass()) {
return false;
}
//...compare fields
}
Zazwyczaj wykonuje się to jak wyżej, porównując obiekty klasy. Może to jednak zawieść w kilku szczególnych przypadkach, co może nie być oczywiste. Na przykład niektóre struktury generują dynamiczne proxy dla klas, a te dynamiczne proxy są w rzeczywistości inną klasą. Oto przykład użycia JPA.
Foo detachedInstance = ...
Foo mergedInstance = entityManager.merge(detachedInstance);
if (mergedInstance.equals(detachedInstance)) {
//Can never get here if equality is tested with getClass()
//as mergedInstance is a proxy (subclass) of Foo
}
Jednym z mechanizmów obejścia tego ograniczenia jest porównywanie klas za pomocą instanceof
@Override
public final boolean equals(Object obj) {
if (!(obj instanceof Foo)) {
return false;
}
//...compare fields
}
Istnieje jednak kilka pułapek, których należy unikać podczas korzystania z instanceof
. Ponieważ Foo może potencjalnie mieć inne podklasy i te podklasy mogą zastąpić equals()
możesz dostać się do przypadku, w którym Foo
jest równe FooSubclass
ale FooSubclass
nie jest równa Foo
.
Foo foo = new Foo(7);
FooSubclass fooSubclass = new FooSubclass(7, false);
foo.equals(fooSubclass) //true
fooSubclass.equals(foo) //false
Narusza to właściwości symetrii i przechodniości, a zatem jest nieprawidłową implementacją metody equals()
. W rezultacie przy korzystaniu z instanceof
dobrą praktyką jest doprowadzenie do final
metody equals()
(jak w powyższym przykładzie). Zapewni to, że żadna podklasa nie zastąpi equals()
i nie naruszy kluczowych założeń.
Metoda hashCode ()
Gdy klasa Java przesłania metodę equals
, powinna również zastąpić metodę hashCode
. Zgodnie z definicją w umowie dotyczącej metody :
- Za każdym razem, gdy jest wywoływana w tym samym obiekcie więcej niż jeden raz podczas wykonywania aplikacji Java, metoda
hashCode
musi konsekwentnie zwracać tę samą liczbę całkowitą, pod warunkiem, że żadna informacja użyta w porównaniach równych obiektu nie jest modyfikowana. Ta liczba całkowita nie musi pozostać spójna od jednego wykonania aplikacji do innego wykonania tej samej aplikacji.- Jeśli dwa obiekty są równe zgodnie z metodą
equals(Object)
, wówczas wywołanie metodyhashCode
na każdym z dwóch obiektów musi dać ten sam wynik w postaci liczby całkowitej.- Nie jest wymagane, aby jeśli dwa obiekty były nierówne zgodnie z metodą
equals(Object)
, wówczas wywołanie metodyhashCode
na każdym z dwóch obiektów musi dawać różne wyniki liczb całkowitych. Jednak programista powinien zdawać sobie sprawę, że tworzenie odrębnych wyników liczb całkowitych dla nierównych obiektów może poprawić wydajność tabel mieszania.
Kody skrótu są używane w implementacjach skrótu, takich jak HashMap
, HashTable
i HashSet
. Wynik funkcji hashCode
określa wiadro, w którym zostanie umieszczony obiekt. Te implementacje hash są bardziej efektywne, jeśli warunkiem hashCode
realizacja jest dobra. Ważną właściwością dobrej implementacji hashCode
jest to, że rozkład wartości hashCode
jest jednolity. Innymi słowy, istnieje małe prawdopodobieństwo, że wiele instancji zostanie zapisanych w tym samym segmencie.
Algorytm obliczania wartości kodu skrótu może być podobny do następującego:
public class Foo {
private int field1, field2;
private String field3;
public Foo(int field1, int field2, String field3) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Foo f = (Foo) obj;
return field1 == f.field1 &&
field2 == f.field2 &&
(field3 == null ? f.field3 == null : field3.equals(f.field3);
}
@Override
public int hashCode() {
int hash = 1;
hash = 31 * hash + field1;
hash = 31 * hash + field2;
hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
return hash;
}
}
Używanie Arrays.hashCode () jako skrótu
W Javie 1.2 i nowszych, zamiast opracowywać algorytm do obliczania kodu skrótu, można go wygenerować za pomocą java.util.Arrays#hashCode
, podając tablicę Object lub primitive zawierającą wartości pól:
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] {field1, field2, field3});
}
Java 1.7 wprowadziła klasę java.util.Objects
która zapewnia metodę wygody, hash(Object... objects)
, która oblicza kod skrótu na podstawie wartości dostarczonych do niego obiektów. Ta metoda działa podobnie jak java.util.Arrays#hashCode
.
@Override
public int hashCode() {
return Objects.hash(field1, field2, field3);
}
Uwaga: to podejście jest nieefektywne i powoduje tworzenie obiektów śmieci za każdym razem, gdy wywoływana jest Twoja niestandardowa metoda hashCode()
:
- Utworzono tymczasowy
Object[]
. (W wersjiObjects.hash()
tablica jest tworzona przez mechanizm „varargs”). - Jeśli którekolwiek z pól są typami prymitywnymi, należy je spakować, co może stworzyć więcej obiektów tymczasowych.
- Tablica musi być wypełniona.
- Tablica musi być iterowana za pomocą metody
Arrays.hashCode
lubObjects.hash
. - Nie można
Object.hashCode()
wywołańObject.hashCode()
któreArrays.hashCode
lubObjects.hash
(prawdopodobnie) muszą wykonać.
Wewnętrzne buforowanie kodów skrótu
Ponieważ obliczanie kodu skrótu obiektu może być kosztowne, atrakcyjnym rozwiązaniem może być buforowanie wartości kodu skrótu w obiekcie przy pierwszym obliczeniu. Na przykład
public final class ImmutableArray {
private int[] array;
private volatile int hash = 0;
public ImmutableArray(int[] initial) {
array = initial.clone();
}
// Other methods
@Override
public boolean equals(Object obj) {
// ...
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = Arrays.hashCode(array);
hash = h;
}
return h;
}
}
To podejście kompromisuje koszt (wielokrotnego) obliczania kodu skrótu w stosunku do narzutu dodatkowego pola w celu buforowania kodu skrótu. To, czy opłaca się to jako optymalizacja wydajności, będzie zależeć od tego, jak często dany obiekt jest mieszany (sprawdzany) i innych czynników.
Zauważysz również, że jeśli prawdziwy hashcode ImmutableArray
ma wartość zero (jedna szansa na 2 32 ), pamięć podręczna jest nieskuteczna.
Wreszcie, to podejście jest znacznie trudniejsze do prawidłowego wdrożenia, jeśli obiekt, który haszujemy, jest zmienny. Istnieją jednak większe obawy dotyczące zmiany kodów skrótu; patrz umowa powyżej.
Metody wait () i notyfikuj ()
wait()
i notify()
działają w tandemie - gdy jeden wątek wywołuje funkcję wait()
na obiekcie, wątek ten będzie blokowany, dopóki inny wątek nie notifyAll()
notify()
lub notifyAll()
dla tego samego obiektu.
(Zobacz też: wait () / powiadom () )
package com.example.examples.object;
import java.util.concurrent.atomic.AtomicBoolean;
public class WaitAndNotify {
public static void main(String[] args) throws InterruptedException {
final Object obj = new Object();
AtomicBoolean aHasFinishedWaiting = new AtomicBoolean(false);
Thread threadA = new Thread("Thread A") {
public void run() {
System.out.println("A1: Could print before or after B1");
System.out.println("A2: Thread A is about to start waiting...");
try {
synchronized (obj) { // wait() must be in a synchronized block
// execution of thread A stops until obj.notify() is called
obj.wait();
}
System.out.println("A3: Thread A has finished waiting. "
+ "Guaranteed to happen after B3");
} catch (InterruptedException e) {
System.out.println("Thread A was interrupted while waiting");
} finally {
aHasFinishedWaiting.set(true);
}
}
};
Thread threadB = new Thread("Thread B") {
public void run() {
System.out.println("B1: Could print before or after A1");
System.out.println("B2: Thread B is about to wait for 10 seconds");
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000); // sleep for 1 second
} catch (InterruptedException e) {
System.err.println("Thread B was interrupted from waiting");
}
}
System.out.println("B3: Will ALWAYS print before A3 since "
+ "A3 can only happen after obj.notify() is called.");
while (!aHasFinishedWaiting.get()) {
synchronized (obj) {
// notify ONE thread which has called obj.wait()
obj.notify();
}
}
}
};
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("Finished!");
}
}
Niektóre przykładowe dane wyjściowe:
A1: Could print before or after B1
B1: Could print before or after A1
A2: Thread A is about to start waiting...
B2: Thread B is about to wait for 10 seconds
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
B1: Could print before or after A1
B2: Thread B is about to wait for 10 seconds
A1: Could print before or after B1
A2: Thread A is about to start waiting...
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
A1: Could print before or after B1
A2: Thread A is about to start waiting...
B1: Could print before or after A1
B2: Thread B is about to wait for 10 seconds
B3: Will ALWAYS print before A3 since A3 can only happen after obj.notify() is called.
A3: Thread A has finished waiting. Guaranteed to happen after B3
Finished!
Metoda getClass ()
Do znalezienia typu klasy środowiska wykonawczego obiektu można użyć metody getClass()
. Zobacz przykład poniżej:
public class User {
private long userID;
private String name;
public User(long userID, String name) {
this.userID = userID;
this.name = name;
}
}
public class SpecificUser extends User {
private String specificUserID;
public SpecificUser(String specificUserID, long userID, String name) {
super(userID, name);
this.specificUserID = specificUserID;
}
}
public static void main(String[] args){
User user = new User(879745, "John");
SpecificUser specificUser = new SpecificUser("1AAAA", 877777, "Jim");
User anotherSpecificUser = new SpecificUser("1BBBB", 812345, "Jenny");
System.out.println(user.getClass()); //Prints "class User"
System.out.println(specificUser.getClass()); //Prints "class SpecificUser"
System.out.println(anotherSpecificUser.getClass()); //Prints "class SpecificUser"
}
Metoda getClass()
zwróci najbardziej specyficzny typ klasy, dlatego gdy getClass()
jest wywoływana na anotherSpecificUser
, zwracana jest class SpecificUser
ponieważ jest ona niższa niż drzewo dziedziczenia niż User
.
Warto zauważyć, że podczas gdy metoda getClass
jest zadeklarowana jako:
public final native Class<?> getClass();
Rzeczywisty typ statyczny zwrócony przez wywołanie getClass
to Class<? extends T>
gdzie T
jest statycznym typem obiektu, na którym getClass
jest getClass
.
tzn. skompilują się następujące elementy:
Class<? extends String> cls = "".getClass();
metoda clone ()
Metoda clone()
służy do tworzenia i zwracania kopii obiektu. Tej metody należy unikać, ponieważ jest problematyczna i należy zastosować konstruktor kopii lub inne podejście do kopiowania na korzyść clone()
.
Aby zastosować metodę, wszystkie klasy wywołujące metodę muszą implementować interfejs Cloneable
.
Sam interfejs Cloneable
jest po prostu interfejsem znaczników służącym do zmiany zachowania native
metody clone()
, która sprawdza, czy klasa obiektów wywołujących implementuje Cloneable
. Jeśli osoba dzwoniąca nie wdroży tego interfejsu, zostanie CloneNotSupportedException
.
Sama klasa Object
nie implementuje tego interfejsu, więc CloneNotSupportedException
zostanie CloneNotSupportedException
, jeśli obiekt wywołujący należy do klasy Object
.
Aby klon był poprawny, powinien być niezależny od obiektu, z którego jest klonowany, dlatego może być konieczna modyfikacja obiektu przed jego zwróceniem. Oznacza to zasadniczo utworzenie „głębokiej kopii” poprzez skopiowanie dowolnego z obiektów podlegających modyfikacji, które tworzą wewnętrzną strukturę klonowanego obiektu. Jeśli nie zostanie to poprawnie zaimplementowane, sklonowany obiekt nie będzie niezależny i będzie miał takie same odniesienia do obiektów zmiennych, jak obiekt, z którego został sklonowany. Spowodowałoby to niespójne zachowanie, ponieważ wszelkie zmiany w jednym wpłynęłyby na drugi.
class Foo implements Cloneable {
int w;
String x;
float[] y;
Date z;
public Foo clone() {
try {
Foo result = new Foo();
// copy primitives by value
result.w = this.w;
// immutable objects like String can be copied by reference
result.x = this.x;
// The fields y and z refer to a mutable objects; clone them recursively.
if (this.y != null) {
result.y = this.y.clone();
}
if (this.z != null) {
result.z = this.z.clone();
}
// Done, return the new object
return result;
} catch (CloneNotSupportedException e) {
// in case any of the cloned mutable fields do not implement Cloneable
throw new AssertionError(e);
}
}
}
metoda finalize ()
Jest to chroniona i niestatyczna metoda klasy Object
. Ta metoda służy do wykonywania niektórych operacji końcowych lub czyszczenia obiektu, zanim zostanie on usunięty z pamięci.
Według doktora ta metoda jest wywoływana przez moduł czyszczenia pamięci na obiekcie, gdy czyszczenie pamięci ustali, że nie ma już żadnych odwołań do obiektu.
Ale nie ma gwarancji, że metoda finalize()
zostanie wywołana, jeśli obiekt będzie nadal osiągalny lub nie zostaną uruchomione Garbage Collectors, gdy obiekt się spełni. Dlatego lepiej nie polegać na tej metodzie.
W bibliotekach podstawowych Java można znaleźć przykłady użycia, na przykład w FileInputStream.java
:
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
W takim przypadku jest to ostatnia szansa na zamknięcie zasobu, jeśli zasób ten nie był wcześniej zamknięty.
Zasadniczo uważa się za złą praktykę stosowanie metody finalize()
w aplikacjach dowolnego rodzaju i należy jej unikać.
Finalizatory nie służą do zwalniania zasobów (np. Zamykania plików). Śmieciarka jest wywoływana, gdy (jeśli!) W systemie zaczyna brakować miejsca na sterty. Nie można polegać na tym, że jest wywoływana, gdy systemowi brakuje uchwytów plików lub z jakiegokolwiek innego powodu.
Przypadek użycia dla finalizatorów dotyczy obiektu, który ma zostać odzyskany, aby powiadomić jakiś inny obiekt o zbliżającym się przeznaczeniu. W tym celu istnieje teraz lepszy mechanizm --- klasa java.lang.ref.WeakReference<T>
. Jeśli uważasz, że potrzebujesz napisać metodę finalize()
, powinieneś rozważyć, czy możesz rozwiązać ten sam problem za pomocą WeakReference
. Jeśli to nie rozwiąże problemu, może być konieczne ponowne przemyślenie projektu na głębszym poziomie.
Do dalszego czytania tutaj jest pozycja na temat metody finalize()
z książki „Effective Java” Joshua Blocha.
Konstruktor obiektów
Wszystkie konstruktory w Javie muszą wywoływać konstruktor Object
. Odbywa się to za pomocą wywołania super()
. To musi być pierwsza linia w konstruktorze. Powodem tego jest fakt, że obiekt może zostać faktycznie utworzony na stercie przed wykonaniem jakiejkolwiek dodatkowej inicjalizacji.
Jeśli nie określisz wywołania super()
w konstruktorze, kompilator wstawi je za ciebie.
Więc wszystkie trzy z tych przykładów są funkcjonalnie identyczne
z jawnym wywołaniem konstruktora super()
public class MyClass {
public MyClass() {
super();
}
}
z niejawnym wywołaniem konstruktora super()
public class MyClass {
public MyClass() {
// empty
}
}
z niejawnym konstruktorem
public class MyClass {
}
Co z łańcuchem konstruktorów?
Możliwe jest wywoływanie innych konstruktorów jako pierwszej instrukcji konstruktora. Ponieważ zarówno jawne wywołanie superkonstruktora, jak i wywołanie innego konstruktora muszą być pierwszymi instrukcjami, wzajemnie się wykluczają.
public class MyClass {
public MyClass(int size) {
doSomethingWith(size);
}
public MyClass(Collection<?> initialValues) {
this(initialValues.size());
addInitialValues(initialValues);
}
}
Wywołanie nowej MyClass(Arrays.asList("a", "b", "c"))
wywoła drugi konstruktor z argumentem List, który z kolei przekaże delegację pierwszemu konstruktorowi (który pośrednio przekaże delegację do super()
), a następnie wywołaj addInitialValues(int size)
z drugim rozmiarem listy. Służy to do zmniejszenia duplikacji kodu, gdy wiele konstruktorów musi wykonać tę samą pracę.
Jak wywołać konkretnego konstruktora?
Biorąc pod uwagę powyższy przykład, można wywołać new MyClass("argument")
lub new MyClass("argument", 0)
. Innymi słowy, podobnie jak przeciążanie metod , wystarczy wywołać konstruktor z parametrami, które są niezbędne dla wybranego konstruktora.
Co stanie się z konstruktorem klasy Object?
Nic więcej nie wydarzy się w podklasie, która ma domyślny pusty konstruktor (minus wywołanie super()
).
Domyślny pusty konstruktor może być jawnie zdefiniowany, ale jeśli nie, kompilator umieści go dla ciebie, o ile żadne inne konstruktory nie są jeszcze zdefiniowane.
W jaki sposób obiekt jest następnie tworzony z konstruktora w obiekcie?
Rzeczywiste tworzenie obiektów zależy od JVM. Każdy konstruktor w Javie pojawia się jako specjalna metoda o nazwie <init>
która jest odpowiedzialna za inicjowanie instancji. Ta metoda <init>
jest dostarczana przez kompilator, a ponieważ <init>
nie jest prawidłowym identyfikatorem w Javie, nie można jej używać bezpośrednio w języku.
W jaki sposób JVM wywołuje tę metodę
<init>
?
JVM wywoła metodę <init>
przy użyciu instrukcji invokespecial
i można ją wywołać tylko w niezainicjowanych instancjach klasy.
Aby uzyskać więcej informacji, zapoznaj się ze specyfikacją JVM i specyfikacją języka Java:
- Metody specjalne (JVM) - JVMS - 2.9
- Konstruktory - JLS - 8.8