주인장의 프로그래밍 개발 공부방

[자바 프로그래밍 기본] 3. 클래스의 구성 (2) 멤버 변수의 초기화 본문

Java/자바 프로그래밍 기본

[자바 프로그래밍 기본] 3. 클래스의 구성 (2) 멤버 변수의 초기화

거신 2023. 5. 18. 02:08

지난 시간에 클래스의 구성요소인 멤버에 대해서 알아봤다.

그렇다면 이제 멤버를 사용하는 방법에 대해서 알아봐야 할텐데

하지만 그 전에 중요한 과정이 하나 기다리고 있다.

바로 멤버 변수의 초기화다.

 

이전에 프로그래밍 기본 지식 과정의 첫 번째 챕터에서 다뤘던 변수에서

변수의 기본 값과 초기화에 대해서 간단히 알아본 적이 있었다.

이번 시간은 그 내용의 연장선으로서 클래스의 멤버 변수의 초기화에 대해서 자세히 알아보고자 한다.

 

변수의 기본값과 초기화 내용은 아래에서 확인할 수 있다.

https://colossus-java-practice.tistory.com/5 

 

[Chapter 1 변수] 5. 변수의 기본 값과 초기화

지난 시간까지 변수가 무엇인지와 변수타입의 종류, 변수를 선언하는 방법 등을 알아봤다. 이번에는 변수의 기본 값과 초기화를 하는 방법에 대해서 알아보자. 목표 : 변수 각 타입의 기본 값이

colossus-java-practice.tistory.com

 

1. 멤버 변수의 초기화

 

이 글을 읽고 있는 사람이라면 프로그래밍에서 초기화가 무슨 의미인지

다들 알고 있을 것이라고 생각한다.

간단하게 설명하자면

변수를 선언하고 그 변수에 값을 저장하는 것초기화라고 한다.

 

멤버 변수의 초기화도 마찬가지로 동일한 의미를 가지고 있다.

멤버 변수를 선언하고 그 멤버 변수에 값을 저장하는 것이다.

그런데 일반적인 변수의 초기화와 멤버 변수의 초기화는 조금 차이점이 있다.

 

   변수의 초기화 내용에서 전역 변수지역 변수에 따라서

초기화를 해도 되고 하지 않아도 된다고 한 적이 있었다.

그것이 바로 오늘 알아보는 멤버 변수의 초기화인 것이다.

 

멤버 변수는 클래스 안에서 선언된 변수, 즉, 전역 변수를 말하는데

전역 변수는 선언하고 초기화를 하지 않아도 문제가 발생하지 않는다.

멤버 변수는 선언함과 동시에 자동으로 변수의 타입에 맞는 기본값으로 초기화가 된다.

 

예시 1)

class SampleClass {
    int x;
    
    void sampleMethod() {
    	int i;
    }
}

간단한 소스코드를 작성해 보았다.

너무나도 잘 알고 있듯이 변수 x는 클래스의 멤버 변수전역 변수이다.

변수 i는 메소드 안에서 선언된 지역 변수이다.

 

위의 소스코드는 오류가 발생하는데

바로 지역 변수i를 초기화하지 않았기 때문이다.

하지만 변수 x는 초기화하지 않았다고 해서 문제가 발생하지 않는다.

 

이는 클래스가 생성되면서 클래스의 멤버 변수인 x에 자동으로 0으로 초기화가 되기 때문이다.

이와 같이 멤버 변수는 굳이 직접적으로 초기화를 하지 않더라도

알아서 초기화를 해주기 때문에 문제가 발생하지 않는다.

"하지만 의도하지 않은 값을 방지하기 위해서 가능하면

멤버 변수라도 직접 초기화를 해주는 것을 권장하는 편이다."

 

 

2. 멤버 변수의 초기화 방법

 

그런데 멤버 변수지역 변수와 다르게 초기화를 하는 방법이 여러가지 존재한다.

멤버 변수를 초기화하는 방법에는 3가지가 있는데

 

명시적 초기화

초기화 블럭을 이용한 초기화

생성자를 통한 초기화

 

로 3가지가 존재하는데

우리는 여기에서 명시적 초기화

초기화 블럭을 이용한 초기화 방법에 대해서 자세히 알아볼 것이고

