1. 변수의 초기화
변수를 선언하고 처음으로 값을 저장하는 것을 '변수의 초기화'라고 한다.
변수의 초기화는 경우에 따라서 필수적이기도 하고 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화 하는 것이 바람직하다.
멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 한다.
(1) 예문 - 지역변수의 초기화가 필요한 이유
1
2
3
4
5
6
7
8
9
10
11
|
class InitTest
{
int x; // 인스턴스 변수
int y = x; // 인스턴스 변수
void method1()
{
int i; // 지역변수
int j = i; // 지역변수인 i가 초기화 되지 않았기 때문에 에러발생
}
}
|
cs |
2. 멤버변수
멤버변수(클래스 변수와 인스턴스 변수)와 배열의 초기화는 선택적이지만 지역변수의 초기화는 필수적이다.
[각 타입의 기본값]
- boolean / false
- char / '\u0000'
- byte, short, int / 0
- long / 0L
- float / 0.0f
- double / 0.0d 또는 0.0
- 참조형 변수 / null
3. 멤버변수의 초기화 방법
(1) 명시적 초기화 (explicit initialization)
- 변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다.
(2) 생성자 (construct)
(3) 초기화 블럭 (initialization block)
- 인스턴스 초기화 블럭 : 인스턴스 변수를 초기화 하는데 사용
- 클래스 초기화 블럭 : 클래스 변수를 초기화 하는데 사용
1
2
3
4
5
6
7
|
class Car
{
int door = 4;
Engine e = new Engine();
// ...
}
|
cs |
명시적 초기화가 간단하고 명료하지만 보다 복잡한 초기화 작업이 필요할 때에는 '초기화 블럭' 또는 생성자를 사용한다.
4. 초기화 블럭
초기화 블럭에는 '클래스 초기화 블럭'과 '인스턴스 초기화 블럭' 두 가지 종류가 있다.
클래스 초기화 블럭은 클래스 변수의 초기화에 사용되고, 인스턴스 초기화 블럭은 인스턴스 변수의 초기화에 사용된다.- 클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용- 인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용
인스턴스 초기화 블럭은 클래스 내부에 블럭{} 을 생성 후 그 안에 코드를 작성하면 되고, 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 static 을 덧붙여주면 된다.
초기화 블럭 내에는 메소드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있으므로 초기화 작업이 복잡하여 명시적 초기화만으로는 부족한 경우 초기화 블럭을 사용한다.
(1) 예문1
1
2
3
4
5
6
7
|
class InitBlock
{
static { /* 클래스 초기화블럭 입니다. */ }
{ /*인스턴스 초기화블럭 입니다.*/}
// ...
}
|
cs |
클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한 번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행된다.
그리고 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다.
참고) 클래스가 처음 로딩될 때 클래스 변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화 블럭이 클래스 변수들을 초기화하게 되는 것이다.
인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행되어야 하는 코드를 넣는데 사용한다.
(2) 예문2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Car()
{
count++;
serialNo = count;
color = "White";
gearType = "Auto";
}
Car(String color, String gearType)
{
count++;
serialNo = count;
this.color = color;
this.gearType = gearType;
}
|
cs |
만약 이렇게 count++; 과 serialNo = count; 처럼 클래스의 모든 생성자에 공통으로 수행되어야 하는 문장들이 있을 때 이 문장들을 생성자마다 각각 주는 것보다 아래와 같이 인스턴스 블럭에 넣어주면 코드가 보다 간결해진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
count++;
serialNo = count;
}
Car()
{
color = "White";
gearType = "Auto";
} Car(String color, String gearType)
{
this.color = color;
this.gearType = gearType;
}
|
cs |
이처럼 코드의 중복을 제거하는 것은 코드의 신뢰성을 높여주고, 오류의 발생 가능성을 낮춰준다. 즉, 재사용성을 높이고 중복을 제거하는 것이 바로 객체지향 프로그래밍이 추구하는 궁극적인 목표이다.
(3) 예문3
1
2 3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 |
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();
}
}
|
cs |
- 실행결과
static { }
BlockTest bt = new BlockTest();
{ }
생성자
BlockTest bt2 = new BlockTest();
{ }
생성자
- 설명
코드가 실행되면서 BlockTest가 메모리에 로딩 될 때 클래스 초기화 블럭이 가장 먼저 실행되어 'static { }' 이 출력된다.
그 다음 main 메소드가 실행되어 BlockTest 인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 실행되고 그 다음 생성자가 실행된다. 위의 결과에서도 알 수 있듯이 클래스 초기화 블럭은 처음 메모리에 로딩될 때 한 번만 실행되었고, 인스턴스 초기화 블럭은 인스턴스가 생성될 때마다 실행된다.
클래스 초기화 블럭 → 메인 메소드 → 인스턴스 생성 → 인스턴스 초기화 블럭 → 생성자 → 메인 메소드 → 인스턴스 생성 → 인스턴스 초기화 블럭 → 생성자
(4) 예문4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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]);
}
}
|
cs |
명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초가화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했다.
이처럼 배열이나 예외적인 처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화 작업을 할 수 없다. 이런 경우 추가적으로 클래스 초기화 블럭을 사용한다.
참고) 인스턴스 변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화 블럭을 사용한다.
5. 멤버변수의 초기화 시점과 순서
(1) 초기화 시점
- 클래스 변수 : 클래스가 처음 로딩될 때 단 한 번 초기화
- 인스턴스변수 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
(2) 초기화 순서
- 클래스 변수 : 기본값 → 명시적 초기화 → 클래스 초기화 블럭
- 인스턴스 변수 : 기본값 → 인스턴스 초기화 블럭 → 생성자
프로그램 실행 중 클래스에 대한 정보가 요구될 때 클래스는 메모리에 로딩된다. 예를 들면, 클래스 멤버를 사용했을 때 인스턴스를 생성할 때 등이 이에 해당한다. 하지만 해당 클래스가 이미 메모리에 로딩되어 있다면 또 다시 로딩하지 않고 초기화도 다시 수행되지 않는다.
참고) 클래스의 로딩 시기는 JVM의 종류에 따라 다를 수 있다. 클래스가 필요할 때 바로 메모리에 로딩되도록 설계가 되어있는 것도 있고, 실행 효율을 높이기 위해 사용될 클래스들을 프로그램이 시작될 때 미리 로딩하도록 되어있는 것도 있다.
(3) 예문
1
2
3
4
5
6
7
8
9
10
11
12
|
class InitTest
{
static int cv = 1;
int iv = 1; // 명시적 초기화(explicit initialization)
static { cv = 2; } // 클래스 초기화 블럭
{ iv = 2; } // 인스턴스 초기화 블럭
InitTest()
{ // 생성자 iv = 3;
}
}
|
cs |
- 실행 순서
클래스 초기화
1 기본값 cv 0
2 명시적 초기화 cv 1
3 클래스 초기화 블럭 cv 2
인스턴스 초기화
4 기본값 cv 2 iv 0
5 명시적 초기화 cv 2 iv 1
6 인스턴스 초기화 블럭 cv 2 iv 2
7 생성자 cv 2 iv 3
- 클래스변수 초기화 (1~3) : 클래스가 처음 메모리에 로딩될 때 차례대로 수행
- 인스턴스변수 초기화(4~7) : 인스턴스를 생성할 때 차례대로 수행
★클래스 변수는 항상 인스턴스 변수보다 먼저 생성되고 초기화 된다.
1. cv가 메모리(method area)에 생성되고, cv에는 int형의 기본값인 0이 cv에 저장된다.
2. 그 다음에는 명시적 초기화(int cv=1)에 의해서 cv에 1이 저장된다.
3. 마지막으로 클래스 초기화 블럭(cv=2)이 수행되어 cv에는 2가 저장된다.
4. InitTest클래스의 인스턴스가 생성되면서 iv가 메모리(heap)에 존재하게 된다.
iv 역시 int형 변수이므로 기본값 0이 저장된다.
5. 명시적 초기화에 의해서 iv에 1이 저장되고
6. 인스턴스 초기화 블럭이 수행되어 iv에 2가 저장된다.
7. 마지막으로 생성자가 수행되어 iv에는 3이 저장된다.
'Java > Day13' 카테고리의 다른 글
[Java] 난수 - 숫자 야구게임 (0) | 2021.11.16 |
---|---|
[Java] 난수 - 두 수 사이의 난수 생성 & 로또 숫자 생성 (0) | 2021.11.15 |