Skip to the content.

- 학습 목표 달성 확인 목록

- [] 생성자 문법의 용도를 설명할 수 있는가?

생성된 인스턴스가 제대로 쓰일 수 있도록 유효한 값으로 초기화시키는 일을 한다.

- [] 생성자를 이용하여 인스턴스 필드를 초기화시킬 수 있는가?

public class Constructor {
  Constructor(String title) {
    System.out.println(title);
  }

  public static void main(String[] args) {
    Constructor c = new Constructor("hi! 자바");
  }
}

//결과
hi! 자바
public class Constructor1 {
  static class Con{
    String title;
    int a;
    int b;
    int c;

    Con(String title, int a, int b, int c) {
      System.out.println("생성자 호출!");
      this.title = title;
      this.a = a;
      this.b = b;
      this.c = c;
    }
  }

  public static void main(String[] args) {
    Con c = new Con("자바", 1, 2, 3);
    System.out.printf("%s %d %d %d", c.title, c.a, c.b, c.c);
  }
}

//결과
생성자 호출!
자바 1 2 3

- [] this() 의 용법을 아는가?

this는 주로 생성자와 메소드의 매개변수 이름이 필드와 동일한 경우 인스턴스 멤버인 필드임을 명시한다.

public class People{
//필드
	int age;

//메소드
    void setAge(int age){
    this.age = age;
	}
}

위와 같이 파라미터 이름과 필드의 이름이 같아도 this를 활용하면 같이 사용이 가능하다

코드를 해석하자면 this.age는 필드인 age를 의미한다.즉 this의 뒤의 변수명은 인수가 아닌 멤버변수(필드)를 의미하게 된다.

- [] 스태틱 변수 초기화 문장을 사용할 수 있는가?

public class StaticCon {
    static class Sample {
        static String name;
        static int a;
        static double b;
    }

    public static void main(String[] args) {
        Sample.name = "자바";
        Sample.a = 100;
        Sample.b = 12.5;
        System.out.printf("%s %d %.1f", Sample.name, Sample.a, Sample.b);
    }
}

//결과
자바 100 12.5

스태틱 멤버는 인스턴스화를 하지 않고 호출할 수 있다.

- [] 스태틱 블록을 이용하여 스태틱 변수를 초기화시킬 수 있는가?

public class StaticCon1 {
  static int a;
  static int b;

  static class Sample {
    //아무런 코드가 없다
  }

  static {
    System.out.println("Static{} 111");
  }
  static {
    System.out.println("Static{} 222");
  }

  public static void main(String[] args) {
    StaticCon1.a = 100;
    System.out.println("종료!");
  }
}

//결과
Static{} 111
Static{} 222
종료!

클래스가 로딩 될때 스태틱 초기화 블록은 실행된다.

클래스가 로딩되는 경우는 클래스 멤버(필드와 메서드)그러니까 스태틱 멤버들을 최초로 사용할 때!

그리고 해당 클래스의 인스턴스를 최소로 생성할 때

인스턴스를 만들려면 설계도가 있어야 하고,

설계도는 메모리에 로딩되어 있어야 한다.

따라서 설계도가 없으면 즉시 설계도를 로딩할 것이다.

그리고 JVM은 이미 로딩된 클래스가 있다면 중복해서 메모리에 로딩하지 않는다.

- [] 스태틱 블록이 여러 개일 경우 컴파일러가 어떻게 다루는지 아는가?

여러개의 스태틱 블록이 있을 때 컴파일러는 한 개의 블록으로 합친다.(위의 코드를 바이트코드를 인간이 볼수 있는 언어로 번역하여 봄)

static {};
     0  getstatic java.lang.System.out : java.io.PrintStream [11]
     3  ldc <String "Static{} 111"> [17]//--------여기랑!
     5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [19]
     8  getstatic java.lang.System.out : java.io.PrintStream [11]
    11  ldc <String "Static{} 222"> [25]//--------여기!
    13  invokevirtual java.io.PrintStream.println(java.lang.String) : void [19]
    16  return

- [] 스태틱 변수 초기화 문장이 컴파일되면 어떻게 바뀌는지 아는가?

  public static class A {
    static int a = 100;
  }

  public static void main(String[] args) throws Exception {
    System.out.println(A.a);
  }
}

//결과
100

스태틱 초기화 블록이 없으면 컴파일러가 자동으로 삽입한다.

스태틱 초기화 블록에 a에 100에 할당하는 문장을 삽입한다.

static int a = 100;
//위의 문장은 다음 문장으로 바뀐다
static int a;
static{
a = 100;
}
  //위의 코드를 바이트코드로 볼때(사람이 볼수 있는)
  static {};
    0  bipush 100
    2  putstatic com.eomcs.oop.ex03.Exam0670$A.a : int [10]
    5  return

- [] 클래스 로딩할 때 어떻게 초기화 과정이 어떻게 진행되는지 이해하는가?