생성자를 통한 초기화 방법은 간단히 형태만 알아보고

다음 내용인 생성자에서 더 자세하게 다뤄보도록 하자.

 

 

먼저 명시적 초기화우리가 흔히 알고 있는 변수의 초기화 방법,

가장 기본적으로 알고 있는 초기화 방법이기도 하다.

변수를 선언하고 초기 값을 넣는 것명시적 초기화라고 한다.

 

예시 2)

class 볼펜심_설계도 {
    String 볼펜촉_모양 = new String();
    int 볼펜심_둘레 = 10;
}

예시 2) 와 같이 우리가 흔히 알고 있는 초기화 방법이 명시적 초기화인 것이다.

가장 직관적이고 기본적인 초기화 방법이지만

기본적인 만큼 단순하게 값을 넣는 것만 되는 방법이기 때문에

좀 더 복잡한 초기화 방법이 필요한 경우에는 적합하지 않다는 부분도 있다.

 

 

다음은 초기화 블럭을 이용한 초기화 방법이다.

초기화 블럭이라는 것이 다소 생소할 수도 있는데 먼저 예시로 만나보자.

 

예시 3)

class 볼펜심_설계도 {
    static int 볼펜심_길이;
    Color[] 잉크색;
    
    // 클래스 초기화 블럭
    static {
    	잉크색 = new Color[3];
        잉크색[0] = 검정색;
        잉크색[1] = 빨간색;
        잉크색[2] = 파란색;
        
        볼펜심_길이 = 40;
    }
    
    // 인스턴스 초기화 블럭
    {
    	볼펜심_길이 = 30;
    }
}

초기화 블럭{ } (중괄호)를 만들고 그 안에 초기화 코드를 작성하면 된다.

초기화 블럭클래스 초기화 블럭인스턴스 초기화 블럭 2종류가 있다.

 

클래스 초기화 블럭은 이름대로 클래스 변수를 초기화하는 블럭이고 { } 앞에 static을 붙여주면 된다.

마찬가지로 인스턴스 초기화 블럭인스턴스 변수를 초기화하는 블럭이다.

 

클래스와 인스턴스의 개념과 동일하게

클래스 초기화 블럭클래스가 생성될 때 초기화 블럭이 실행이 되지만

인스턴스 초기화 블럭클래스의 인스턴스가 생성될 때 초기화 블럭이 실행된다.

 

클래스 초기화 블럭은 이름대로 클래스 멤버의 초기화만 가능하고 호출이 가능하다.

(초기화 블럭 안에서는 메소드도 호출할 수 있다.)

당연하게도 인스턴스 멤버의 초기화와 호출은 불가능하다.

 

반면 인스턴스 초기화 블럭의 경우에는 당연히 인스턴스 멤버의 초기화와 호출이 가능하고,

클래스 멤버의 초기화와 호출도 가능하다.

 

그런데 예시 3)에서 중요한 부분이 있다.

클래스 초기화 블럭인스턴스 초기화 블럭 안에 있는

볼펜심_길이의 경우 두 번의 초기화가 이루어지고 있다는 점이다.

 

초기화 블럭이 실행되는 시점을 생각해보면

기본적으로 볼펜심_길이40이라는 값을 가지고 있을 것이다.

그러나 볼펜심_설계도 클래스의 인스턴스가 생성되면

인스턴스 초기화 블럭이 실행되므로 볼펜심_길이의 값은 40에서 30으로 변경된다.

이렇게 클래스의 인스턴스가 생성됨에 따라서 변수의 값을 변경할 수도 있다.

 

하지만 반대로 클래스의 인스턴스가 생성되지 않는다면

인스턴스 초기화 블럭은 실행되지 않을테니 볼펜심_길이는 항상 40이라는 값을 가지고 있을 것이다.

이는 바로 뒤에서 설명할 초기화의 순서에서 자세하게 알아보도록 하자.

 

예시 4)

class 볼펜심_설계도 {
    static int 볼펜심_길이;
    
    static {
    	볼펜심_길이 = 40;
    }
    
    {
    	볼펜심_길이 = 20;
    }
    
    public static void main(String[] args) {
    	System.out.println(볼펜심_길이);
        볼펜심_설계도 설계도 = new 볼펜심_설계도();
        System.out.println(볼펜심_길이);
    }
}

