Inicijalizacija klase i objekta u Javi

Klase i objekti u Javi moraju se inicijalizirati prije nego što se koriste. Ranije ste već saznali da se polja klase inicijaliziraju na zadane vrijednosti kada se klase učitavaju i da se objekti inicijaliziraju putem konstruktora, ali inicijalizacije ima još. Ovaj članak predstavlja sve Java značajke za inicijalizaciju klasa i objekata.

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

Kako inicijalizirati Java klasu

Prije nego što istražimo Javinu podršku za inicijalizaciju klase, sažeto ćemo opisati korake inicijalizacije Java klase. Razmislite o popisu 1.

Popis 1. Inicijalizacija polja klase na zadane vrijednosti

class SomeClass { static boolean b; static byte by; static char c; static double d; static float f; static int i; static long l; static short s; static String st; }

Popis 1 deklarira klasu SomeClass. Ovaj razred izjavljuje devet polja tipa boolean, byte, char, double, float, int, long, short, i String. Kada SomeClassse učita, bitovi svakog polja postave se na nulu, što tumačite na sljedeći način:

false 0 \u0000 0.0 0.0 0 0 0 null

Prethodna polja klase implicitno su inicijalizirana na nulu. Međutim, možete i izričito inicijalizirati polja klase izravnim dodjeljivanjem vrijednosti njima, kao što je prikazano na popisu 2.

Popis 2. Inicijalizacija polja klase u eksplicitne vrijednosti

class SomeClass { static boolean b = true; static byte by = 1; static char c = 'A'; static double d = 2.0; static float f = 3.0f; static int i = 4; static long l = 5000000000L; static short s = 20000; static String st = "abc"; }

Vrijednost svakog zadatka mora biti kompatibilna s tipom tipa polja klase. Svaka varijabla vrijednost pohranjuje izravno, s izuzetkom st. Varijabla stpohranjuje referencu na Stringobjekt koji sadrži abc.

Referenciranje polja klase

Kada se inicijalizira polje klase, legalno je inicijalizirati je na vrijednost prethodno inicijaliziranog polja klase. Na primjer, Popis 3 inicijalizira se yna xvrijednost '. Oba su polja inicijalizirana u 2.

Popis 3. Upućivanje na prethodno deklarirano polje

class SomeClass { static int x = 2; static int y = x; public static void main(String[] args) { System.out.println(x); System.out.println(y); } }

Međutim, obrnuto nije legalno: ne možete inicijalizirati polje klase na vrijednost naknadno deklariranog polja klase. Java kompajler izlazi illegal forward referencekad naiđe na ovaj scenarij. Razmislite o popisu 4.

Popis 4. Pokušaj referenciranja na naknadno deklarirano polje

class SomeClass { static int x = y; static int y = 2; public static void main(String[] args) { System.out.println(x); System.out.println(y); } }

Prevoditelj će izvijestiti illegal forward referencekada naiđe static int x = y;. To je zato što se izvorni kod kompajlira od vrha prema dolje, a kompajler još nije vidio y. (Također bi izbacila ovu poruku ako ynije izričito inicijalizirana.)

Blokovi inicijalizacije klase

U nekim ćete slučajevima možda htjeti izvesti složene inicijalizacije temeljene na klasama. To ćete učiniti nakon učitavanja klase i prije nego što se iz te klase stvore bilo kakvi objekti (pod pretpostavkom da klasa nije korisna klasa). Za ovaj zadatak možete koristiti blok inicijalizacije klase.

Klase inicijalizacija blok je blok izjava prethodila staticključnu riječ koja je uvedena u klasi tijela. Kada se učita klasa, ti se izrazi izvršavaju. Razmislite o popisu 5.

Popis 5. Inicijalizacija nizova sinusnih i kosinusnih vrijednosti

class Graphics { static double[] sines, cosines; static { sines = new double[360]; cosines = new double[360]; for (int i = 0; i < sines.length; i++) { sines[i] = Math.sin(Math.toRadians(i)); cosines[i] = Math.cos(Math.toRadians(i)); } } }

Popis 5 deklarira Graphicsklasu koja deklarira sinesi cosinesvarijable niza. Također deklarira blok inicijalizacije klase koji stvara nizove od 360 elemenata čije su reference dodijeljene sinesi cosines. Zatim koristi fornaredbu za inicijalizaciju ovih elemenata niza u odgovarajuće vrijednosti sinusa i kosinusa pozivanjem Mathklase sin()i cos()metoda. ( Mathdio je Java-ove knjižnice standardnih razreda. O ovoj klasi i ovim metodama raspravit ću u sljedećem članku.)

Trik u izvedbi

Budući da je izvedba važna za grafičke aplikacije i jer je brže pristupiti elementu niza nego pozivati ​​metodu, programeri pribjegavaju trikovima izvedbe poput stvaranja i inicijalizacije nizova sinusa i kosinusa.

Kombinirajući inicijalizatore polja klase i blokove inicijalizacije klase

U aplikaciji možete kombinirati više inicijalizatora polja klase i blokova inicijalizacije klase. Popis 6 daje primjer.

Popis 6. Izvođenje inicijalizacije klase redoslijedom odozgo prema dolje

