การเริ่มต้นคลาสและอ็อบเจ็กต์ใน Java

คลาสและอ็อบเจ็กต์ใน Java ต้องถูกเตรียมใช้งานก่อนที่จะใช้ ก่อนหน้านี้คุณได้เรียนรู้แล้วว่าฟิลด์คลาสถูกกำหนดค่าเริ่มต้นเป็นค่าเริ่มต้นเมื่อคลาสถูกโหลดและอ็อบเจ็กต์นั้นถูกเตรียมใช้งานผ่านตัวสร้าง แต่ยังมีการเตรียมใช้งานเพิ่มเติม บทความนี้แนะนำคุณลักษณะทั้งหมดของ Java สำหรับการเริ่มต้นคลาสและอ็อบเจ็กต์

ดาวน์โหลดรับโค้ดดาวน์โหลดซอร์สโค้ดสำหรับแอปพลิเคชันตัวอย่างในบทช่วยสอนนี้ สร้างโดย Jeff Friesen สำหรับ JavaWorld

วิธีเริ่มต้นคลาส Java

ก่อนที่เราจะสำรวจการสนับสนุนของ Java สำหรับการเริ่มต้นคลาสเรามาสรุปขั้นตอนการเริ่มต้นคลาส Java กันก่อน พิจารณารายชื่อ 1.

รายการ 1. การกำหนดค่าฟิลด์คลาสเป็นค่าเริ่มต้น

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

รายการที่ 1 SomeClassประกาศระดับ คลาสนี้บอกเก้าสาขาประเภทboolean, byte, char, double, float, int, long, และshort Stringเมื่อSomeClassโหลดบิตของแต่ละฟิลด์จะถูกตั้งค่าเป็นศูนย์ซึ่งคุณตีความได้ดังนี้:

false 0 \u0000 0.0 0.0 0 0 0 null

ฟิลด์คลาสก่อนหน้านี้ถูกเตรียมใช้งานโดยปริยายเป็นศูนย์ อย่างไรก็ตามคุณสามารถเริ่มต้นฟิลด์ของชั้นเรียนอย่างชัดเจนได้โดยกำหนดค่าให้กับฟิลด์เหล่านี้โดยตรงดังแสดงในรายการ 2

รายการ 2. การกำหนดค่าฟิลด์คลาสเป็นค่าที่ชัดเจน

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

ค่าของงานแต่ละงานต้องเข้ากันได้กับประเภทของฟิลด์คลาส stแต่ละตัวแปรเก็บค่าโดยตรงด้วยข้อยกเว้นของ ตัวแปรจัดstเก็บการอ้างอิงไปยังStringวัตถุที่มีabc.

การอ้างอิงฟิลด์คลาส

เมื่อเริ่มต้นฟิลด์คลาสการเริ่มต้นเป็นค่าของฟิลด์คลาสเริ่มต้นก่อนหน้านี้ถูกต้องตามกฎหมาย ยกตัวอย่างเช่นรายการ 3 initializes yเพื่อxค่า 's 2สาขาทั้งสองจะเริ่มต้นได้ที่

รายการ 3. การอ้างอิงฟิลด์ที่ประกาศไว้ก่อนหน้านี้

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

อย่างไรก็ตามการย้อนกลับไม่ถูกกฎหมาย: คุณไม่สามารถเริ่มต้นฟิลด์คลาสเป็นค่าของฟิลด์คลาสที่ประกาศในภายหลังได้ คอมไพลเลอร์ Java เอาต์พุตillegal forward referenceเมื่อพบสถานการณ์นี้ พิจารณารายชื่อ 4.

รายชื่อ 4. พยายามอ้างอิงฟิลด์ที่ประกาศในภายหลัง

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

คอมไพเลอร์จะรายงานเมื่อพบillegal forward reference เพราะนี่คือรหัสที่มาจะรวบรวมจากบนลงล่างและคอมไพเลอร์ยังไม่ได้เห็นstatic int x = y; y(นอกจากนี้ยังส่งออกข้อความนี้หากyไม่ได้เริ่มต้นอย่างชัดเจน)

บล็อกการเริ่มต้นคลาส

ในบางกรณีคุณอาจต้องการดำเนินการเริ่มต้นตามคลาสที่ซับซ้อน คุณจะทำสิ่งนี้หลังจากโหลดคลาสแล้วและก่อนที่จะสร้างออบเจ็กต์จากคลาสนั้น (สมมติว่าคลาสนั้นไม่ใช่คลาสยูทิลิตี้) คุณสามารถใช้บล็อกการเริ่มต้นคลาสสำหรับงานนี้