이와 관련해 재미있는 예시를 하나 보자면

예시 4)와 같은 소스코드를 작성해 실행하면 콘솔에

40

20

으로 출력이 되는데

클래스의 인스턴스의 생성 시점에 따라서 변수의 값이 바뀌는 것을 확인할 수 있다.

 

그런데 일반적으로 인스턴스 초기화 블럭의 경우에는

그 역할과 동작이 생성자와 거의 동일하기 때문에 일반적인 초기화 방식으로는 사용되지 않고

공통으로 실행되는 코드의 중복을 방지하는 방식에서 주로 사용된다.

 

 

마지막으로 생성자를 이용한 초기화 방법이다.

클래스에 대해서 공부한다면 무조건 따라오는 것이 바로 생성자(Constructor)이다.

하지만 우리는 클래스에 대해서는 배웠지만

생성자라는 것에 대해서는 아직 공부하지 못했다.

그렇기 때문에 생성자가 무엇인지에 대해서 자세하게 알아보는 것은 다음 시간으로 아껴두고

지금은 간단하게 찍먹하는 정도로 설명하고자 한다.

 

예시 5)

class 볼펜심_설계도 {
	볼펜심_설계도() {}
}

 

예시 5)에서 클래스의 생성자를 보여주고 있는데

생성자클래스명과 동일하고 메소드와 비슷한 모습이지만 리턴타입이 없다.

생성자클래스의 인스턴스가 생성되어야만 실행이 되는 녀석이기 때문에

방금 앞에서 만났던 인스턴스 초기화 블럭과 동일한 기능을 한다.

 

그래서 인스턴스 초기화 블럭에 대해서 설명할 때

생성자와 거의 동일하기 때문에 일반적인 방식으로는 사용되지 않는다고 했던 것이다.

 

예시 6)

class 볼펜심_설계도 {
    static int 볼펜심_길이;
    Color[] 잉크색;
    
    볼펜심_설계도() {
    	볼펜심_길이 = 30;
        잉크색[0] = 검정색;
        잉크색[1] = 빨간색;
        잉크색[2] = 파란색;
    }
    
    볼펜심_설계도(int 길이) {
    	볼펜심_길이 = 길이;
    }
}

 

인스턴스 초기화 블럭예시를 수정해서

생성자멤버 변수의 초기화를 한 예시이다.

당연히 static이 붙은 클래스 변수인스턴스 변수 모두 초기화가 가능하다.

그리고 생성자는 하나의 메소드이므로 오버로딩이 가능해 여러 형태의 생성자를 만들 수 있고

매개변수로 값을 받아 초기화를 하는 것도 가능하다.

 

여기에서는 생성자에 대해 자세한 설명을 하지 않고 있기 때문에

설명을 들으면 조금 생소한 부분도 분명히 있을 것이다.

그렇다고 해도 지금은 그냥 그런거구나 하고 넘어가고

멤버 변수는 이런 식으로 초기화가 가능하고 이렇게 사용할 수 있구나라고만 이해해도

충분하다고 생각한다.

더 디테일한 설명들은 다음에 이어질 글에서 하나하나 등장할테니

너무 복잡하게 생각하지 않길 바란다.

 

 

3. 각 초기화 방법들의 실행 순서

 

지금까지 알아본 3가지의 초기화 방법과 관련해서

각 초기화 방법의 초기화 시기와 순서에 대해서 알아보자.

 

먼저 클래스가 처음 생성되는 시점에서 만들어지는 클래스 변수부터 살펴보자면

 

그림 1)

클래스 변수 초기화 순서

클래스 변수의 초기화 순서는 그림 1)과 같이 실행이 된다.

가장 첫 번째는

변수가 선언되고 생성되면서 해당 변수의 타입의 기본값으로 초기화가 되고

명시적 초기화를 했을 경우 명시적 초기화가 실행되고

마지막으로 클래스 초기화 블럭이 있다면

클래스 초기화 블럭의 내용으로 초기화가 실행된다.

 

여기에서 명시적 초기화가 있더라도 기본값으로의 초기화가 실행이 되는데

이는 클래스가 처음 생성되고