class MCFICIB { static int x = 10; static double temp = 98.6; static { System.out.println("x = " + x); temp = (temp - 32) * 5.0/9.0; // convert to Celsius System.out.println("temp = " + temp); } static int y = x + 5; static { System.out.println("y = " + y); } public static void main(String[] args) { } }

Popis 6 deklarira i inicijalizira par polja klase ( xi y) i deklarira par staticinicijalizatora. Sastavite ovaj popis kao što je prikazano:

javac MCFICIB.java

Zatim pokrenite rezultirajuću aplikaciju:

java MCFICIB

Trebali biste promatrati sljedeći rezultat:

x = 10 temp = 37.0 y = 15

Ovaj izlaz otkriva da se inicijalizacija klase izvodi redoslijedom od vrha prema dolje.

() metode

Kada kompilira inicijalizatore klase i blokove inicijalizacije klase, Java kompajler sprema sastavljeni bytecode (u redoslijedu odozgo prema dolje) u posebnu metodu imenovanu (). Ove zgrade spriječio sukob ime : ne možete proglasiti ()metoda u izvornom kodu, jer je <i >likovi su ilegalne u identifikatora kontekstu.

Nakon učitavanja klase, JVM poziva ovu metodu prije poziva main()(kada main()je prisutan).

Pogledajmo unutra MCFICIB.class. Slijedi djelomičan rastavljanje otkriva pohranjene podatke za x, tempi ypolja:

Field #1 00000290 Access Flags ACC_STATIC 00000292 Name x 00000294 Descriptor I 00000296 Attributes Count 0 Field #2 00000298 Access Flags ACC_STATIC 0000029a Name temp 0000029c Descriptor D 0000029e Attributes Count 0 Field #3 000002a0 Access Flags ACC_STATIC 000002a2 Name y 000002a4 Descriptor I 000002a6 Attributes Count 0

The Descriptor line identifies the JVM's type descriptor for the field. The type is represented by a single letter: I for int and D for double.

The following partial disassembly reveals the bytecode instruction sequence for the () method. Each line starts with a decimal number that identifies the zero-based offset address of the subsequent instruction:

 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 new java/lang/StringBuilder 17 dup 18 invokespecial 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 new java/lang/StringBuilder 62 dup 63 invokespecial 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 new java/lang/StringBuilder 97 dup 98 invokespecial 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 return

The instruction sequence from offset 0 through offset 2 is equivalent to the following class field initializer:

static int x = 10;

The instruction sequence from offset 5 through offset 8 is equivalent to the following class field initializer:

static double temp = 98.6;

The instruction sequence from offset 11 through offset 80 is equivalent to the following class initialization block:

static { System.out.println("x = " + x); temp = (temp - 32) * 5.0/9.0; // convert to Celsius System.out.println("temp = " + temp); }

The instruction sequence from offset 83 through offset 88 is equivalent to the following class field initializer:

static int y = x + 5;

The instruction sequence from offset 91 through offset 115 is equivalent to the following class initialization block:

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

Finally, the return instruction at offset 118 returns execution from () to that part of the JVM that called this method.

Don't worry about what the bytecode means

The takeaway from this exercise is to see that all code in Listing 6's class field initializers and class initialization blocks is located in the () method, and is executed in top-down order.

How to initialize objects

After a class has been loaded and initialized, you'll often want to create objects from the class. As you learned in my recent introduction to programming with classes and objects, you initialize an object via the code that you place in a class's constructor. Consider Listing 7.

Listing 7. Using the constructor to initialize an object

class City { private String name; int population; City(String name, int population) { this.name = name; this.population = population; } @Override public String toString() { return name + ": " + population; } public static void main(String[] args) { City newYork = new City("New York", 8491079); System.out.println(newYork); // Output: New York: 8491079 } }

Listing 7 declares a City class with name and population fields. When a City object is created, the City(String name, int population) constructor is called to initialize these fields to the called constructor's arguments. (I've also overridden Object's public String toString() method to conveniently return the city name and population value as a string. System.out.println() ultimately calls this method to return the object's string representation, which it outputs.)

Before the constructor is called, what values do name and population contain? You can find out by inserting System.out.println(this.name); System.out.println(this.population); at the start of the constructor. After compiling the source code (javac City.java) and running the application (java City), you would observe null for name and 0 for population. The new operator zeroes an object's object (instance) fields before executing a constructor.

Kao i kod polja klase, možete eksplicitno inicijalizirati polja objekta. Na primjer, možete odrediti String name = "New York";ili int population = 8491079;. Međutim, obično se time ništa ne može dobiti, jer će se ta polja inicijalizirati u konstruktoru. Jedina pogodnost koje se mogu sjetiti je dodijeliti zadanu vrijednost polju objekta; ova se vrijednost koristi kada pozovete konstruktor koji ne inicijalizira polje:

int numDoors = 4; // default value assigned to numDoors Car(String make, String model, int year) { this(make, model, year, numDoors); } Car(String make, String model, int year, int numDoors) { this.make = make; this.model = model; this.year = year; this.numDoors = numDoors; }

Inicijalizacija objekta odražava inicijalizaciju klase