1. Inicjalizacja zmiennych
Jak już wiesz, możesz zadeklarować kilka zmiennych klasowych w swojej klasie i nie tylko zadeklarować, ale natychmiast zainicjować je wartościami początkowymi.
Jednak te same zmienne można również zainicjować w konstruktorze. Dlatego teoretycznie jest możliwe, że tym samym zmiennym klasowym zostaną przypisane wartości dwukrotnie. Przykład
Kod | Notatka |
---|---|
|
Zmiennej age jest przypisywana wartość początkowa Wartość początkowa jest nadpisywana W przypadku wieku używana jest wartość początkowa |
|
Więc możesz: zostanie wywołany pierwszy konstruktor |
|
Jest więc możliwe: zostanie wywołany drugi konstruktor |
Oto, co się stanie, gdy kod zostanie wykonany Cat cat = new Cat("Васька", 2);
:
- Tworzony jest obiekt typu
Cat
- Wszystkie zmienne klasy są inicjowane ich wartościami początkowymi.
- Konstruktor jest wywoływany i wykonywany jest jego kod.
Te. zmienne klasy są najpierw inicjowane ich wartościami, a dopiero potem wykonywany jest kod konstruktorów.
2. Kolejność inicjalizacji zmiennych klasowych
Zmienne nie są po prostu inicjowane przed wykonaniem konstruktora: są one również inicjowane w ściśle określonej kolejności — kolejności deklaracji w klasie.
Spójrzmy na ten interesujący kod:
Kod | Notatka |
---|---|
|
Taki kod nie skompiluje się, ponieważ w momencie tworzenia zmiennej а
, zmienne b
i c
jeszcze nie. Ale możesz napisać to w ten sposób, a ten kod skompiluje się idealnie i będzie działał.
Kod | Notatka |
---|---|
|
0 0+2 0+2+3 |
Uwaga: pamiętaj jednak, że Twój kod powinien być przejrzysty dla innych programistów, więc lepiej nie stosować takich sztuczek - pogarsza to czytelność kodu.
Tutaj trzeba pamiętać, że wszystkie zmienne klasowe, zanim zostały im nadane wartości, mają wartość domyślną . Dla typu int
jest to zero.
Kiedy JVM inicjuje zmienną а
, po prostu przypisze jej domyślną wartość dla typu int, 0.
Kiedy kolejka osiągnie b
, zmienna a będzie już znana i będzie zawierała wartość, więc JVM przypisze jej wartość 2.
Cóż, jeśli chodzi o zmienną c
, zmienne а
i b
zostaną już zainicjowane, a JVM bez problemu obliczy wartość początkową dla с
: 0+2+3.
Jeśli utworzysz zmienną w ramach metody, nie możesz jej użyć, chyba że wcześniej przypisałeś jej wartość. Nie dotyczy to zmiennych klas. Jeśli zmiennej klasowej nie jest przypisana wartość początkowa, wówczas przypisywana jest jej wartość domyślna.
3. Stałe
Ponieważ kontynuujemy analizę procesu tworzenia obiektu, warto poruszyć kwestię stałych inicjalizacyjnych – zmiennych klasy, które posiadają modyfikator final
.
Jeśli zmienna klasy ma modyfikator final
, musi mieć przypisaną wartość początkową. To już wiesz i nie ma w tym nic dziwnego.
Ale nie wiesz, że nie musisz od razu przypisywać wartości początkowej, jeśli przypiszesz ją w konstruktorze. I to zadziała dobrze dla zmiennej końcowej. Jedynym wymaganiem jest to, że jeśli istnieje wiele konstruktorów, zmienna końcowa musi mieć przypisaną wartość we wszystkich konstruktorach.
Przykład:
public class Cat
{
public final int maxAge = 25;
public final int maxWeight;
public Cat (int weight)
{
this.maxWeight = weight; // занесение стартового значения в константу
}
}
4. Kod w konstruktorze
I jeszcze kilka ważnych uwag na temat konstruktorów. W przyszłości, gdy będziesz uczyć się Javy, zetkniesz się z takimi rzeczami, jak dziedziczenie, serializacja, wyjątki i tak dalej. Wszystkie w różnym stopniu wpływają na pracę projektantów. Teraz nie ma sensu zagłębiać się w te tematy, ale przynajmniej musimy ich dotknąć.
Na przykład jedna ważna uwaga dotycząca konstruktorów. Teoretycznie w konstruktorze można pisać kod o dowolnej złożoności. Ale nie musisz. Przykład:
|
Otwórz strumień odczytu pliku Wczytaj plik do tablicy bajtów Zapisz tablicę bajtów jako ciąg znaków Wydrukuj zawartość pliku na ekranie |
W konstruktorze klasy FilePrinter od razu otworzyliśmy strumień bajtów do pliku i odczytaliśmy jego zawartość. Jest to dość złożone zachowanie, które może potencjalnie prowadzić do błędów.
Co by było, gdyby takiego pliku nie było? A gdyby były problemy z odczytaniem? A gdyby był za duży?
Złożona logika implikuje wysokie prawdopodobieństwo błędów i kod, który musi poprawnie obsługiwać wyjątki.
Przykład 1 — Serializacja
Istnieje wiele sytuacji w standardowym programie Java, w których obiekty Twojej klasy nie są tworzone przez Ciebie. Na przykład zdecydujesz się przesłać obiekt przez sieć: w tym przypadku maszyna Java sama zamieni twój obiekt w zestaw bajtów, prześle go i ponownie utworzy obiekt na podstawie zestawu bajtów.
I tutaj okazuje się, że twojego pliku nie ma na innym komputerze, w konstruktorze wystąpi błąd i nikt go nie przetworzy - co może doprowadzić do zamknięcia programu.
Przykład 2 - Inicjalizacja pól klasy
Jeśli twój konstruktor klasy może rzucać sprawdzone wyjątki - zawiera słowo kluczowe throws - musisz przechwycić ten wyjątek w metodzie, która tworzy twój obiekt.
A co jeśli nie ma takiej metody? Przykład:
Kod | Notatka |
---|---|
|
Taki kod się nie skompiluje. |
Konstruktor klasy FilePrinter
zawiera sprawdzone wyjątki : nie można utworzyć obiektu FilePrinter
bez zawinięcia go w try-catch. A try-catch można zapisać tylko w metodzie
5. Konstruktor klasy bazowej
W poprzednich wykładach trochę omówiliśmy dziedziczenie. Niestety pełne dziedziczenie i OOP omówimy na poziomie poświęconym OOP, a to dotyczy konstruktorów już teraz.
Jeśli odziedziczysz swoją klasę z innej klasy, obiekt twojej klasy nadrzędnej zostanie faktycznie osadzony w obiekcie twojej klasy. Co więcej, ta klasa nadrzędna ma własne zmienne klasowe i własne konstruktory.
Dlatego bardzo ważne jest, abyś wiedział i rozumiał, w jaki sposób inicjowane są parametry i wywoływane są konstruktory, gdy twoja klasa ma klasę nadrzędną, której zmienne i metody dziedziczysz.
Klasy
Skąd wiemy, w jakiej kolejności inicjowane są zmienne i wywoływane są konstruktory? Najpierw napiszmy kod dla dwóch klas, z których jedna dziedziczy po drugiej:
Kod | Notatka |
---|---|
|
Klasa ChildClass dziedziczy po ParentClass . |
Musimy określić, w jakiej kolejności inicjowane są zmienne i wywoływane są konstruktory. Logowanie nam w tym pomoże.
Logowanie
Rejestrowanie to zapis w konsoli lub pliku działań, które mają miejsce podczas działania programu.
Ustalenie, że konstruktor został wywołany, jest dość proste: należy napisać o tym komunikat do konsoli w treści konstruktora. Ale jak ustalić, że zmienna została zainicjowana?
W rzeczywistości nie jest to również bardzo trudne: musisz napisać specjalną metodę, która zwróci wartość, z jaką zmienna klasy jest inicjowana, i zarejestrować ten fakt. Oto jak może wyglądać ten kod:
Kod końcowy
|
Tworzymy obiekt typu Ta metoda zapisuje przekazany tekst ChildClass do konsoli i zwraca go . Zwracana wartość jest ignorowana. Deklarujemy Piszemy tekst i inicjujemy nim zmienne Piszemy komunikat o wywołaniu konstruktora do konsoli. Zwracana wartość jest ignorowana. ParentClass ChildClass |
Jeśli uruchomisz ten kod, tekst zostanie wyświetlony na ekranie:
Zrzut ekranu metodyMain.print() |
---|
|
Dlatego zawsze możesz osobiście upewnić się, że zmienne klasy są inicjowane przed wywołaniem jej konstruktora. Cała inicjalizacja klasy bazowej poprzedza inicjalizację klasy pochodnej.
GO TO FULL VERSION