이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › Java › 클래스와 객체
클래스, 객체, 생성자
new를 하면 어떤 순서로 무슨 일이 일어나나
클래스는 설계도이고, new는 그 설계도로 객체를 하나 만듭니다. 이때 필드는 먼저 기본값으로 채워지고, 그다음 필드 초기화식과 초기화 블록이 적힌 순서대로 실행되고, 마지막에 생성자 본문이 실행됩니다. 어떤 생성자를 적었는지에 따라 new Order()가 아예 컴파일되지 않기도 합니다.
실행으로 대조 · JDK 21에서 열여섯 가지 조합을 컴파일하고 실행해 컴파일 결과, 실행 순서, 출력을 대조
이 실습에 나오는 개념
- 클래스와 객체
- 클래스는 어떤 필드(데이터)와 메서드(동작)를 가질지 적어 둔 설계도이고, 객체는 그 설계도로 new를 해서 메모리(힙)에 실제로 만든 것이다. 객체를 인스턴스라고도 부른다. 클래스 하나로 객체를 여러 개 만들 수 있고, 객체마다 필드 값을 따로 가진다. 변수에는 객체 자체가 아니라 객체가 있는 곳을 가리키는 참조가 담긴다.
- 생성자
- 객체를 new로 만들 때 한 번 실행되어 필드를 처음 상태로 맞추는 코드다. 이름이 클래스 이름과 같고 반환 타입을 적지 않는다. 받는 인자가 다르면 한 클래스에 여러 개를 둘 수 있으며, new에 넘긴 인자에 맞는 것이 선택된다. 생성자 본문은 필드가 기본값으로 채워지고 필드 초기화식과 초기화 블록이 실행된 뒤에 실행된다.
- 기본 생성자
- 클래스에 생성자를 하나도 적지 않았을 때 컴파일러가 자동으로 넣어 주는, 인자가 없고 본문이 빈 생성자다. 이것 덕분에 생성자를 적지 않은 클래스도 new 클래스이름()으로 객체를 만들 수 있다. 생성자를 하나라도 직접 적으면 만들어지지 않는다. 그래서 인자 있는 생성자만 적은 클래스는 인자 없이 new를 하면 컴파일 오류가 난다.
- 필드의 기본값
- 객체가 만들어질 때 값을 따로 주지 않은 필드에 자동으로 들어가는 값이다. int 같은 숫자 타입은 0, boolean은 false, String을 비롯한 참조 타입은 null이다. 필드 초기화식이나 생성자가 값을 넣기 전까지는 이 값이 보인다. 메서드 안의 지역 변수에는 기본값이 없어서, 값을 넣지 않고 읽으면 컴파일 오류가 난다.
- this
- 인스턴스 메서드나 생성자 안에서 지금 다루고 있는 객체 자신을 가리키는 참조다. this.name처럼 써서 같은 이름의 매개변수와 필드를 구분한다. 생성자의 첫 문장으로 this(인자)를 쓰면 같은 클래스의 다른 생성자를 먼저 실행한다. static 메서드에는 대상 객체가 없으므로 this를 쓸 수 없다.
이렇게 바꿔 보세요
- 처음 설정으로 — Order()가 this(1)로 다른 생성자를 먼저 부릅니다. 출력은 21, 기본입니다.
- 생성자를 하나도 적지 않으면 — 컴파일러가 기본 생성자를 넣어 줍니다. memo는 null로 남습니다.
- 인자 있는 생성자만 적고 new Order() — 기본 생성자가 생기지 않아 컴파일 오류입니다.
- 초기화 블록을 위에 적으면 — 초기화식이 나중에 실행되어 qty가 10이 됩니다.
- new Order(3) — 인자에 맞는 생성자 Order(int extra)가 선택됩니다.
- 생성자가 없는데 new Order(3) — 기본 생성자는 인자를 받지 않으므로 컴파일 오류입니다.
처음 설정으로 실행하면
- 컴파일: Order 클래스에 생성자 Order()와 Order(int extra)를 직접 적었습니다. 생성자는 받는 인자가 다르면 여러 개 둘 수 있습니다.
- 컴파일: new Order()에 맞는 생성자를 찾습니다. 인자가 없으므로 Order()가 선택됩니다. 이제 프로그램을 실행합니다.
- new Order() 실행: 가장 먼저 힙에 Order 객체의 자리를 잡고 모든 필드를 기본값으로 채웁니다. int인 qty는 0, 참조 타입인 memo는 null입니다. 초기화식도 생성자 본문도 아직 실행되지 않았습니다.
- 생성자 Order()의 첫 줄 this(1): 같은 클래스의 다른 생성자 Order(int extra)를 먼저 부릅니다. extra에는 1이 들어갑니다. Order()의 나머지 본문은 그 생성자가 끝난 뒤에 실행됩니다.
- int qty = 10: 필드 초기화식이 실행되어 qty가 0에서 10이 됩니다. 필드 초기화식과 초기화 블록은 어느 생성자가 불리든 생성자 본문보다 먼저, 클래스에 적힌 순서대로 실행됩니다.
- 초기화 블록의 qty = 20: qty가 10에서 20이 됩니다. 블록이 초기화식보다 아래에 적혀 있어 나중에 실행되었습니다.
- Order(int extra) 본문의 qty = qty + extra: 초기화가 끝난 뒤에야 생성자 본문이 실행됩니다. 지금 qty인 20에 extra 1을 더해 qty가 21이 됩니다.
- Order(int extra) 본문의 memo = "추가 " + extra: null이던 memo에 "추가 1"이 들어갑니다.
- Order()의 나머지 본문 memo = "기본": this(1)로 부른 생성자가 끝나 Order()로 돌아왔습니다. memo가 "추가 1"에서 "기본"으로 바뀝니다. 초기화식과 블록은 다시 실행되지 않습니다.
- Order o = new Order(): 생성자가 끝났습니다. new는 완성된 객체를 가리키는 참조를 돌려주고, 변수 o가 그 참조를 담습니다.
- System.out.println(o.qty + ", " + o.memo): o가 가리키는 객체의 qty와 memo를 읽어 출력합니다. 출력은 21, 기본입니다.
실행 결과
- 초기화식(10)이 먼저, 그 아래의 블록(20)이 나중에 실행되어 생성자 본문이 시작될 때 qty는 20이었습니다.
- this(...)는 생성자의 첫 문장으로만 쓸 수 있고, 같은 클래스의 다른 생성자를 먼저 실행합니다. 공통으로 할 일을 한 생성자에 모아 둘 때 씁니다. 초기화식과 블록은 한 번만 실행됩니다.
코드
class Order {
int qty = 10; // 필드 초기화식
String memo; // 초기화식이 없는 필드
{ // 초기화 블록
qty = 20;
}
Order() {
this(1); // 같은 클래스의 다른 생성자를 먼저 부른다
memo = "기본";
}
Order(int extra) {
qty = qty + extra;
memo = "추가 " + extra;
}
}
Order o = new Order();
System.out.println(o.qty + ", " + o.memo);
이 모형의 전제
열여섯 가지 조합을 이 PC의 JDK 21에서 같은 코드로 실제로 컴파일하고 실행해, 컴파일 결과와 오류 메시지, 초기화식과 블록과 생성자 본문이 실행된 순서, 출력을 대조했습니다. 실행된 순서는 같은 코드에 기록하는 줄만 끼워 넣어 확인했고, 필드가 초기화식보다 먼저 기본값(0, null)으로 채워져 있다는 것도 실행으로 확인했습니다. 단순화한 점: 모든 생성자는 본문보다 먼저 부모 클래스(여기서는 Object)의 생성자를 부르는데, 이 단계는 그림에서 뺐습니다("상속과 오버라이딩" 실습에서 다룹니다). 초기화 블록은 실무에서 자주 쓰지는 않으며, 순서를 보이려고 넣었습니다. 오류 메시지는 javac의 영어 메시지 첫 줄입니다. this(...)를 생성자의 첫 문장으로만 쓸 수 있다는 것은 JDK 21 기준이며 이 PC에서 컴파일로 확인했습니다. 더 새로운 JDK에서는 this(...) 앞에 일부 문장을 둘 수 있게 바뀌었다고 알려져 있으나 이 PC에서는 확인하지 못했습니다.
앞 실습: 배열 · 다음 실습: static과 인스턴스