변수의 초기화

mingseok·2022년 5월 14일
0

객체 지향 2

목록 보기
10/10
post-thumbnail

필수적이기도 하고, 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로
초기화 하는 것이 바람직하다.


멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로
초기화하지 않고, 사용해도 되지만 지역변수는 사용하기 전에 반드시 초기화해야 한다.

x, y는 인스턴스 변수이고, i, j는 지역변수이다.


인스턴스 변수 x는 초기화를 해주지 않아도 자동적으로 int형의 기본값인 0으로 초기화 되므로,
‘int y = x;’ 와 같이 할 수 있다. x의 값이 0이므로 y역시 0이 저장된다.


지역변수 i는 자동적으로 초기화 되지 않으므로, 초기화 되지 않은 상태에서 변수 j를 초기화
하는데 사용될 수 없다.

멤버변수(클래스변수와, 인스턴스변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적이다.

에러 코드.

class InitTest {
    int x; // 인스턴스 변수
    int y = x; // 인스턴스 변수

    void method1() {
        int i; // 지역 변수
        int j = i; // 에러. 지역변수를 초기화하지 않고 사용.
    }
}


각 타입의 기본값은 다음과 같다.

자료형기본값
booleanfalse
char‘\u0000’
byte, short, int0
long0L
float0.0f
double0.0d 또는 0.0
참조형 변수null


변수의 초기화에 대한 예를 몇가지 더 알아보자.

멤버변수의 초기화 방법

  1. 명시적 초기화
  2. 생성자
  3. 초기화 블럭
  • 인스턴스 초기화 블럭 : 인스턴스변수를 초기화 하는데 사용
  • 클래스 초기화 블럭 : 클래스변수를 초기화 하는데 사용

명시적 초기화

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

명시적 초기화가 간단하고 명료하긴 하지만, 보다 복잡한 초기화 작업이 필요할 때는
‘초기화 블럭’ 또는 생성자를 사용해야 한다.


초기화 블럭

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


클래스 초기화 블럭은 클래스변수의 초기화에 사용되고, 인스턴스 초기화 블럭은
인스턴스변수의 초기화에 사용된다.


초기화 블럭을 작성하려면, 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭 { } 만들고
그 안에 코드를 작성하기만 하면 된다. 그리고 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에
단순히 static 을 덧붙이기만 하면 된다.


초기화 블럭 내에는 메서드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용 할 수있다.

class InitBlock {

		static { /* 클래스 초기화블럭 입니다. */ }
	
		{ /* 인스턴스 초기화블럭 입니다. */ }
}


초기화 블럭은 클래스가 메모리에 처음 로딩 될 때 한번만 수행된다.


그리고 생성자 보다 인스턴스 초기화 블럭이 먼저 수행된다는 사실도 기억해두자.


인스턴스 초기화 블럭은 같은 코드가 중복으로 있을 때도 사용한다.

코드의 신뢰성을 높여 주고, 오류의 발생가능성을 줄여 준다는 장점이 있다.

즉, 재사용성을 높이고 중복을 제거하는 것, 이것이 바로 객체지향프로그램밍이 추구하는
궁극적인 목표이다.

Car() {
	count++; // 중복 코드
	serialNo = count; // 중복 코드
	color = "while";
	gearType = "Auto";
}

Car(String color, String gearType) {
	count++; // 중복 코드
	serialNo = count; // 중복 코드
	this.color = color;
	this.gearType = gearType;
}


중복 코드 줄이기 예제. - 한 클래스 안에 다 있다.

  1. 클래스 초기화 블럭이 가장 먼저 수행 된다.

  2. 그 다음 main메서드가 수행 된다.

  3. BlockTest인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행된다.

  4. 끝으로 생성자가 수행된다.


코드의 출력 결과를 보면 알 수 있듯이 클래스 초기화 블럭은 처음 메모리에 로딩될
때 한번만 수행되었지만, 인스턴스 최고하 블럭은 인스턴스가 생성될 때 마다 수행 되었다.

class BlockTest{
    static {
        System.out.println("static { }");
    }

    {
        System.out.println("{ }");
    }

    public BlockTest() {
        System.out.println("생성자");
    }

    public static void main(String[] args) {
        System.out.println("BlockTest bt = new BlockTest(); ");
        BlockTestbt = new BlockTest();

        System.out.println("BlockTest bt2 = new BlockTest(); ");
        BlockTestbt2 = new BlockTest();

    }
}

실행결과.
BlockTest bt = new BlockTest(); 
static { }
{ }
생성자
BlockTest bt2 = new BlockTest();
{ }
생성자


배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화 작업을 할 수 없다.
이런 경우에 추가적으로 클래스 초기화 블럭을 사용하도록 한다.

class StaticBlockTest {
    static int[] arr = new int[10];

    static {
        for (int i = 0; i < arr.length; i++) {
            // 1과 10사이의 임의의 값을 배열 arr에 저장한다.
            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]);
        }

    }

}

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

