[스터디]Java의 정석 10일차

Kristopher·2022년 1월 5일
0

Java 스터디

목록 보기
10/31

(Ch6) 5. 생성자 ~ 6.4 멤버변수의 초기화 시기와 순서

생성자

생성자란 인스턴스가 호출될 때 동작하는 인스턴스 초기화 메소드이다. 주로 인스턴스 변수를 초기화할 때 사용하며, 인스턴스 생성시 수행해야 할 작업을 위해 사용하기도 한다. 생성자는 메소드처럼 클래스 내에 생성되며 두가지 조건을 만족시켜야 한다.

  1. 생성자의 이름은 클래스의 이름과 동일해야 한다.
  2. 생성자의 return 값은 존재하지 않는다.

앞에서 return 값이 없는 메소드에는 void를 붙인다고 배웠다. 하지만 생성자의 경우 모두 return 값이 존재하지 않기 때문에 void를 붙이지 않고 return 값이 없는데 void가 없는 경우에 생성자라고 판단할 수 있다. 생성자에는 매개변수가 존재할 수도 있고 안 할 수도 있는데 매개변수의 개수가 다르므로 오버로딩이 허용된다고 볼 수 있다.

지금까지 봤던 예제는 모두 생성자가 포함되어 있지 않았다. 그 이유는 컴파일러가 기본 생성자(default constructor)를 생성해주기 때문인데, 기본 생성자의 형태는 아래와 같다.

Class_name () {}

코드에서 볼 수 있듯이 아무런 매개변수도 존재하지 않고 실행되는 코드도 없는 무의미한 생성자이다. 특별히 수행해야 할 작업이 없는 경우에는 기본 생성자를 사용해도 좋다.

생성자도 메소드처럼 작성하기 때문에 매개변수를 받을 수 있다. 아래의 예시를 보자.

class Phone {
	String type; // 핸드폰 기종
    String color; // 색상
    int capacity; // 용량
    
    Phone() {} // 기본 생성자 형태
    Phone(String t, String c, int ca){ // 매개변수가 있는 생성자
    	type = t;
        color = c;
        capacity = ca;
    }
}

기본 생성자를 사용하는 경우 인스턴스를 생성한 후 인스턴스 변수를 따로 초기화시켜야 하지만, 매개변수가 있다면 인스턴스를 생성할 때 원하는 값으로 초기화 할 수 있다.

생성자끼리 호출하기

생성자 간에도 호출이 가능한데, 다음 두가지 조건을 만족시켜야한다.

  1. 생성자 이름으로 클래스 이름을 직접 사용하는 것이 아니라 this로 대체한다.
  2. 다른 생성자를 호출할 때는 반드시 첫줄에서만 호출해야 한다.

사실 조건만 봐서는 어떻게 호출하는건지 감이 오지 않는다. 다음의 예시로 이해해보자.

Phone(String type){
	capacity = 128;
    Phone(type, "blue", 128); 
}

위의 예시는 두가지 조건을 모두 만족시키지 못하고 있다. 다른 생성자를 호출할 때는 반드시 첫줄에서 호출해야 하는데 두번째 줄에서 호출하고 있고, 생성자의 이름을 직접 호출하고 있는데 이를 this로 바꿔야 한다.

this라는 단어가 생성자에서 처음 소개되게 되는데 사실 지금까지 객체지향코드를 보며 this를 종종 보았다. this가 다 같은 문법인줄 알았지만 두가지 형태로 쓰인다.

1. this가 인스턴스 변수 앞에 사용되는 경우

위의 경우는 this가 인스턴스 자신을 가리키는 참조변수의 역할을 한다. 앞의 예시를 변형하여 이해해 보자.

// 지역변수 t의 값을 인스턴스변수 type에 저장하는 형태
Phone(String t, String c, int ca){ 
    type = t;
    color = c;
    capacity = ca;
}
// 매개변수 type과 인스턴스 변수 type이 구분되지 않기 때문에 인스턴스 변수 앞에 this를 붙였다.
Phone(String type, String color, int capacity){ 
    this.type = type;
    this.color = color;
    this.capacity = capacity;
}

