Java'da sınıf ve nesne başlatma

Java'daki sınıflar ve nesneler kullanılmadan önce başlatılmalıdır. Daha önce, sınıflar yüklendiğinde sınıf alanlarının varsayılan değerlerle başlatıldığını ve nesnelerin yapıcılar aracılığıyla başlatıldığını öğrendiniz, ancak başlatma için daha fazlası var. Bu makale, Java'nın sınıfları ve nesneleri başlatmak için tüm özelliklerini tanıtır.

indir Kodu alın Bu eğitimdeki örnek uygulamalar için kaynak kodunu indirin. JavaWorld için Jeff Friesen tarafından düzenlendi.

Java sınıfı nasıl başlatılır

Java'nın sınıf başlatma desteğini keşfetmeden önce, bir Java sınıfını başlatma adımlarını özetleyelim. Liste 1'i düşünün.

Liste 1. Sınıf alanlarını varsayılan değerlere başlatma

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

Liste 1, sınıf bildirir SomeClass. Bu sınıf türlerinden dokuz alanları beyan boolean, byte, char, double, float, int, long, short, ve String. Ne zaman SomeClassyüklenir, her alanın bitleri aşağıdaki gibi yorumlamak hangi sıfıra ayarlanır:

false 0 \u0000 0.0 0.0 0 0 0 null

Önceki sınıf alanları dolaylı olarak sıfır olarak başlatıldı. Ancak, Liste 2'de gösterildiği gibi, sınıf alanlarını doğrudan bunlara değerler atayarak da açık bir şekilde başlatabilirsiniz.

Liste 2. Sınıf alanlarını açık değerlere ilklendirme

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"; }

Her ödevin değeri, sınıf alanının türüyle tür uyumlu olmalıdır. Her değişken, değeri doğrudan saklar st. Değişken , içeren stbir Stringnesneye bir başvuru saklar abc.

Sınıf alanlarına başvurma

Bir sınıf alanını başlatırken, onu önceden başlatılmış bir sınıf alanının değerine başlatmak yasaldır. Örneğin, Liste 3 y, x'nın değerini başlatır . Her iki alan da başlatılır 2.

Liste 3. Önceden beyan edilmiş bir alana başvurma

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

Ancak, bunun tersi geçerli değildir: Bir sınıf alanını, sonradan bildirilen bir sınıf alanının değerine başlatamazsınız. Java derleyicisi illegal forward referencebu senaryoyla karşılaştığında çıktı verir. Liste 4'ü düşünün.

Listeleme 4. Daha sonra bildirilen bir alana başvurmaya çalışma

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

Derleyici illegal forward referencekarşılaştığında rapor verecektir static int x = y;. Bunun nedeni, kaynak kodun yukarıdan aşağıya derlenmesi ve derleyicinin henüz görmemiş olmasıdır y. ( yAçıkça başlatılmamış olsaydı bu mesajı da çıkarırdı.)

Sınıf başlatma blokları

Bazı durumlarda, karmaşık sınıf tabanlı başlatmalar gerçekleştirmek isteyebilirsiniz. Bunu, bir sınıf yüklendikten sonra ve bu sınıftan herhangi bir nesne oluşturulmadan önce yapacaksınız (sınıfın bir yardımcı sınıf olmadığı varsayılarak). Bu görev için bir sınıf başlatma bloğu kullanabilirsiniz.

Bir sınıf başlatma bloğu öncesinde tabloların bir bloktur staticsınıfının vücuda girdikleri anahtar kelimeyle. Sınıf yüklendiğinde, bu ifadeler yürütülür. Liste 5'i düşünün.

Liste 5. Sinüs ve kosinüs değerleri dizilerini başlatma

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

Liste 5, değişkenleri ve dizi değişkenlerini bildiren bir Graphicssınıf bildirir . Aynı zamanda, referanslar atanır 360 eleman dizileri oluşturan bir sınıf başlatma blok bildirir ve . Daha sonra bu dizi öğelerini uygun sinüs ve kosinüs değerlerine başlatmak için, sınıfın ve yöntemlerini çağırarak bir ifade kullanır . ( Java'nın standart sınıf kitaplığının bir parçasıdır. Bu sınıfı ve bu yöntemleri ilerideki bir makalede tartışacağım.)sinescosinessinescosinesforMathsin()cos()Math

Performans hilesi

Performans, grafik uygulamaları için önemli olduğundan ve bir dizi öğesine erişmek bir yöntemi çağırmaktan daha hızlı olduğundan, geliştiriciler sinüs ve kosinüs dizilerini oluşturma ve başlatma gibi performans hilelerine başvurur.

Sınıf alanı başlatıcılarını ve sınıf başlatma bloklarını birleştirmek

Bir uygulamada birden çok sınıf alanı başlatıcısını ve sınıf başlatma bloğunu birleştirebilirsiniz. Liste 6, bir örnek sağlar.

Liste 6. Yukarıdan aşağıya sırayla sınıf başlatma gerçekleştirme

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) { } }

Liste 6, bir çift sınıf alanı ( xve y) bildirir ve başlatır ve bir çift staticbaşlatıcı bildirir . Bu listeyi gösterildiği gibi derleyin:

javac MCFICIB.java

Ardından ortaya çıkan uygulamayı çalıştırın:

java MCFICIB

Aşağıdaki çıktıyı gözlemlemelisiniz:

x = 10 temp = 37.0 y = 15

Bu çıktı, sınıf başlatmanın yukarıdan aşağıya doğru yapıldığını ortaya koymaktadır.

() yöntemler

Sınıf başlatıcılarını ve sınıf başlatma bloklarını derlerken, Java derleyicisi derlenmiş bayt kodunu (yukarıdan aşağı sırayla) adlı özel bir yöntemde depolar (). Köşeli parantezler bir ad çakışmasını önler : ()kaynak kodda bir yöntem bildiremezsiniz çünkü <ve >karakterleri bir tanımlayıcı bağlamında geçersizdir.

Bir sınıfı yükledikten sonra, JVM bu yöntemi çağırmadan önce main()( main()mevcut olduğunda ) çağırır .

İçeri bir göz atalım MCFICIB.class. Aşağıdaki kısmi sökme saklanan bilgileri ortaya çıkarır x, tempve yalanları:

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.

Sınıf alanlarında olduğu gibi, nesne alanlarını açık bir şekilde başlatabilirsiniz. Örneğin, String name = "New York";veya belirtebilirsiniz int population = 8491079;. Bununla birlikte, genellikle bunu yaparak kazanılacak hiçbir şey yoktur, çünkü bu alanlar yapıcıda başlatılacaktır. Aklıma gelen tek fayda, bir nesne alanına varsayılan bir değer atamaktır; bu değer, alanı başlatmayan bir kurucu çağırdığınızda kullanılır:

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

Nesne başlatma, sınıf başlatmayı yansıtır