Programování

Inicializace tříd a objektů v Javě

Třídy a objekty v Javě musí být před použitím inicializovány. Dříve jste se dozvěděli, že pole třídy se inicializují na výchozí hodnoty při načtení tříd a že objekty se inicializují pomocí konstruktorů, ale k inicializaci je toho víc. Tento článek představuje všechny funkce Java pro inicializaci tříd a objektů.

stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Jak inicializovat třídu Java

Než prozkoumáme podporu Java pro inicializaci tříd, rekapitulujme si kroky inicializace třídy Java. Zvažte výpis 1.

Výpis 1. Inicializace polí třídy na výchozí hodnoty

třída SomeClass {static boolean b; statický bajt podle; statický znak c; statické dvojité d; statický plovák f; statický int i; statický dlouhý l; statické krátké s; statický řetězec St; }

Výpis 1 deklaruje třídu SomeClass. Tato třída deklaruje devět polí typů booleovský, byte, char, dvojnásobek, plovák, int, dlouho, krátký, a Tětiva. Když SomeClass je načteno, bity každého pole jsou nastaveny na nulu, což interpretujete takto:

false 0 \ u0000 0,0 0,0 0 0 0 null

Předchozí pole třídy byla implicitně inicializována na nulu. Můžete však také explicitně inicializovat pole třídy přímým přiřazením hodnot k nim, jak je uvedeno v výpisu 2.

Výpis 2. Inicializace polí třídy na explicitní hodnoty

třída SomeClass {static boolean b = true; statický bajt o = 1; static char c = 'A'; statické dvojité d = 2,0; statický float f = 3,0f; statický int i = 4; statický dlouhý l = 5000000000L; statický krátký s = 20 000; statický řetězec st = "abc"; }

Hodnota každého přiřazení musí být typově kompatibilní s typem pole třídy. Každá proměnná ukládá hodnotu přímo, s výjimkou Svatý. Variabilní Svatý ukládá odkaz na a Tětiva objekt, který obsahuje abc.

Odkazy na pole třídy

Při inicializaci pole třídy je legální ji inicializovat na hodnotu dříve inicializovaného pole třídy. Například výpis 3 se inicializuje y na Xhodnota. Obě pole jsou inicializována na 2.

Výpis 3. Odkaz na dříve deklarované pole

třída SomeClass {static int x = 2; statický int y = x; public static void main (String [] args) {System.out.println (x); System.out.println (y); }}

Opak však není legální: nemůžete inicializovat pole třídy na hodnotu následně deklarovaného pole třídy. Výstupy kompilátoru Java nelegální odkaz vpřed když narazí na tento scénář. Zvažte výpis 4.

Výpis 4. Pokus o odkaz na následně deklarované pole

třída SomeClass {static int x = y; statický int y = 2; public static void main (String [] args) {System.out.println (x); System.out.println (y); }}

Kompilátor bude hlásit nelegální odkaz vpřed když narazí statický int x = y;. Důvodem je, že zdrojový kód je kompilován shora dolů a kompilátor ještě neviděl y. (Tuto zprávu by také vydalo, pokud y nebyl výslovně inicializován.)

Inicializační bloky třídy

V některých případech možná budete chtít provést složité inicializace založené na třídách. Uděláte to po načtení třídy a před vytvořením jakýchkoli objektů z této třídy (za předpokladu, že třída není třídou nástrojů). Pro tento úkol můžete použít blok inicializace třídy.

A inicializační blok třídy je blok příkazů, kterému předchází statický klíčové slovo, které je zavedeno do těla třídy. Po načtení třídy se tyto příkazy provedou. Zvažte výpis 5.

Výpis 5. Inicializace polí sínusových a kosinových hodnot

třída Grafika {statické dvojité [] siny, kosiny; static {sines = new double [360]; kosiny = nový dvojitý [360]; for (int i = 0; i <sines.length; i ++) {sines [i] = Math.sin (Math.toRadians (i)); kosiny [i] = Math.cos (Math.toRadians (i)); }}}

Výpis 5 deklaruje a Grafika třída, která deklaruje sines a kosiny proměnné pole. Deklaruje také blok inicializace třídy, který vytváří pole s 360 prvky, jejichž odkazy jsou přiřazeny sines a kosiny. Poté použije a pro příkaz k inicializaci těchto prvků pole na příslušné hodnoty sinu a kosinu, voláním Matematika třídy hřích() a cos () metody. (Matematika je součástí standardní knihovny tříd Java. O této třídě a těchto metodách pojednám v budoucím článku.)

