변수를 선언하고 처음으로 값을 저장하는 것을 변수의 초기화라고 한다. 변수의 초기화는 경우에 따라서 필수적이기도 하고 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화 하는것이 바람직하다.

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

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

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

x, y는 지역변수이고 i와 j는 지역변수이다 인스턴스 변수x는 초기화를 해주지 않아도 자동적으로 int형의 기본값인 0으로 초기화되므로 요류가 나지 않지만  method1()의 지역변수i는 자동적으로 초기화되지 않으므로, 초기화 되지 않은 상태에서 변수j를 초기화 하는데 사용될 수 없다.

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

각 타입의 기본값(default value)는 다음과 같다.

자료형 기본값
boolean false
char '\u0000'
byte, short, int 0
long 0L
float 0.0f
double 0.0d 또는 0.0
참조형변수 null

 

 

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

선언예 설 명
int i =10;
int j=10;
int형 변수 i를 선언하고 10으로 초기화 된다.
int형 변수 j를 선언하고 10으로 초기화 된다.
int i=10,  j=10; 같은 타입의 변수는 콤마(,)를 사용해서 함께 선언하거나 초기화 할 수 있다.
int i=10;, long j=10; 에러. 타입이 다른 변수는 함께 선언하거나 초기화할 수 없다.
int i=10;
int j=i;
변수 i에 저장된 값으로 변수 j를 초기화 한다.
변수 j는 i의 값인 10으로 초기화 된다.
int j=i;
int i=10;
에러. 변수 i가 선언되기 전에 i를 사용할 수 없다.

 

멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
	- 인스턴스 초기화 블럭 :인스턴스변수를 초기화 하는데 사용.
	- 클래스 초기화 블럭 :클래스변수를 초기화 하는데 사용.

 

명시적 초기화(explicit initialization)

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

 

초기화 블럭(initialization block)

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

클래스 초기화 블럭 클래스변수의 복잡한 초기화에 사용된다.
인스턴스 초기화 블럭 인스턴스변수의 복잡한 초기화에 사용된다.

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

 

초기화 블럭 내에는 매서드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있으므로, 초기화 작업이 복잡하여 명시적 초기화만으로는 부족한 경우 초기화 블럭을 사용한다.

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

클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화블럭은 생성자와 같이 인스턴스를 생성할 때 마다 수행된다.

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

클래스가 처음 로딩될 때 클래스변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화블럭이 클래스변수들을 초기화하게 되는 것이다.

 

인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야 하는 코드를 넣는데 사용한다.

Car() {
	count++;
	serialNo = count;
	color="White";
	gearType ="Auto";
}
Car(String color, String gearType) {
	count++;
	serialNo = count;
	this.color=color;
	this.gearType = gearType;
}

모든 생성자에 공통적으로 수행되어야 하는 문장들이 있을 때, 이 문장들을 각 생성자마다 써주기 보다는 아래와 같이 인스턴스 블럭에 넣어주면 코드가 보다 간결해진다.

{
	count++;
	serialNo = count;
}
Car() {
	clor = "White";
	gearType="Auto";
}

Car(String color, String gearType) {
	this.color=color;
	this.gearType = gearType;
}

코드의 중복을 제거하는 것은 코드의 신뢰성을 높여 주고, 오류의 발생가능성을 줄여 준다는 장점이 있다.

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

프로그래머는 이와 같은 객체지향언어의 요소들을 잘 이해하고 활용하여 코드의 중복을 최대한 제거해야 한다.

 

public 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(); ");
		BlockTest bt = new BlockTest();
		
		System.out.println("BlockTest bt2 = new BlockTest(); ");
		BlockTest bt2 = new BlockTest();
	}
}

예제가 실행되면서 BlockTest가 메모리에 로딩될 때, 클래스 초기화 블럭이 가장 먼저 수행되어 static {}이 화면에 출력된다. 그 다음에 main메서드가 수행되어 BlockTest인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행되고, 끝으로 생성자가 수행된다.

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

public 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]);
	}
}
arr[0] :7
arr[1] :2
arr[2] :8
arr[3] :1
arr[4] :7
arr[5] :8
arr[6] :7
arr[7] :7
arr[8] :5
arr[9] :10

명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초기화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했다.

이처럼 배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화작업을 할 수 없다.

이런 경우에 추가적으로 클래스 초기화 블럭을 사용하도록 한다.

인스턴스변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화블럭을 사용한다.

 

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

클래스변수의 초기화시점	클래스가 처음 로딩될 때 단 한번 초기화 된다.
인스턴스변수의 초기화시점	인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

클래스변수의 초기화순서	기본값 > 명시적초기화 > 클래스초기화블럭
인스턴스변수의 초기화순서	기본값 > 명시적초기화 > 인스턴스 초기화 블럭 > 생성자

 

프로그램 실행도중 클래스에 대한 정보가 요구될 때, 클래스는 메모리에 로딩된다.

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

클래스의 로딩 시기는 JVM의 종류에 따라 다를 수 있다.

필요할때 로딩하는lazyload와 실행효율을 높이기위해 미리 로딩하는preload가 있다.

 

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

 

클래스 초기화 인스턴스 초기화
기본값 명시적
초기화
클래스
초기화블럭
기본값 명시적
초기화
인스턴스
초기화블럭
생성자
cv : 0

1

2

cv : 2
iv : 0
cv : 2
iv : 1
cv : 2
iv : 2
cv : 2
iv : 3
1 2 3 4 5 6 7

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

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

※클래스변수는 항상 인스턴스변수보다 항상 먼저 생성되고 초기화 된다.

+ Recent posts