6) 객체지향 프로그래밍3 - 생성자

dev-mage·2022년 10월 7일
0

Hello Java World!

목록 보기
13/32
post-thumbnail

Java의 생성자와 this() 및 참조 변수 this

생성자(constructor)

생성자는 인스턴스가 생성될 때 호출되는 ‘인스턴스 초기화 메서드'이다. 인스턴스 변수 초기화 작업이나 인스턴스 생성 시 실행되어야 할 작업을 위해서 사용된다. 생성자 역시 메서드처럼 클래스 내에 선언되며 이름은 클래스의 이름과 같아야 한다. 메서드의 구조와 유사하지만 리턴값이 없는 점이 다른데 리턴값이 없지만 void를 사용하지 않는다. 이는 모든 생성자가 리턴값이 없으므로 생략할 수 있게 했기 때문이다.

생성자는 다음과 같이 정의한다. 생성자도 오버로딩이 가능하므로 하나의 클래스에 여러 개의 생성자가 존재할 수 있다.

// 매개변수가 없는 기본 생성자
클래스이름() {
		// 인스턴스 생성시 수행될 코드
}

// 매개변수가 있는 생성자
클래스이름(매개변수타입 매개변수이름, ...) {
		// 인스턴스 생성시 수행될 코드
}

연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다. 생성자는 단순히 인스턴스 초기화에 사용되는 조금 특별한 메서드일 뿐이다.

기본 생성자(default constructor)와 생성자 매개변수

사실 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다. 그러나 컴파일러가 제공하는 ‘기본 생성자' 덕분에 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있다. 컴파일할 때, 소스파일(~.java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 클래스이름() { }와 같이 매개변수도 없고 아무런 내용도 없는 기본 생성자를 추가하여 컴파일 한다.

하나 주의해야 할 것은 컴파일러에 의해 기본 생성자가 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이라는 점이다. 다음은 역어셈블러javap를 사용하여 생성자가 정의되어 있지 않은 Hello.class 파일을 확인해 본 것으로 소스코드에 없는 추가된 기본 생성자를 볼 수 있다.

public class Hello {}

생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다. 인스턴스마다 각기 다른 값으로 초기화되어야 하는 경우 생성자에 매개변수를 추가하면 된다. 생성자 매개변수를 이용하면 인스턴스를 생성한 후 인스턴스 변수들을 따로 초기화하는 대신 인스턴스를 생성하는 동시에 원하는 값으로 초기화 할 수 있게 된다.

생성자 this()

같은 클래스 내의 생성자 간에 서로를 호출하려면 다음의 두 조건의 만족시켜야 한다.

  1. 생성자의 이름으로 클래스 이름 대신 this 사용
  2. 한 생성자에서 다른 생성자를 호출할 때는 첫 줄에서만 호출 가능

아래 코드는 생성자를 호출할 때 지켜야하는 조건을 만족시키지 못해 에러가 발생한다.

생성자에서 다른 생성자를 첫 줄에서만 호출이 가능하도록 한 이유는 생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면 호출된 다른 생성자 내에서도 멤버 변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문이다. 다음과 같이 생성자 첫줄에 this();로 호출해주면 에러가 해결된다.

객체 자신을 가리키는 참조 변수 this

아래 코드의 color = c는 생성자의 매개변수로 선언된 c의 값을 인스턴스 변수 color에 저장한다. 이 때 변수 colorc는 이름만으로 서로 구별되므로 아무런 문제가 없다.

public class Car {
		String color;
		String gearType;
		int door;

		Car(String c, String g, int d) {
				color = c;
				gearType = g;
				door = d;
		}
}

하지만 생성자의 매개변수로 선언된 변수의 이름이 인스턴스 변수와 같을 경우에는 이름만으로 두 변수를 서로 구별할 수 없다. 이런 경우 인스턴스 변수 앞에 this를 사용한다.

public class Car {
		String color;
		String gearType;
		int door;

