ksouth9

객체지향언어(3) - 변수 본문

Java

객체지향언어(3) - 변수

ksouth9 2022. 3. 3. 01:07

선언위치에 따른 변수의 종류


변수는 클래스변수, 인스턴스변수, 지역변수 세 종류가 있다.

 

변수의 종류를 결정짓는 중요한 요소는 '변수의 선언된 위치'이므로 변수의 종류를 파악하기 위해서는 변수가 어느 영역에 선언되었는지를 확인해야 한다.

  • 멤버변수를 제외한 나머지 변수들은 모두 지역변수
  • 멤버변수 중 static이 붙은 것은 클래스 변수
  • 붙지 않은 것은 인스턴스 변수

변수 예시

class Variables{
	int iv;			//인스턴스 변수
    static int cv;		//클래스 변수(static변수, 공유변수)
    
    void method(){
    	int lv = 0;		//지역변수(메서드 영역)
    }
}

ivcv클래스 영역에 선언되어있으므로 멤버변수이다. 그 중 cv는 키워드 static과 함께 선언되어 있으므로 클래스 변수이며, iv인스턴스 변수이다. 그리고 lv는 메서드인 method()의 내부, 즉 '메서드 영역'에 선언되어 있으므로 지역변수이다.

변수의 종류 선언위치 생성시기
클래스변수 클래스영역 클래스가 메모리에 올라갈 때
인스턴스 변수 인스턴스가 생성되었을 때
지역변수 클래스 영역 이외의 영역
(메서드, 생성자, 초기화 블럭 내부)
변수 선언문이 수행되었을 때

1. 인스턴스변수

  • 클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다.
  • 인스턴스 변수의 값을 읽어 오거나 저장하기 위해서는 먼저 인스턴스를 생성해야한다.
  • 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다.
  • 고유한 상태를 유지해야하는 속성의 경우, 인스턴스변수로 선언한다.

2. 클래스변수

  • 인스턴스변수 앞에 static을 붙이면 클래스 변수가 된다.
  • 모든 인스턴스가 공통된 저장공간(변수)을 공유한다.
  • 공통적인 값을 유지해야하는 속성의 경우, 클래스변수로 선언한다.
  • 생성하지 않고 바로 사용할 수 있다. '클래스이름.클래스변수'

3. 지역변수

  • 메서드 내에서만 사용 가능
  • 메서드가 종료되면 소멸되어 사용 할 수 없다.
  • for문, while문의 블럭 내에 선언된 지역변수는, 지역변수가 선언된 블럭{} 내에서만 사용 가능
  • 블럭{}을 벗어나면 소멸되어 사용할 수 없다.

클래스변수와 인스턴스변수 예시

카드게임 예시

class Card{
	String kind;		//무늬(인스턴스변수)
        int number;		//숫자(인스턴스변수)
    
   	static int width = 100;	//폭(클래스변수)
   	static int height = 250;//높이(클래스변수)

Card인스턴스는 자신만의(독립적인) 무늬와 숫자를 유지해야 하므로 이들은 인스턴스변수로 선언, 각 카드의 폭과 높이는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언

인스턴스변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만,
클래스변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.

변수 초기화


변수를 선언하고 처음으로 값을 저장하는 것

 

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

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

변수 초기화 예시

class InitTest{
	int x;		//인스턴스변수
    int y = x;		//인스턴스변수
    
    void method1(){
    	int i;		//지역변수
        int j = i;	//에러발생. 지역변수를 초기화하지 않고 사용

위의 코드에서 x,y는 인스턴스변수이고, i,j는 지역변수이다. 그 중 x와 i는 선언만 하고 초기화를 하지 않았다. 그리고 y를 초기화 하는데 x를 사용하였고, j를 초기화 하는데 i를 사용하였다.

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

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

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

초기화 방법 예시

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

멤버변수의 초기화 방법

1. 명시적 초기화

2. 생성자

3. 초기화 블럭

 - 인스턴스 초기화 블럭 : 인스턴스변수를 초기화 하는데 사용

 - 클래스 초기화 블럭 : 클래스변수를 초기화 하는데 사용

명시적 초기화


변수를 선언과 동시에 초기화하는 것

 

class Car {
    int door = 4;		//기본형 변수의 초기화
    Engine e = new Engine();	//참조형 변수의 초기화
    //...
}

복잡한 초기화 작업이 필요할 때는 '초기화 블럭' 또는 생성자를 사용해야 한다.

초기화 블럭


클래스 초기화 블럭

  • 클래스변수의 복잡한 초기화에 사용된다.
  • 블럭{}앞에 static을 붙인다.(사용방법)
  • 메모리가 처음 로딩될 때 한번만 수행

인스턴스 초기화 블럭

  • 인스턴스의 복잡한 초기화에 사용된다.
  • 클래스 내에 블럭{}을 만들고 그 안에 코드를 작성(사용방법)
  • 생성자와 같이 인스턴스를 생성할 때 마다 수행
class InitBlock {
	static { /* 클래스 초기화 블럭 */}
    
    { /* 인스턴스 초기화 블럭 */ }
    
    //...
}

생성자 보다 인스턴스 초기화 블럭이 먼저 수행된다.

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

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

 

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

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

초기화 블럭 예시

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

}

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

1. 예제가 실행되면서 BlockTest가 메모리에 로딩될 때, 클래스 초기화 블럭이 가장 먼저 수행되어 'static {}'이 화면에 출력된다. 

2. main메서드가 수행되어 BlockTest인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행

3. 생성자(BlockTest()) 수행

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


클래스변수의 초기화시점

- 클래스가 처음 로딩될 때 단 한번 초기화 된다.

인스턴스변수의 초기화시점

- 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

클래스변수의 초기화순서

 - 기본값 → 명시적초기화 → 클래스 초기화 블럭

인스턴스변수의 초기화순서

  - 기본값 → 명시적초기화 → 인스턴스 초기화 블럭 → 생성자

 

public class InitTest {
	
	static int cv = 1;	//멤버변수(명시적 초기화)
	int iv = 1;			//멤버변수(명시적 초기화)
	
	static {
		cv = 2;		//클래스 초기화 블럭
	}
	
	{
		iv = 2;		//인스턴스 초기화 블럭
	}
	InitTest(){	//생성자
		iv = 3;
	}
	
	public static void main(String[] args) {
		
		InitTest test = new InitTest();
		System.out.println("cv = "+test.cv);
		System.out.println("iv = "+test.iv);
		
	}
	
}
//실행결과
cv = 2
iv = 3
클래스 초기화 인스턴스 초기화
기본값 명시적
초기화
클래스
초기화블럭
기본값 명시적
초기화
인스턴스
초기화블럭
생성자
cv 0 cv 1 cv 2 cv 2
iv 0
cv 2
iv 1
cv 2
iv2
cv 2
iv 3
1 2 3 4 5 6 7

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

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

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

 

1. cv가 메모리에 생성되고, cv에는 int형의 기본값인 0이 cv에 저장된다.

2. 명시적 초기화(int cv = 1)에 의해서 cv에 1이 저장된다.

3. 마지막으로 클래스 초기화 블럭(cv = 2)이 수행되어 cv에는 2가 저장된다.

4. InitTest클래스의 인스턴스가 생성되면서 iv가 메모리(heep)에 존재하게 된다.

    iv역시 int형 변수이므로 기본값 0이 저장된다.

5. 명시적 초기화에 의해서 iv에 1이 저장된다.

6. 인스턴스 초기화 블럭이 수행되어 iv에 2가 저장된다.

7. 마지막으로 생성자가 수행되어 iv에는 3이 저장된다.