생성자 (Constructor) : new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당
- 객체 초기화 : 필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 하는 것
- 생성자를 실행시키지 않고는 클래스로부터 객체를 만들 수 없음
- new 연산자에 의해 생성자가 성공적으로 실행되면 힙(heap) 영역에 객체가 생성되고 객체의 주소가 리턴됨
- 리턴된 객체의 주소는 클래스 타입 변수에 저장되어 객체에 접근할 때 이용됨
* 생성자가 성공적으로 실행되지 않고 예외(에러)가 발생하면 객체는 생성되지 않음
기본 생성자
- 모든 클래스는 생성자가 반드시 존재하며, 하나 이상을 가질 수 있음
- 클래스 내부에 생성자 선언을 생략
➡ 컴파일러가 중괄호 { } 블럭 내용이 비어있는 기본 생성자 (Default Constructor)를 바이트 코드에 자동 추가 - 클래스가 public class로 선언되면 기본 생성자에도 public이 붙음
- 클래스가 public class 없이 class로만 선언되면 기본 생성자에도 public이 붙지 않음
[public] 클래스() { }
💡 단, 클래스에 명시적으로 선언한 생성자가 한 개라도 있으면, 컴파일러는 기본 생성자를 추가하지 않음
* 명시적으로 생성자를 선언하는 이유는 객체를 다양하게 초기화하기 위해서!
생성자 선언
- 명시적 생성자 선언
클래스 ( 매개변수 선언, ... ) {
//객체의 초기화 코드
}
- 생성자는 메소드와 비슷한 모양을 가지고 있지만 리턴 타입이 없고 클래스 이름과 동일함
- 생성자 블록 내부 : 객체 초기화 코드
1) 필드에 초기값 저장
2) 메소드를 호출하여 객체 사용 전 필요한 준비 - 매개 변수 선언은 생략할 수도 있고, 여러 개를 선언할 수 있음
- 매개 변수는 new 연산자로 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달하는 역할을 함
생성자 EX)
Car myCar = new Car("그랜져", "검정", 300);
- 두 개의 매개값은 String 타입이고 마지막 매개 값은 int 타입인 것을 볼 수 있음
public class Car {
//생성자
Car(String model, String color, int maxSpeed) {
...
}
}
- 세 매개값을 생성자가 받도록 선언
- 클래스에 생성자가 명시적으로 선언되어 있을 경우 반드시 선언된 생성자를 호출해서 객체를 생성해야 함
명시적 생성자 호출 EX)
//[Car.java]
public class Car {
//생성자
Car(String color, int cc){
...
}
}
//[CarExample.java]
public class CarExample {
public static void main(String[] args) {
Car myCar1 = new Car("검정", 3000);
Car myCar2 = new Car(); //(X) 기본생성자를 호출할 수 없음.
}
}
- Car 클래스에 생성자 선언이 있기 때문에 기본생성자 Car()를 호출해서 객체를 생성할 수 없음
- Car(String color, int cc) 를 호출해서 객체를 생성해야 함
필드 초기화
- 클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정됨
- 다른 값으로 초기화를 하는 방법은 두 가지가 있음
1) 필드를 선언할 때 초기값을 주는 법
- 동일한 클래스로부터 생성되는 객체들이 모두 같은 데이터를 갖게 됨
- 객체 생성 후 변경할 수 있지만 객체 생성 시점에는 필드의 값이 모두 같음
필드 선언시 초기화 EX)
public class Korea {
String nation = "대한민국";
String name;
String ssn;
}
- Korean 클래스에 nation 필드를 선언하면서 "대한민국"으로 초기값을 줌
public static void main(String[] args) {
Korean k1 = new Korean();
Korean k2 = new Korean();
}
- Korean 클래스로부터 k2과 k2 객체를 생성하면 k1과 k2 객체의 nation 필드에는 모두 "대한민국"이 저장됨
2) 생성자에서 초기값을 주는 방법
- 객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화 되어야 한다면 생성자에서 초기화를 해야 함
- 객체의 필드는 하나가 아니라 여러 개가 있음
- 필드들을 모두 생성자에서 초기화한다면 생성자 매개 변수의 수는 객체의 필드 수만큼 선언되어야 함
➡ 실제로는 중요한 몇 개 필드만 매개 변수를 통해 초기화
➡ 나머지 필드들은 선언 시에 초기화 하거나 생성자 내부에서 임의의 값 또는 계산된 값으로 초기화
➡ 객체 생성 후 필드 값을 별도로 저장하기도 함
생성자에서의 초기화 EX)
Public class Korean {
//필드
String nation = "대한민국";
String name;
String ssn;
//생성자
public Korean(String n, String s) {
name = n;
ssn = s;
}
}
- name(이름)과 ssn(주민번호) 필드값은 클래스를 작성할 때 초기값을 줄 수 없음
- 객체 생성 시점에 다양한 값을 가져야 함 ➡ 생성자의 매개값으로 값들을 받아 초기화하는 것이 맞음
Korean k1 = new Korean("김철수", "990101-1234567");
Korean k2 = new Korean("박영희", "991231-2987654");
- "김철수"와 "박영희"는 매개 변수 n을 통해 전달됨
- "990101-1234567"과 "991231-2987654"는 매개 변수 s를 통해 전달됨
- 이 값들은 각각 name 필드와 ssn 필드의 초기값으로 사용됨
this
- 생성자의 매개 변수 이름이 너무 짧으면 코드 가독성이 좋지 않음
- 초기화시킬 필드 이름과 비슷하거나 동일한 이름을 사용하는 것이 좋음
- 관례적으로 필드와 동일한 이름을 갖는 매개 변수를 사용함
문제점)
필드와 매개변수 이름이 동일하기 때문에 생성자 내부에서 해당 필드에 접근할 수 없음
➡ 동일한 이름의 매개 변수가 사용 우선순위가 높기 때문
해결방법)
필드 앞에 this. 를 붙임
this : 객체 자신의 참조
- 우리가 우리 자신을 "나"라고 하듯 객체가 객체 자신을 this라고 함
- this.필드 는 this라는 참조 변수로 필드를 사용하는 것과 동일
this 사용 EX)
public Korean(String name, String ssn) {
this.name = name; //this.name은 필드, name은 매개변수
this.ssn = ssn; //this.ssn은 필드, ssn은 매개 변수
}