변수가 생성되는 시점에서 실행이 되기 때문에

명시적 초기화가 있다고 해서 바로 명시적 초기화로 실행이 되는 것이 아니라

일단은 기본값으로의 초기화가 실행이 된다는 점을 알고 있기를 바란다.

사실 자바의 내부 동작과 관련된 부분이라서

그렇게 자세하게 알 필요는 없지만 그래도 알고 있다고 해서 손해볼 건 없으니까?

 

예시 7)

class 볼펜심_설계도 {
    
//  static int 잉크양;		// 1. 변수가 선언되면 해당 타입의 기본값으로 자동 초기화
    static int 잉크양 = 100;	// 2. 명시적 초기화가 있다면 해당 값으로 초기화
    
    // 3. 이후 클래스 초기화 블럭이 있다면 초기화 블럭의 내용으로 초기화 
    static {
    	잉크양 = 200;
    }
    
    public static void main(String[] args) {
    	System.out.println(볼펜심_설계도.잉크양);
    }
}
200

예시 7)과 같이 소스코드를 작성해서 실행하면

콘솔에 200이라는 값이 출력된다.

여기에서 클래스 초기화 블럭이 없다면 결과는 100으로 출력될 것이다.

 

다음은 클래스의 인스턴스가 생성되는 시점에서 실행되는

인스턴스 변수의 초기화 순서이다.

 

그림 2)

인스턴스 변수 초기화 순서

인스턴스 변수 초기화클래스 변수 초기화와 동일하게

기본값으로 초기화, 명시적 초기화 후에 초기화 블럭을 통한 초기화가 실행된다.

그리고 클래스 변수 초기화에서는 없었던 생성자에 의한 초기화가 추가되었다.

다시 말해, 생성자는 클래스의 인스턴스가 생성되어야 동작하는 것이다.

 

예시 8)

class 볼펜심_설계도 {
    static int 잉크양 = 200;	// 1. 클래스 변수의 명시적 초기화
//  int 볼펜심_길이;		// 1. 기본값 초기화
    int 볼펜심_길이 = 40;	// 2. 명시적 초기화
    
    // 3. 인스턴스 초기화 블럭으로 초기화
    {
    	잉크양 = 300;
    	볼펜심_길이 = 60;
    }
    
    // 4-1. 생성자로 초기화
    볼펜심_설계도() {
    	볼펜심_길이 = 10;
    }
    
    // 4-2. 매개변수가 있는 생성자로 초기화
    볼펜심_설계도(int ink) {
    	잉크양 = ink;
    }
    
    public static void main(String[] args) {
    	System.out.println("2 잉크양 : " + 볼펜심_설계도.잉크양);
        
        // 매개변수가 없는 생성자로 인스턴스를 생성
        볼펜심_설계도 설계도_인스턴스1 = new 볼펜심_설계도();
        System.out.println("3 잉크양 : " + 볼펜심_설계도.잉크양);
        System.out.println("4-1 볼펜심_길이 : " + 설계도_인스턴스1.볼펜심_길이);
        
        // 매개변수가 있는 생성자로 인스턴스를 생성
        볼펜심_설계도 설계도_인스턴스2 = new 볼펜심_설계도(100);
        System.out.println("4-2 볼펜심_길이 : " + 설계도_인스턴스2.볼펜심_길이);
    }
}
// 클래스의 인스턴스가 생성되기 전
2 잉크양 : 200

// 클래스의 인스턴스가 생성된 후
3 잉크양 : 300
4-1 볼펜심_길이 : 10

// 매개변수를 가지고 인스턴스가 생성된 후
4-2 볼펜심_길이 : 100

예시 8)의 내용이 조금 길어서 이해가 잘 되지 않을 수도 있을텐데

위와 같이 소스코드를 작성하면 아래의 결과가 출력되는 것을 확인할 수 있다.

 

클래스 변수 초기화의 경우에는 클래스가 생성되는 시점에서 실행되기 때문에

순서대로 진행되더라도 마지막에 초기화한 값으로 출력이 되지만

인스턴스 변수 초기화의 경우에는 클래스의 인스턴스 생성 유무 순서에 따라서

다른 값을 출력하는 경우도 발생할 수 있으니 이 부분을 잊어선 안되겠다.

 