Výkonový trik

Protože výkon je pro grafické aplikace důležitý a protože je rychlejší získat přístup k prvku pole, než volat metodu, vývojáři se uchylují k výkonovým trikům, jako je vytváření a inicializace polí sinusů a kosinů.

Kombinace inicializátorů pole třídy a bloků inicializace třídy

V aplikaci můžete kombinovat více inicializátorů pole třídy a bloků inicializace třídy. Výpis 6 poskytuje příklad.

Výpis 6. Provádění inicializace třídy v pořadí shora dolů

třída MCFICIB {static int x = 10; statická dvojnásobná teplota = 98,6; static {System.out.println ("x =" + x); teplota = (teplota - 32) * 5,0 / 9,0; // převést na Celsius System.out.println ("temp =" + temp); } statický int y = x + 5; static {System.out.println ("y =" + y); } public static void main (String [] args) {}}

Výpis 6 deklaruje a inicializuje dvojici třídních polí (X a y) a deklaruje pár statický inicializátory. Zkompilovat tento výpis, jak je znázorněno:

javac MCFICIB.java

Potom spusťte výslednou aplikaci:

java MCFICIB

Měli byste dodržovat následující výstup:

x = 10 temp = 37,0 y = 15

Tento výstup odhaluje, že inicializace třídy se provádí v pořadí shora dolů.

() metody

Při kompilaci inicializátorů třídy a bloků inicializace třídy kompilátor Java ukládá kompilovaný bytecode (v pořadí shora dolů) ve speciální metodě s názvem (). Úhlové závorky zabraňují a konflikt jmen: nemůžete deklarovat a () metoda ve zdrojovém kódu, protože < a > znaky jsou v kontextu identifikátoru nelegální.

Po načtení třídy JVM volá tuto metodu před voláním hlavní() (když hlavní() je přítomen).

Pojďme se podívat dovnitř MCFICIB.třída. Následující částečná demontáž odhalí uložené informace pro X, tepl, a y pole:

Pole č. 1 00000290 Přístupové příznaky ACC_STATIC 00000292 Název x 00000294 Deskriptor I 00000296 Počet atributů 0 Pole # 2 00000298 Přístupové příznaky ACC_STATIC 0000029a Teplota názvu 0000029c Deskriptor D 0000029e Atributy Počet 0 Pole # 3 000002a0 Přístupové příznaky ACC_STATIC 002 0002a2 002 0

The Deskriptor řádek identifikuje JVM deskriptor typu pro pole. Typ je reprezentován jedním písmenem: pro int a D pro dvojnásobek.

Následující částečná demontáž odhaluje sekvenci instrukcí bytecode pro () metoda. Každý řádek začíná desetinným číslem, které identifikuje nulovou adresu offsetu následující instrukce:

 0 bipush 10 2 putstatic MCFICIB / x I 5 ldc2_w # 98.6 8 putstatic MCFICIB / temp D 11 getstatic java / lang / System / out Ljava / io / PrintStream; 14 nových java / lang / StringBuilder 17 dup 18 vyvolá speciální java / lang / StringBuilder / () V 21 ldc "x =" 23 invokevirtual java / lang / StringBuilder / append (Ljava / lang / String;) Ljava / lang / StringBuilder; 26 getstatic MCFICIB / x I 29 invokevirtual java / lang / StringBuilder / append (I) Ljava / lang / StringBuilder; 32 invokevirtual java / lang / StringBuilder / toString () Ljava / lang / String; 35 invokevirtual java / io / PrintStream / println (Ljava / lang / String;) V 38 getstatic MCFICIB / temp D 41 ldc2_w # 32 44 dsub 45 ldc2_w # 5 48 dmul 49 ldc2_w # 9 52 ddiv 53 putstatic MCFICIB / temp D 56 getstatic java / lang / System / out Ljava / io / PrintStream; 59 nových java / lang / StringBuilder 62 dup 63 vyvolá speciální java / lang / StringBuilder / () V 66 ldc "temp =" 68 invokevirtual java / lang / StringBuilder / append (Ljava / lang / String;) Ljava / lang / StringBuilder; 71 getstatic MCFICIB / temp D 74 invokevirtual java / lang / StringBuilder / append (D) Ljava / lang / StringBuilder; 77 invokevirtual java / lang / StringBuilder / toString () Ljava / lang / String; 80 invokevirtual java / io / PrintStream / println (Ljava / lang / String;) V 83 getstatic MCFICIB / x I 86 iconst_5 87 iadd 88 putstatic MCFICIB / y I 91 getstatic java / lang / System / out Ljava / io / PrintStream; 94 nových java / lang / StringBuilder 97 dup 98 vyvolá speciální java / lang / StringBuilder / () V 101 ldc "y =" 103 invokevirtual java / lang / StringBuilder / append (Ljava / lang / String;) Ljava / lang / StringBuilder; 106 getstatic MCFICIB / y I 109 invokevirtual java / lang / StringBuilder / append (I) Ljava / lang / StringBuilder; 112 invokevirtual java / lang / StringBuilder / toString () Ljava / lang / String; 115 invokevirtual java / io / PrintStream / println (Ljava / lang / String;) V 118 návrat

Sekvence instrukcí od offsetu 0 do offsetu 2 je ekvivalentní následujícímu inicializátoru pole třídy:

statický int x = 10;

Sekvence instrukcí od offsetu 5 do offsetu 8 je ekvivalentní následujícímu inicializátoru pole třídy:

statická dvojnásobná teplota = 98,6;

Sekvence instrukcí od offsetu 11 do offsetu 80 je ekvivalentní následujícímu inicializačnímu bloku třídy:

static {System.out.println ("x =" + x); teplota = (teplota - 32) * 5,0 / 9,0; // převést na Celsius System.out.println ("temp =" + temp); }

Sekvence instrukcí od offsetu 83 do offsetu 88 je ekvivalentní následujícímu inicializátoru pole třídy:

statický int y = x + 5;

Sekvence instrukcí od offsetu 91 do offsetu 115 je ekvivalentní následujícímu inicializačnímu bloku třídy:

static {System.out.println ("y =" + y); }

Nakonec vrátit se instrukce na offsetu 118 vrací provedení z () k té části JVM, která tuto metodu volala.

Nedělejte si starosti s tím, co znamená bytecode

Při tomto cvičení je vidět, že veškerý kód v inicializátorech pole třídy 6 a inicializačních blocích třídy je umístěn v () metoda a je prováděna v pořadí shora dolů.

Jak inicializovat objekty

Po načtení a inicializaci třídy budete často chtít z třídy vytvářet objekty. Jak jste se dozvěděli v mém nedávném úvodu k programování pomocí tříd a objektů, inicializujete objekt pomocí kódu, který umístíte do konstruktoru třídy. Zvažte výpis 7.

Výpis 7. Použití konstruktoru k inicializaci objektu

třída Město {soukromé jméno řetězce; int populace; Město (název řetězce, počet obyvatel) {this.name = name; this.population = populace; } @Override public String toString () {návratové jméno + ":" + populace; } public static void main (String [] args) {City newYork = new City ("New York", 8491079); System.out.println (newYork); // Výstup: New York: 8491079}}

Výpis 7 deklaruje a Město třída s název a populace pole. Když Město objekt je vytvořen, Město (název řetězce, počet obyvatel) konstruktor je volán k inicializaci těchto polí na argumenty volaného konstruktoru. (Také jsem přepsal Objektje public String toString () metoda pro pohodlné vrácení názvu města a hodnoty populace jako řetězce. System.out.println () nakonec volá tuto metodu, aby vrátila řetězcovou reprezentaci objektu, kterou vygeneruje.)

Před voláním konstruktoru, jaké hodnoty se dělají název a populace obsahovat? Zjistíte vložením System.out.println (this.name); System.out.println (this.population); na začátku konstruktoru. Po kompilaci zdrojového kódu (javac City.java) a spuštění aplikace (město java), pozorovali byste nula pro název a 0 pro populace. The Nový operátor vynuluje pole objektu (instance) objektu před provedením konstruktoru.

Stejně jako u polí třídy můžete explicitně inicializovat pole objektu. Můžete například určit Název řetězce = "New York"; nebo int populace = 8491079;. Tím však obvykle nic nezískáte, protože tato pole budou inicializována v konstruktoru. Jedinou výhodou, na kterou si myslím, je přiřadit výchozí hodnotu poli objektu; tato hodnota se používá, když voláte konstruktor, který neinicializuje pole:

int numDoors = 4; // výchozí hodnota přiřazená numDoors Car (řetězec, značka, model roku, int rok) {this (značka, model, rok, počet čísel); } Auto (značka řetězce, model řetězce, int rok, int numDoors) {this.make = make; this.model = model; this.year = rok; this.numDoors = numDoors; }

Inicializace objektu zrcadlí inicializaci třídy