앞에서는 지역변수와 인스턴스변수의 형태를 달리하려다보니 축약되어 의미를 파악하기 힘들었다. 하지만 둘다 풀네임을 사용하고 인스턴스변수 앞에 this를 붙여 구별하면 파악이 용이해진다. 참조변수를 통해 인스턴스 변수에 접근할 수 있는 것처럼, this를 사용하여 인스턴스 변수에 접근할 수 있다.

2. this(),this(매개변수) 형태로 같은 클래스 내의 다른 생성자를 호출할 때 생성자의 이름을 직접 입력하지 않고 this를 사용하는 경우이다.

두번재의 경우 앞의 예시로 파악했으므로 이해가 갈 것이다.

생성자에 매개변수로 참조변수를 받아 복사할 수도 있다.

Phone(Phone p){
	type = p.type;
    color = p.color;
    capacity = p.capacity;
}

위와같은 형태의 생성자가 존재할 때 인스턴스 생성시 매개변수로 다른 참조변수를 넣으면 참조변수의 값이 복사되어 새로운 인스턴스가 생성된다. 하지만 두 인스턴스는 별도의 공간에 저장된 것이므로 이후에 하나의 값을 변경하여도 복사한 개체에게 영향을 주지 않는다.

변수의 초기화

초기화는 상황에 따라 필수적이기도 하고 선택적이기도 하다. 멤버변수의 경우 초기화를 하지 않아도 변수 타입에 맞추어 기본값으로 초기화가 이루어지지만 지역변수는 사용하기 전에 반드시 초기화하여 사용해야 한다.

지금까지는 명시적 초기화 방법을 사용하였다. 용어가 어렵지만 변수 선언과 동시에 값을 지정해 주는 것을 명시적 초기화라고 한다. 복잡한 상황에서는 명시적 초기화를 사용하기 어려울 수도 있는데(많은 수의 변수를 한꺼번에 초기화해야 하는 경우 등) 이 경우에는 초기화 블록을 사용하여야 한다.

초기화 블록의 개념

초기화 블록도 변수와 마찬가지로 클래스 초기화 블록과 인스턴스 초기화 블록이 존재한다. 변수에서 static의 유무로 구분한 것처럼 초기화 블록에서도 static이 붙은 것은 클래스, 아닌 것은 인스턴스 초기화 블록으로 구분한다.

class Init_Block{
    static {// 클래스 초기화 블록의 형태}
    {// 인스턴스 초기화 블록의 형태}
}

초기화 블록의 경우 {}형태만 적어주면 된다. 초기화 블록은 생성자보다 먼저 수행된다. 그렇기에 생성자에서 반복되는 코드가 존재한다면 초기화 블록에 넣어주어 코드의 중복을 피하고 신뢰성을 높일 수 있다.

멤버변수별 초기화 시기와 순서

클래스변수와 인스턴스변수는 초기화되는 시기와 순서가 다르다. 먼저 클래스 변수는 클래스가 처음 로딩될 때 딱 한번만 초기화 된다. 처음 로딩되면 기본값 -> 명시적초기화 -> 클래스 초기화 블록의 우선순위에 따라 초기화가 이루어진다. 인스턴스변수는 인스턴스가 생성될 때마다 초기화가 이루어지며, 기본값 -> 명시적초기화 -> 인스턴스 초기화 블록 -> 생성자 순서로 초기화가 이루어진다.

class Init_Order{
	static int cv = 1;
    int iv = 1;
    
    static { cv = 2; }
    { iv=2; }
    Init_Order() {
    	iv=3;
    }
}

위의 코드를 실행시키면 클래스변수는 0(int 타입의 기본값) -> 1(명시적 초기화) -> 2(초기화 블록)의 순서에 따라 초기화가 이루어진다. 인스턴스변수는 0(int 타입의 기본값) -> 1(명시적 초기화) -> 2(인스턴스 초기화 블록) -> 3(생성자)의 순서에 따라 초기화가 이루어진다.

Reference

Java의 정석
남궁성의 정석코딩

profile
개발자 지망생입니다.

1개의 댓글

comment-user-thumbnail
2022년 1월 8일

🤗

답글 달기