생성자는 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'이다.
따라서, 인스턴스 변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 할 작업을 위해서도 사용된다.
생성자 역시 메서드처럼 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다르다. 생성자 앞에 리턴값이 없음을 뜻하는 void도 적지 않는다.
1. 생성자의 이름은 클래스의 이름과 같아야 한다.
2. 생성자는 리턴값이 없다.
클래스이름(타입 변수명, 타입 변수명, ...) {
// 인스턴스 생성 시 수행될 코드,
// 주로 인스턴스 변수의 초기화 코드를 적는다.
}
class Card{
Card() { // 매개변수가 없는 생성자.
...
}
Card(String k, int num) { // 매개변수가 있는 생성자.
...
}
...
}
생성자도 오버로딩이 가능하므로 하나의 클래스에 여러개의 생성자가 존재할 수 있다.
또한, 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
생성자는 단순히 인스턴스변수들의 초기화에 사용되는 특별한 메서드일 뿐이다.
▶ 인스턴스 생성과정
Card c = new Card();
1. 연산자 new에 의해서 메모리(heap)에 Card클래스의 인스턴스가 생성된다.
2. 생성자 Card()가 호출되어 수행된다.
3. 연산자 new의 결과로, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장된다.
인스턴스를 생성할 때는 반드시 클래스 내에 정의된 생성자 중의 하나를 선택하여 지정해주어야 한다.
모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다. 클래스에 생성자가 하나도 없다면 컴파일러가 '기본 생성자'를 추가하여 컴파일 한다.
컴파일 할 때, 소스파일(*.java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 아래와 같은 내용의 기본 생성자를 추가하여 컴파일 한다.
클래스 이름() { }
Card() { }
class Data1 {
int value;
}
class Data2{
int value;
Data2(int x) { // 매개변수가 있는 생성자
value = x;
}
}
public class ConstructorTest{
public static void main(String[] args) {
Data1 d1 = new Data1();
Data2 d2 = new Data2(); // compile에러 발생
}
}
위의 예제1을 실행하면 컴파일 에러가 발생한다. 그 이유는 Data2에는 이미 생성자 Data2(int x)가 정의되어 있으므로 기본 생성자가 추가되지 않았기 때문이다. 컴파일러가 자동으로 기본 생성자를 추가해주는 경우는 '클래스 내에 생성자가 하나도 없을 때'뿐이다.
컴파일 에러가 발생하지 않도록 하기 위해서는 Data2 d2 = new Data2(10);처럼 쓰거나 클래스 Data2에 생성자 Data2()를 추가로 정의해주어야 한다.
생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다.
class Car {
String color;
String gearType;
int door;
Car() {} // 생성자
Car(String c, String g, int d) { // 생성자
color = c;
gearType = g;
door = d;
}
}
Car인스턴스를 생성할 때, 생성자 Car()를 사용한다면, 인스턴스를 생성한 다음에 인스턴스 변수들을 따로 초기화해주어야 하지만, 매개변수가 있는 생성자를 사용한다면 인스턴스를 생성하는 동시에 원하는 값으로 초기화를 할 수 있게된다.
Car c = new Car();
c.color = "white";
c.gearType = "auto";
c.door = 4;
▼
Car c = new Car("white", "auto", 4);
- 생성자의 이름으로 클래스 이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.Car() { this("white", "auto", 4); }
만약 생성자의 매개변수로 선언된 변수의 이름이 인스턴스변수와 같다면, 인스턴스변수 앞에 'this'를 사용하면 된다. 'this.color' 는 인스턴스 변수이고, color는 생성자의 매개변수로 정의된 지역변수로 구별이 가능하다.
'this'는 참조변수로 인스턴스 자신을 가리키며 인스턴스의 주소가 저장되어 있다. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있는 것처럼 'this'로 인스턴스변수에 접근할 수 있는 것이다.
생성자를 포함한 모든 인스턴스메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 'this'가 숨겨진 채로 존재한다.
현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다. 두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스 변수(상태)가 동일한 값을 갖고 있다는 것을 뜻한다.
하나의 클래스로부터 생성된 모든 인스턴스의 메서드와 클래스변수는 서로 동일하기 때문에 인스턴스간의 차이는, 인스턴스마다 각기 다른 값을 가질 수 있는 인스턴스변수 뿐이다.
Car(Car c) {
color = c.color;
gearType = c.gearType;
door = c.door;
}
위의 코드는 Car클래스의 참조변수를 매개변수로 선언한 생성자이다. 매개변수로 넘겨진 참조변수가 가리키는 Car인스턴스의 인스턴스변수인 color, gearType, door의 값을 인스턴스 자신으로 복사하는 것이다.
만약 c2가 c1을 복사하여 생성되었다면 서로 같은 상태를 갖지만, 서로 독립적으로 메모리 공간에 존재하는 별도의 인스턴스이므로 c1의 값들이 변경되어도 c2는 영향을 받지 않는다.
참고
자바의 정석(저자: 남궁성)
생성자 개념은 간단했지만 활용 부분이 어려울 것 같다. 특히 인스턴스 복사 부분은 잘 활용한다면 기존의 코드를 재사용하기 좋을 것 같은데 아직 익숙하지 않다. 생성자를 잘 사용한다면 보다 간결하고 직관적인 코드를 작성할 수 있을 것 같다.
자바를 공부할수록 '객체지향적'이라는 것이 무엇인지 점점 감이 잡혀간다. 말로만 듣던 java가 객체지향의 장점을 가졌다는것이 무엇인지 직접 배우니까 크게 와닿는다.
다음은 변수의 초기화에 대해 공부하겠다.