บล็อกเริ่มต้นเรียนเป็นบล็อกของงบนำโดยที่staticคำหลักที่นำเข้าสู่ร่างกายของชั้น เมื่อคลาสโหลดคำสั่งเหล่านี้จะถูกดำเนินการ พิจารณารายชื่อ 5.

รายการ 5. การเริ่มต้นอาร์เรย์ของค่าไซน์และโคไซน์

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

รายการ 5 ประกาศGraphicsคลาสที่ประกาศsinesและcosinesตัวแปรอาร์เรย์ นอกจากนี้ยังประกาศบล็อกเริ่มต้นเรียนที่สร้างอาร์เรย์ 360 องค์ประกอบที่มีการอ้างอิงได้รับมอบหมายให้และsines cosinesจากนั้นใช้forคำสั่งเพื่อเริ่มต้นองค์ประกอบอาร์เรย์เหล่านี้เป็นค่าไซน์และโคไซน์ที่เหมาะสมโดยเรียกMathคลาสsin()และcos()วิธีการ ( Mathเป็นส่วนหนึ่งของไลบรารีคลาสมาตรฐานของ Java ฉันจะพูดถึงคลาสนี้และวิธีการเหล่านี้ในบทความในอนาคต)

เคล็ดลับประสิทธิภาพ

เนื่องจากประสิทธิภาพมีความสำคัญต่อแอปพลิเคชันกราฟิกและเนื่องจากสามารถเข้าถึงองค์ประกอบอาร์เรย์ได้เร็วกว่าการเรียกใช้เมธอดนักพัฒนาจึงใช้เทคนิคประสิทธิภาพเช่นการสร้างและกำหนดค่าเริ่มต้นอาร์เรย์ของไซน์และโคไซน์

การรวมตัวเริ่มต้นฟิลด์คลาสและบล็อกการเริ่มต้นคลาส

คุณสามารถรวมตัวเริ่มต้นฟิลด์คลาสหลายตัวและบล็อกการเริ่มต้นคลาสในแอปพลิเคชัน รายการ 6 ให้ตัวอย่าง

รายการ 6. ดำเนินการเริ่มต้นคลาสตามลำดับจากบนลงล่าง

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

รายการ 6 ประกาศและเริ่มต้นคู่ของฟิลด์คลาส ( xและy) และประกาศคู่ของตัวstaticเริ่มต้น รวบรวมรายชื่อนี้ตามที่แสดง:

javac MCFICIB.java

จากนั้นเรียกใช้แอปพลิเคชันที่เป็นผลลัพธ์:

java MCFICIB

คุณควรสังเกตผลลัพธ์ต่อไปนี้:

x = 10 temp = 37.0 y = 15

ผลลัพธ์นี้แสดงให้เห็นว่าการเริ่มต้นคลาสจะดำเนินการตามลำดับจากบนลงล่าง

() วิธีการ

When compiling class initializers and class initialization blocks, the Java compiler stores the compiled bytecode (in top-down order) in a special method named (). The angle brackets prevent a name conflict: you cannot declare a () method in source code because the < and > characters are illegal in an identifier context.

After loading a class, the JVM calls this method before calling main() (when main() is present).

Let's take a look inside MCFICIB.class. The following partial disassembly reveals the stored information for the x, temp, and y fields:

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.

เช่นเดียวกับฟิลด์คลาสคุณสามารถเริ่มต้นฟิลด์ออบเจ็กต์อย่างชัดเจนได้ ตัวอย่างเช่นคุณสามารถระบุหรือString name = "New York"; int population = 8491079;อย่างไรก็ตามโดยปกติจะไม่มีอะไรที่จะได้รับจากการทำเช่นนี้เนื่องจากฟิลด์เหล่านี้จะเริ่มต้นในตัวสร้าง ประโยชน์เดียวที่ฉันคิดได้คือการกำหนดค่าเริ่มต้นให้กับฟิลด์วัตถุ ค่านี้ใช้เมื่อคุณเรียกตัวสร้างที่ไม่ได้เตรียมใช้งานฟิลด์:

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

การเริ่มต้นอ็อบเจ็กต์มิเรอร์คลาสเริ่มต้น