마찬가지로 생성자에 따라서도 달라질 수 있다는 점도 알고 있기를 바란다.

 

 

4. 마무리

 

클래스의 멤버에는

멤버 변수멤버 메소드가 있는데 그 중 멤버 변수의 초기화 방법에 대해서 알아봤다.

변수는 선언을 하고 초기화를 해줘야 하는데

변수의 초기화에서는 변수가 지역 변수냐, 전역 변수냐에 따라서

초기화를 해줘야 하고 해주지 않아도 되는 경우가 있다.

 

멤버 변수의 경우에는 선언과 동시에 초기화를 하지 않아도

해당 변수의 타입에 맞는 기본값으로 자동으로 초기화가 된다.

이렇듯 선언과 동시에 초기화를 하지 않아도 되기 때문에

멤버 변수를 초기화하는 방법이 다양하게 존재한다.

 

멤버 변수의 초기화 방법에는 3가지가 있는데

명시적 초기화

초기화 블럭을 이용한 초기화

생성자를 통한 초기화

가 그것이다.

 

명시적 초기화는 우리가 흔히 알고 있는 변수의 초기화 방법으로

int a = 10; 처럼 변수에 직접 값을 넣는 방법이다.

직접적이면서도 간단한 방법이기 때문에 가장 기본적인 초기화 방법이지만

복잡한 방식의 초기화가 불가능하다는 부분이 있다.

이렇게 가려운 부분을 긁어줄 수 있는 것이 바로 초기화 블럭생성자가 되겠다.

 

초기화 블럭은 클래스 안에서 { } (중괄호)를 만들어 그 안에 초기화 내용을 작성한다.

초기화 블럭클래스 초기화 블럭인스턴스 초기화 블럭으로 나뉘는데

인스턴스 초기화 블럭{ }를 그대로 사용하지만

클래스 초기화 블럭{ } 앞에 static을 붙여준다.

클래스 초기화 블럭은 인스턴스 생성여부와 상관없이

클래스가 생성되는 시점에서 함께 실행이 된다.

그렇기 때문에 멤버 변수 중에서 static이 붙은 변수의 초기화만 가능하다.

반대로 인스턴스 초기화 블럭은 이름대로 클래스의 인스턴스가 생성되는 시점에서 실행되며

변수의 static 유무와 상관없이 모든 멤버 변수의 초기화가 가능하다.

 

생성자

클래스명과 동일한 리턴타입이 없는 메소드의 형태 하고 있는데

인스턴스 초기화 블럭과 마찬가지로 클래스의 인스턴스가 생성되는 시점에 실행이 된다.

역시 모든 멤버 변수의 초기화가 가능하고

생성자메소드이기 때문에 매개변수를 받을 수도 있는데

인스턴스 생성 시 전달받은 매개변수멤버 변수를 초기화할 수도 있다.

 

마지막으로 각 초기화 방법이 실행되는 순서

클래스 변수 초기화의 경우

1. 기본값으로 초기화 -> 2. 명시적 초기화 -> 3. 클래스 초기화 블럭

의 순서로 실행이 된다.

여기에서 명시적 초기화의 경우에는 명시적 초기화를 하더라도

일단 변수가 생성되는 시점에서 기본값으로 초기화가 되고

이후 명시적 초기화의 유무에 따라서 해당 값으로 초기화가 된다.

 

인스턴스 변수 초기화의 경우

1. 기본값으로 초기화 -> 2. 명시적 초기화 -> 3. 인스턴스 초기화 블럭 -> 4. 생성자

의 순서로 실행이 된다.

그리고 클래스 변수 초기화 클래스가 처음 생성되는 시점에서 실행이 되기 때문에

최초 한 번 실행이 되지만

인스턴스 변수 초기화클래스의 인스턴스가 생성될 때 마다 실행이 된다는 것을 잊지 말아야 한다.

 

여기까지 클래스의 멤버멤버 변수에 대해서 알아보고

멤버 변수를 초기화하는 방법까지 알아봤으니

클래스의 멤버 중 다른 녀석인 멤버 메소드생성자에 대해서 알아보기 위해

메소드가 무엇인지에 대해서 간단히 살펴보도록 하자.

Comments