TIL-23-03-04
Updated:
생성자
- 인스턴스가 생성될 때 호출되는 ‘인스턴스 초기화 메서드’
- 인스턴스 변수의 초기화 작업에 주로 사용된다.
생성자의 조건
- 생성자의 이름은 클래스의 이름과 같아야 한다.
- 생성자는 리턴값이 없다.
- 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
class Card {
Card () { // 매개변수가 없는 생성자
...
}
Card (String k, int num) { // 매개변수가 있는 생성자
...
}
}
Card c = new Card();
1. 연산자 new에 의해서 메모리(heap)에 Car클래스의 인스턴스가 생성된다.
2. 생성자 Card()가 호출되어 수행된다.
3. 연산자 new의 결과로, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장된다.
기본 생성자
- 클래스에 생성자가 하나도 정의되있지 않은 경우 컴파일러는 자동적으로 기본 생성자를 추가하여 컴파일한다.
- 클래스에 정의된 생성자가 하나라도 있는 경우, 기본 생성자는 자동 추가되지 않으므로 기본 생성자를 추가해주던가 인스턴스를 생성할 때 정의한 생성자를 사용해야 한다.
Data1 d1 = new Data1();
Data2 d2 = new Data2(); // 에러
Data1 d1 = new Data1();
Data2 d2 = new Data2(10); // OK
매개변수가 있는 생성자
- 생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다.
- 이떄, 매개변수를 갖는 생성자를 사용하면 코드가 보다 간결하고 직관적이다.
- 따라서 다양한 생성자를 제공함으로써 인스턴스 생성 후에 별도로 초기화를 하지 않아도 되도록하는 것이 좋다.
class Car {
String color;
String gearType;
int door;
Car() {} // 생성자
Car(String color, String gearType, Int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
...
Car c = new Car("white, "auto", 4);
생성자에서 다른 생성자 호출하기 - this(), this
- 다음 두 조건을 만족시킬 때, 생성자 간에 서로 호출이 가능하다.
- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫줄에서만 호출이 가능하다.
Car() {
color = "white";
gearType = "auto";
door = 4;
}
↓↓
Car() {
this("white", "auto", 4);
}
- 위 코드는 같은 일을 하지만 오른쪽 코드는 생성자 Car(String color, String gearType, Int 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;
}
- 왼쪽 코드의 ‘color = c’는 생성자의 매개변수로 선언된 지역변수 c의 값을 인스턴스변수 color에 저장한다.
- 오른쪽 코드와 같이 생성자의 매개변수 이름과 인스턴스 변수 이름을 같게 할 경우, 인스턴스 변수 앞에 ‘this’를 붙이면 된다.
- 생성자의 매개변수로 인스턴스 변수의 초기값을 제공받는 경우가 많으므로 이름이 일치하는 경우가 많다.
- 이름을 다르게 하는 것보다 ‘this’로 구별하는 것이 의미가 명확하고 이해가 쉽다.
생성자를 이용한 인스턴스의 복사
- 현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다.
Car(Car c) {
color = c.color;
gearType = c.gearType;
door = c.door;
}
- 위 코드는 Car클래스의 참조변수를 매개변수로 선언한 생성자이다.
- 매개변수로 넘겨진 참조변수가 가리키는 Car인스턴스의 인스턴스변수인 color, gearType, door의 값을 인스턴스 자신으로 복사하는 것이다.
Car(Car c) {
color = c.color;
gearType = c.gearType;
door = c.door;
}
↓↓
Car(Car c){
this(c.color, c.gearType, c.door);
}
- 생성자 ‘Car(Car c)’는 다른 생성자인 ‘Car(String color, String gearType, Int door)’를 호출하여 기존 코드를 재사용할 수 있다.
변수의 초기화
- 변수를 선언하고 처음으로 값을 저장하는 것을 ‘변수의 초기화’라 한다.
- 멤버변수는 자동 초기화가 이루어지나, 지역변수는 사용하기 전 반드시 초기화를 해야 한다.
int i=10, j=10; // 같은 타입의 변수는 콤마를 사용해 함께 선언하거나 초기화할 수 있다.
int i=10, long j=0; // 에러. 타입이 다른 변수는 함께 선언하거나 초기회할 수 없다.
int j=i;
int i=10; // 에러. 변수 i가 선언되기 전에 i를 사용할 수 없다.
멤버변수의 초기화 방법
- 명시적 초기화, 생성자, 초기화 블럭(인스턴스 초기화 블럭, 클래스 초기화 블럭)
- 복잡한 초기화 방법이 필요할 때 ‘초기화 블럭’ 또는 ‘생성자’를 사용한다.
초기화 블럭
- 클래스 초기화 블럭
- 클래스변수의 복잡한 초기화에 사용되며, 클래스가 메모리에 처음 로딩될 때 한번만 수행된다.
- 인스턴스 초기화 블럭
- 인스턴스변수의 복잡한 초기화에 사용되며, 생성자와 같이 인스턴스를 생성할 때마다 수행된다.
- 생성자보다 인스턴스 초기화 블럭이 먼저 실행된다.
class InitBlock {
static { /*클래스 초기화 블럭*/ }
{ /*인스턴스 초기화 블럭*/ }
}
- 명시적 초기화를 통해 배열 arr를 생성하고, 클래스 초기화 블럭을 이용해 배열의 각 요소들을 random()을 사용하여 임의의 값으로 채우게 했다.
- 배열이나 예외처리가 필요한 초기화에서는 추가적으로 클래스 초기화 블럭을 사용한다.
public class StaticBlockTest{
static int[] arr = new int[10];
static{
for(int i=0;i<arr.length; i++){
arr[i] = (int)(Math.random()*10) + 1;
}
}
public static void main(String[] args)
{
for(int i=0; i<arr.length; i++)
System.out.println("arr["+i+"] :" + arr[i]);
}
}
멤버변수의 초기화 시기와 순서
- 클래스 변수의 초기화 시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
- 인스턴스 변수의 초기화 시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
- 클래스 변수의 초기화 순서 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
- 인스턴스 변수의 초기화 순서 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자
클래스 변수는 인스턴스 변수보다 항상 먼저 생성되고 초기화 된다.