1.클래스를 Method Area에 로딩한다.

2.스태틱 변수를 만든다.

3.스태틱 블록을 실행한다.

- [] 인스턴스 블록을 이용하여 인스턴스 변수를 초기화시킬 수 있는가?

public class InstantCon {
    static class A {
        int a;
        int b;
        int c;

        {
            a = 100;//이 두 코드는 별도 메서드 없이 어느 메서드에든 삽입한다. 
            System.out.println("자바!");
        }

        A() {
//            a = 100;
//            System.out.println("자바!");
            System.out.println("aaa");
        }

        A(int p1) {
//            a = 100;
//            System.out.println("자바!");
            System.out.println("bbb");
        }
    }

    public static void main(String[] args) {
        A a = new A();
        System.out.printf("a=%d, b=%d, c=%d", a.a, a.b, a.c);
    }
}

스태틱 생성자든 인스턴스 생성자든 생성자를 따로 선언하지 않으면

컴파일러가 자동으로 디폴트로 생성자를 생성한다(보이지않음)

- [] 인스턴스 블록이 컴파일되면 어떻게 바뀌는지 아는가?

컴파일러는 필드초기화나 인스턴스 초기화 블록이 있으면

선언된 순서대로 모든 생성자의 첫 부분에 복사한다.

즉 위에 선언된 초기화 블록 안에 있는 코드가 그대로 다음과 같이 복사된다.

Java Tutorial 문서: Learning the Java Language > Classes and Objects

‘The Java compiler copies initializer blocks

into every constructor.

Therefore, this approach can be used

to share a block of code

between multiple constructors.’

- [] 인스턴스 변수 초기화 문장이 컴파일되면 어떻게 바뀌는지 아는가?

public class Exam {

  static class A {
    int a;
    int b;
    int c;
    
    {
      this.a = 101;
      System.out.println("첫 번째 인스턴스 초기화 블록");
    }

    {
      this.a = 102;
      System.out.println("두 번째 인스턴스 초기화 블록");
    }

    {
      this.a = 103;
      System.out.println("세 번째 인스턴스 초기화 블록");
    }

    A() {
      // 인스턴스 초기화 블록은 선언된 순서대로 삽입한다.
      // 즉 다음과 같다.
      // this.a = 101;
      // System.out.println("첫 번째 인스턴스 초기화 블록");
      // this.a = 102;
      // System.out.println("두 번째 인스턴스 초기화 블록");
      // this.a = 103;
      // System.out.println("세 번째 인스턴스 초기화 블록");

      System.out.println("A()");
      b = 200;
      c = 300;
    }

  }
  public static void main(String[] args) {
    A obj1 = new A();
    System.out.printf("a=%d, b=%d, c=%d\n", obj1.a, obj1.b, obj1.c);
  }
}

위의 코드를 바이트 코드로 번역된 것이다.

0  aload_0 [this]
     1  invokespecial java.lang.Object() [12]
     4  aload_0 [this]
     5  bipush 101//----------->여기
     7  putfield com.eomcs.oop.ex03.Exam0740$A.a : int [14]
    10  getstatic java.lang.System.out : java.io.PrintStream [16]
    13  ldc <String "첫 번째 인스턴스 초기화 블록"> [22]//----------->여기
    15  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
    18  aload_0 [this]
    19  bipush 102//----------->여기
    21  putfield com.eomcs.oop.ex03.Exam0740$A.a : int [14]
    24  getstatic java.lang.System.out : java.io.PrintStream [16]
    27  ldc <String "두 번째 인스턴스 초기화 블록"> [30]//----------->여기
    29  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
    32  aload_0 [this]
    33  bipush 103//----------->여기
    35  putfield com.eomcs.oop.ex03.Exam0740$A.a : int [14]
    38  getstatic java.lang.System.out : java.io.PrintStream [16]
    41  ldc <String "세 번째 인스턴스 초기화 블록"> [32]//----------->여기
    43  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
    46  getstatic java.lang.System.out : java.io.PrintStream [16]
    49  ldc <String "A()"> [34]

+참고

여러개의 인스턴스 초기화 블록이 있을 때,

선언된 순서대로 생성자의 앞 부분에 삽입된다

인스턴스 초기화 블록이 선언된 위치는 상관없다.

- [] 인스턴스 블록의 용도를 아는가?

1.여러 생성자에 공통으로 들어가는 초기화 문장을 작성할 때

2.생성자를 만들지 못하는 상황에서 복잡한 로직에 따라

인스턴스 필드를 초기화시켜야할때

생성자를 만들지 못하는 상황?

다음은 Object 클래스를 상속 받은 익명 클래스를 정의하고 객체를 만드는 명령이다.

Object obj1 = new Object() {
      {
        System.out.println("인스턴스 블록...");
      }
    };

이 클래스는 이름이 없기 때문에 생성자를 만들 수 없다.

그래서 초기화 명령을 작성하려면 인스턴스 블록을 이용해야 한다.