Java의 클래스 및 객체 초기화

Java의 클래스와 객체는 사용하기 전에 초기화해야합니다. 이전에 클래스 필드가 클래스가로드 될 때 기본값으로 초기화되고 객체가 생성자를 통해 초기화된다는 것을 배웠지 만 초기화에는 더 많은 것이 있습니다. 이 기사에서는 클래스와 객체를 초기화하기위한 Java의 모든 기능을 소개합니다.

다운로드 코드 받기이 자습서에서 예제 응용 프로그램의 소스 코드를 다운로드합니다. JavaWorld를 위해 Jeff Friesen이 만들었습니다.

Java 클래스를 초기화하는 방법

클래스 초기화에 대한 Java의 지원을 살펴보기 전에 Java 클래스를 초기화하는 단계를 요약 해 보겠습니다. 목록 1을 고려하십시오.

Listing 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. 이 클래스는 종류의 9 개 개의 필드를 선언 boolean, byte, char, double, float, int, long, short,와 String. 가로 SomeClass드되면 각 필드의 비트가 0으로 설정되며 다음과 같이 해석됩니다.

false 0 \u0000 0.0 0.0 0 0 0 null

이전 클래스 필드는 암시 적으로 0으로 초기화되었습니다. 그러나 목록 2에 표시된대로 값을 직접 할당하여 클래스 필드를 명시 적으로 초기화 할 수도 있습니다.

Listing 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은의 값으로 초기화 y됩니다 x. 두 필드는 모두로 초기화됩니다 2.

Listing 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배열 하는 클래스를 선언 합니다. 또한 참조가 sines및에 할당 된 360 요소 배열을 생성하는 클래스 초기화 블록을 선언합니다 cosines. 그런 다음 for문을 사용 하여 Math클래스 sin()cos()메서드를 호출하여 이러한 배열 요소를 적절한 사인 및 코사인 값으로 초기화 합니다. ( MathJava의 표준 클래스 라이브러리의 일부입니다.이 클래스와 이러한 메서드는 이후 기사에서 설명하겠습니다.)

성능 트릭

Because performance is important to graphics applications, and because it's faster to access an array element than to call a method, developers resort to performance tricks such as creating and initializing arrays of sines and cosines.

Combining class field initializers and class initialization blocks

You can combine multiple class field initializers and class initialization blocks in an application. Listing 6 provides an example.

Listing 6. Performing class initialization in top-down order

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

Listing 6 declares and initializes a pair of class fields (x and y), and declares a pair of static initializers. Compile this listing as shown:

javac MCFICIB.java

Then run the resulting application:

java MCFICIB

You should observe the following output:

x = 10 temp = 37.0 y = 15

This output reveals that class initialization is performed in top-down order.

() methods

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

객체 초기화는 클래스 초기화를 반영합니다.