생성자란?
o 생성자란 인스턴스가 생성될 때 호출되는 ‘인스턴스 초기화 메소드’이다. 그렇기에 인스턴스 변수의 초기화 작업 및 인스턴스 생성 시 실행되어야 할 작업을 위해서 사용된다.
o 생성자도 메소드처럼 클래스 내에 선언되며 리턴 값이 없다. 생성자는 리턴 값이 없는 void 키워드를 적지 않기에 아무것도 적지 않는다. 생성자의 조건은 다음과 같다.
1. 생성자의 이름은 클래스의 이름과 같아야 한다.
2. 생성자는 리턴 값이 없다. (void 키워드 작성하지 않는다.)
class Card {
//매개 변수가 없는 생성자
Card(){
}
// 매개 변수가 있는 생성자
Card(String k, int num){
}
}
|
o 생성자도 오버로딩이 가능하기에 하나의 클래스에서 여러 개의 생성자가 존재할 수 있다.
o 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것은 아니다. 생성자는 단순히 인스턴스 변수 초기화에 사용되는 조금 특별한 메소드이다.
o 인스턴스를 생성하기 위해 사용되었던 ‘클래스이름( )’이 생성자이다. 인스턴스를 생성할 때는 반드시 클래스내에 정의되어 있는 생성자 중 하나를 선택하여 지정해주어야 한다. 인스턴스를 생성하는 수행 과정은 다음과 같다.
Card c = new Card();
1. 연산자 new에 의해메모리(heap)에 Card클래스의 인스턴스가 생성된다.
2. 생성자 Card()가 호출되어 수행된다.
3. 연산자 new의 결과로 생성된 Card인스턴스 주소가 반환되어 참조변수 c에 저장된다.
기본 생성자(default constructor)
o 모든 클래스는 하나 이상의 생성자가 있어야 하며 생성자가 정의 되어있지 않은 경우에는 컴파일러가 자동으로 아무 내용이 없는 ‘기본 생성자(default constructor)’를 추가하여 컴파일한다. 지금까지 생성자 없이 인스턴스 생성이 가능했던 이유이기도 하다.
기본 생성자 예) 클래스 이름( ) { } -> Card ( ) { }
o 기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다. 하나라도 정의되어 있는 경우 기본 생성자를 추가하지 않는다.
public class Test {
public static void main(String[] args) {
Data1 d1 = new Data1();
Data2 d2 = new Data2(10);
// 생성자가 정의되어 있기에 기본생성자가 생성되지 않음
// 정의된 생성자의 형식에 맞추어야 한다.
// Data2 d2 = new Data2();
}
}
class Data1{
int value;
}
class Data2{
int value;
Data2(int x){
value = x;
}
}
|
매개변수가 있는 생성자
o 생성자도 메소드처럼 매개변수를 선언하여 호출 시 값을 받아 인스턴스 초기화 작업에 사용할 수 있다. 인스턴스마다 각각 다른 값으로 초기화 되어야하는 경우가 많아 매개변수를 활용한 초기화는 매우 유용하다.
o 매개변수가 없는 생성자의 경우 인스턴스 생성 후 따로 초기화를 해주어야 하지만, 매개변수가 있는 생성자를 사용하면 인스턴스 생성과 동시에 초기화를 할 수 있게 되어 코드를 보다 간결하고 직관적으로 만든다.
public class Test {
public static void main(String[] args) {
// c1, c2는 같은 형식의 인스턴스 생성
Car c1 = new Car();
c1.color = "white";
c1.gearType = "auto";
c1.door = 4;
Car c2 = new Car("black","auto",10); // 코드가 더욱 간결해진다.
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c1.color,c1.gearType,c1.door);
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c2.color,c2.gearType,c2.door);
}
}
class Car {
String color;
String gearType;
int door;
Car() { }
Car(String c, String g, int d){
color = c;
gearType = g;
door = d;
}
}
|
생성자에서 다른 생성자 호출하기 – this(), this
o 클래스 멤버 간 서로 호출이 가능한 것처럼 생성자 간에도 서로 호출이 가능하나 아래 조건을 만족해야 한다.
1. 생성자의 이름으로 클래스이름 대신 this를 사용한다.
2. 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
(this( )가 생성자 맨 첫 줄이 되어야 한다.)
o 생성자에서 다른 생성자 호출 시 첫 줄에서만 가능하도록 한 이유는 생성자에서 초기화 작업 도중 다른 생성자를 호출하게 되면 호출된 다른 생성자에서도 초기화 작업을 하기에 이전에 초기화 작업이 무의미해질 수 있기 때문이다.
public class Test {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car("blue");
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c1.color,c1.gearType,c1.door);
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c2.color,c2.gearType,c2.door);
}
}
class Car {
String color;
String gearType;
int door;
/*
에러1. 첫번째 줄에서 호출해야 함
에러2. this를 사용해서 호출해야 함
Car() {
door=5;
Car("white","auto",4);
}
*/
Car() {
this("white","auto",4);
}
Car(String color) {
this(color,"auto",4);
}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
|
Car(String c, String g, int d){
color = c;
gearType = g;
door = d;
}
|
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
|
o 생성자의 매개변수가 인스턴스 변수와 이름이 구별이 된다면 문제가 없으나 오른쪽 코드처럼 인스턴스 변수와 생성자 매개변수의 이름이 구별이 되지 않는 경우 인스턴스 변수 앞에 ‘this’를 사용해야 한다.
o this없이 color = color로 정의하면 둘 다 지역변수로 간주된다. 생성자의 매개변수로 인스턴스 변수들의 초기값을 제공받는 경우가 많기에 이름을 일치하는 경우가 자주 있다. this를 사용해서 구별하는 것이 의미가 명확하고 이해하기 쉽다.
o this는 인스턴스 자신을 가리키는 참조 변수이다. 참조변수로 인스턴스 멤버 변수에 접근하는 것이며 인스턴스 멤버만 사용할 수 있다. static메소드에서 인스턴스 멤버들을 사용할 수 없는 것처럼 this역시 사용할 수 없다.
o static메소드는 인스턴스 생성 없이 호출될 수 있으므로 static메소드가 호출된 시점에 인스턴스가 존재하지 않을 수 있기 때문이다.
o 생성자를 포함한 모든 인스턴스 메소드에는 자신을 가리키는 참조변수 ‘this’가 지역변수로 숨겨진 채로 존재한다. 인스턴스 메소드는 특정 인스턴스와 관련된 작업을 하기에 자신과 관련된 인스턴스 정보가 필요하기 때문이다.
o 정리
this |
- 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있다. - 모든 인스턴스 메소드에 지역변수로 숨겨진 채로 존재한다. |
this(), this(매개변수) |
- 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다. |
생성자를 이용한 인스턴스의 복사
o 현재 사용하는 인스턴스와 같은 상태(모든 인스턴스 변수 값이 동일)를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다.
o 하나의 클래스에서 생성된 모든 인스턴스 메소드, 클래스 변수는 서로 동일하기에 인스턴스 간의 다른 값을 가질 수 있는 것은 인스턴스 변수뿐이다.
public class Test {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car(c1); // c1의 본사복을 c2에 생성한다.
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c1.color,c1.gearType,c1.door);
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c2.color,c2.gearType,c2.door);
c1.color = "black";
c1.door=100;
// c1의 값을 변경했으나 복사한 c2는 별개의 인스턴스이기에 영향을 받지 않음
System.out.println("\n c1의 값을 변경 후");
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c1.color,c1.gearType,c1.door);
System.out.printf("c1의 color=%s, gearType=%s, door=%d\n",c2.color,c2.gearType,c2.door);
}
}
class Car {
String color;
String gearType;
int door;
Car() {
this("white","auto",4);
}
Car(Car c) {
this(c.color, c.gearType, c.door);
/* 아래,위의 문장은 같은 코드이나 위의 문장이 더 간결하다.
color = c.color;
gearType = c.gearType;
door = c.door;
*/
}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
|
- 다른 인스턴스의 값을 복사하더라도 결국엔 별도의 인스턴스이기에 같은 값을 갖지만 서로 독립적인 메모리 공간에 존재하기에 복사 대상의 인스턴스에서 값이 변경되어도 복사한 인스턴스는 영향이 없다.
o 인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야 한다.
1. 클래스 – 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 – 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?
'언어 > 자바의 정석' 카테고리의 다른 글
[자바의 정석] Chapter 7 객체 지향 프로그래밍II(1) - 상속 (0) | 2021.02.16 |
---|---|
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(6) - 변수의 초기화 (0) | 2021.02.04 |
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(4) - 오버로딩 (0) | 2021.02.04 |
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(3) (0) | 2021.02.03 |
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(2) (0) | 2021.02.03 |