		Car(String color, String gearType, int door) {
				this.color = color; // this.color: 클래스의 인스턴스 변수, color: 생성자의 매개변수
				this.gearType = gearType;
				this.door = door;
		}
}

만약 this.color = color; 대신 color = color;와 같이 작성하면 둘 다 생성자의 지역 변수로 간주된다.

this는 참조 변수로 인스턴스 자신을 가리킨다(인스턴스의 주소가 저장되어 있음). 생성자를 포함한 모든 인스턴스 메서드에는 사실 자신과 관련된 인스턴스를 가리키는 참조변수 this가 지역변수로 숨겨진 채로 존재한다. 생성자의 매개변수로 인스턴스 변수들의 초기값을 제공받는 일이 많기 때문에 매개변수와 인스턴스 변수의 이름이 일치하는 경우가 자주 있다. 이 때 매개변수 이름을 다르게 하는 것보다 this를 사용해서 구별되도록 하는 것이 좋다.

참조 변수(new 연산자로 생성된 인스턴스의 주소를 담고 있는 변수)를 통해 인스턴스의 멤버에 접근할 수 있는 것처럼 this로 인스턴스 변수에 접근할 수 있다. 하지만 인스턴스 멤버만이 this를 사용할 수 있는데 클래스 메서드(static 메서드)는 인스턴스를 생성하지 않고도 호출될 수 있으므로 클래스 메서드가 호출된 시점에 인스턴스가 존재하지 않을 수 있기 때문에 클래스 메서드 내에선 this를 사용할 수 없다.

public class Hello { }

위 코드를 컴파일 한 후 IntelliJ에서 바이트코드를 확인하면 다음과 같이 this가 지역 변수로 숨겨진 것을 볼 수 있다.

변수의 초기화

변수를 선언하고 처음으로 값을 저장하는 것을 변수의 초기화라고 한다. 멤버 변수(클래스 변수, 인스턴스 변수)와 배열은 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어져 따로 초기화하지 않고 쓸 수 있지만 지역 변수는 자동적으로 초기화 되지 않으므로 사용하기 전에 반드시 초기화해야 한다.(참고: https://jypthemiracle.medium.com/weekly-java-인스턴스-변수는-기본값으로-초기화되지만-왜-지역-변수는-초기화되지-않나요-bc7bf7a1a295 )

public class Hello {
    int a;
    int b = a; // OK

    void method() {
        int x;
        int y = x; // Variable 'x' might not have been initialized 발생
    }
}

초기화 순서

1) 클래스 변수 초기화 → 인스턴스 변수 초기화
2) 자동 초기화 → 명시적 초기화 → 클래스 초기화 블럭 → 인스턴스 초기화 블럭 → 생성자

  • 명시적 초기화(explicit initialization)
    • 변수 선언과 동시에 초기화

      class Car {
      		int door = 4; // 기본형 변수의 초기화
      		Engine e = new Engine(); // 참조형 변수의 초기화
      }
  • 초기화 블럭(initialization block)
    • 클래스 초기화 블럭: 클래스 변수의 초기화에 사용( static { ... } )
    • 인스턴스 초기화 블럭: 인스턴스 변수의 초기화에 사용( { ... } )
  • 예)
public class Hello {
    static { System.out.println("클래스 초기화 블럭 실행"); }

    { System.out.println("인스턴스 초기화 블럭 실행"); }

    public Hello() { System.out.println("생성자 실행"); }

    public static void main(String[] args) {
        System.out.println("h1 인스턴스 생성 실행");
        Hello h1 = new Hello();

        System.out.println("h2 인스턴스 생성 실행");
        Hello h2 = new Hello();
    }
}
  • 결과)

  • 클래스 초기화 블럭은 처음 메모리에 로딩될 때 한번만 수행되었지만, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다 수행됨.

References

  • 자바의 정석 CHAPTER 6

0개의 댓글