수행되는 시기와 순서에 대해서 정리해보도록 하자.

클래스변수의 초기화 시점 : 클래스가 처음 로딩될 때 단 한번 초기화 한다.
인스턴스변수의 초기화 시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
클래스변수의 초기화순서 : 기본값 → 명시적초기화 → 클래스 초기화 블럭.
인스턴스변수의 초기화 순서 : 기본값 → 명시적초기화 → 인스터스 초기화 블럭 → 생성자.

해당 클래스가 이미 메모리에 로딩 되어 있다면, 또다시 로딩하지 않는다.
물론 초기화도 다시 수행하지 않는다.

클래스변수 초기화 1~3 : 클래스가 처음 메모리에 로딩될 때 차례대로 수행됨


인스턴스변수 초기화 4~7 : 인스턴스를 생성할 때 차례대로 수행됨

  1. cv가 메모리에 생성되고, cv에는 int형의 기본값인 0이 cv에 저장된다.
  2. 그 다음에는 명시적 초기화(int cv = 1) 에 의해서 cv에 1이 저장된다.
  3. 마지막으로 클래스 초기화 블럭(cv=2) 이 수행되어 cv에는 2가 저장된다.
  4. initTest클래스의 인스턴스가 생성되면서 iv가 메모리에 존자하게 된다.

iv 역시 int형 변수이므로 기본값 0이 저장된다. 5. 명시적 초기화에 의해서 iv에 1이 저장되고 6. 인스턴스 초기화 블럭이 수행되어 iv에 2가 저장된다. 7. 마지막으로 생성자가 수행되어 iv에는 3이 저장된다.
class InitTest {

    static int cv = 1; // 명시적 초기화
    int iv = 1; // 명시적 초기화

    static { cv = 2; } // 클래스 초기화 블럭 

    { iv = 2; } // 인스터스 초기화 블럭 

    InitTest() { iv = 3; } // 생성자 

  • 공장에서 제품을 생산할 때 제품마다 생산일련번호를 부여하는 프로그램.

  • 인스턴스 블럭이 수행되어, 클래스 변수 count의 값을 1증가시킨 다음, 그 값을 인스턴스변수
    serialNo에 저장한다.

  • 새로 생성되는 인스턴스는 이전에 생성된 인스턴스보다 1이 증가된 seriaNo값을 갖게 된다.


생성자가 하나 밖에 없기 때문에 인스턴스 블럭 대신, product 클래스의 생성자를 사용해도 결과는 같지만,

코드의 의미상 모든 생성자에서 공통으로 수행되어야하는 내용이기 때문에 인스턴스 블럭을 사용하였다.


문서의 이름을 지정하는 프로그램.

class product {

    static int count = 0;
    int serialNo;

    {
        ++count;
        serialNo = count;
    }

    public product() {
    }
}

class aaa {
    public static void main(String[] args) {

        product p1 = new product();
        product p2 = new product();
        product p3 = new product();

        System.out.println("p1의 제품번호는 " + p1.serialNo);
        System.out.println("p2의 제품번호는 " + p2.serialNo);
        System.out.println("p3의 제품번호는 " + p3.serialNo);
        System.out.println("생산된 제품의 수는 모두 " + product.count + "개 입니다.");

    }
}

출력값.
p1의 제품번호는 1
p2의 제품번호는 2
p3의 제품번호는 3
생산된 제품의 수는 모두 3개 입니다.

Reference
남궁 성 지음, 『자바의 정석』, 도우출판.

profile
블로그 이사 했습니다.

0개의 댓글