자바 기본형과 참조형 - 2

계리·2024년 2월 19일
0

변수와 초기화

변수의 종류

멤버 변수와 지역 변수가 있다.

간략하게 말하면 멤버 변수는 클래스 안에서 선언된 변수, 메서드 안에서 선언된 것이 지역 변수이다.

  • 멤버 변수 : 클래스 안에서 선언
  • 지역 변수 : 메서드 안에서 선언. 매개변수도 지역 변수 중 하나이다.

멤버 변수 예시

public class Student {
	String name;
    int age;
    int grade;
}

name, age, grade는 멤버 변수이다.


지역 변수 예시

public class ClassStart3 {
	public static void main(String[] args) {
		Student student1;
		student1 = new Student();
		Student student2 = new Student();
	}
}

student1, student2는 지역 변수이다.


public class MethodChange1 {
	public static void main(String[] args) {
		int a = 10;
		System.out.println("메서드 호출 전: a = " + a);
        changePrimitive(a);
        System.out.println("메서드 호출 후: a = " + a);
	}
    
	public static void changePrimitive(int x) {
		x = 20;
	}
}

a, x(매개변수)는 지역 변수이다.

지역 변수는 말 그대로 어느 지역에서만 사용하는 변수(메서드를 지역이라고 생각하면 된다.)이다. x는 changePrimitive 메서드에서만 사용이 되고 해당 메서드가 끝나면 제거된다. a도 마찬가지로 main 메서드가 끝나면 제거된다.


변수의 값 초기화

  • 멤버 변수 : 자동 초기화
    • 인스턴스의 멤버 변수는 인스턴스를 생성할 때 자동 초기화된다.
    • int = 0, boolean = false, 참조형 = null(null값은 참조할 대상이 없다는 뜻으로 사용된다.)
    • 개발자가 초기 값을 직접 지정할 수 있다.
  • 지역 변수 : 수동 초기화
    • 지역 변수는 항상 직접 초기화해야 한다.

예시

package ref;

public class InitData {
	int value1; //초기화 하지 않음
	int value2 = 10; //10으로 초기화
}

value1은 초기화 하지 않았고, value2는 초기화 했다.


package ref;

public class InitMain {
	public static void main(String[] args) {
      InitData data = new InitData();
      System.out.println("value1 = " + data.value1);
      System.out.println("value2 = " + data.value2);
	}
}
실행 결과
value1 = 0
value2 = 10

value1은 초기화 하지 않았지만 결과 값을 보면 0으로 자동 초기화 된것을 볼 수 있다.
value2는 10으로 초기화 했기 때문에 결과 값이 10으로 나왔다.


null

참조형 변수는 해당 값의 주소 값을 가지고 있다고 했다. null은 아직 주소 값이 없다, 비워져있다, 가리키는 대상이 없다 혹은 가리키는 대상을 나중에 입력하고 싶다 등 여러 가지 말로 표현할 수 있다. 반대로 얘기하면 주소 값을 받을 준비가 되었다고도 해석할 수 있을 것 같다. 보통 null은 값이 존재하지 않다, 없다는 뜻으로 사용한다.

예제

package ref;

public class Data {
	int value;
}

이전에 만들었던 Data 클래스가 있기 때문에 따로 만들지는 않는다.


package ref;

public class NullMain1 {
	public static void main(String[] args) {
      Data data = null;
      System.out.println("1. data = " + data);
      
      data = new Data();
      System.out.println("2. data = " + data);
      
      data = null;
      System.out.println("3. data = " + data);
	}
}
실행 결과
1. data = null
2. data = ref.Data@x001
3. data = null

Data data = null;

처음 data변수에 null값을 할당했다. 그래서 가리키는 것이 없는 상태이다.


data = new Data();

두 번째 data 변수에는 객체를 생성했기 때문에 가리키는 객체가 생겨 참조할 수 있게 되었다.


data = null;

세 번째 data변수에 다시 null을 할당하여 가리키는 객체가 사라져 참조할 수 없게 됐다.


GC(Garbage Collection) - 참조하지 않는 인스턴스는 GC에 의해 제거된다.

null을 할당하여 아무도 참조하지 않게 된다면은 아무도 접근을 할 수가 없다. 그러면 인스턴스는 GC에 의해 제거된다. 그래서 x001도 GC에 의해 제거된다.

이렇게 아무도 참조하지 않은 인스턴스는 메모리만 차지하게 된다. C언어 같은 경우 이와 같이 사용하지 않은 객체를 직접 제거하지 않으면 나중에 메모리가 쌓여 메모리가 부족하게 되기 때문에 직접 제거해야 한다. 하지만 자바는 JVM의 GC(가비지 컬렉션)가 더 이상 사용하지 않은 인스턴스라 판단되는 것들은 자동으로 삭제를 해줘 메모리 공간을 확보해준다.

만약 객체가 참조하는 곳이 있다면 JVM이 종료될 때 까지 해당 객체는 계속 생존한다. 그런데 중간에 해당 객체를 참조하는 곳이 모두 사라지면 그때 JVM은 필요 없는 객체로 판단하고 GC를 사용해서 제거한다.

NullPointException

개발자들이 많이 겪는 예외라고 한다. 점(. dot)을 이용해서 해당 객체의 값에 접근을 하려고 하는데 해당 객체에는 null값이 할당되어 있을 때 점(. dot)을 통해 접근을 시도하여 생긴 예외라고 할 수 있다.

지인의 집에 필요한걸 얻으려 가기 위해 집 주소 받고 해당 집 주소를 따라가 도착했는데 그 집에 아무도 살지 않아서 필요한걸 얻지 못해서 문제가 생겼다. 라고 생각하며 비유를 해봤다.

예제

package ref;

public class NullMain2 {
	public static void main(String[] args) {
      Data data = null;
      data.value = 10;// NullPointerException 예외 발생
      System.out.println("data = " + data.value);
	} 
}

위 코드에서 data.value = 10;을 아래와 같이 해석할 수 있다.

data.value = 10
null.value = 10 //data에는 null 값이 들어있다.

null값은 결국 참조할 수 있는 주소가 없다는 뜻이다. 따라서 참조할 객체(인스턴스)가 없는 것이므로 java.lang.NullPointerException이 발생하고 프로그램이 종료된다. 참고로 예외가 발생한 후 로직은 수행되지 않는다.

 Exception in thread "main" java.lang.NullPointerException: Cannot assign field
 "value" because "data" is null
     at ref.NullMain2.main(NullMain2.java:6)

멤버변수와 null

위 코드처럼 지역 변수인 경우에는 그나마 금방 찾을 수 있었지만 멤버 변수인 경우에는 쉽지 않기 때문에 주의가 필요하다.

기존의 Data 클래스를 사용한다.

package ref;

public class Data {
	int value;
}

package ref;

public class BigData {
     Data data;
	int count;
}

package ref;

public class NullMain3 {
     public static void main(String[] args) {
         BigData bigData = new BigData();
         System.out.println("bigData.count=" + bigData.count);
         System.out.println("bigData.data=" + bigData.data);
         //NullPointerException
         System.out.println("bigData.data.value=" + bigData.data.value);
     }
}
실행 결과
bigData.count=0
bigData.data=null
Exception in thread "main" java.lang.NullPointerException: Cannot read field
 "value" because "bigData.data" is null
     at ref.NullMain3.main(NullMain3.java:10)

BigData bigData = new BigData();로 인스턴스를 생성이 됐고 해당 인스턴스의 멤버 변수들은 자동 초기화를 하게 됐다.

  • Data data는 참조형이므로 null로 초기화
  • int count는 숫자형이므로 0으로 초기화

이런 상태에서 bigData.count는 0을 가져오게 된다. 하지만 bigData.data는 null값을 할당받은 상태이기 때문에 참조할 곳이 없는 상태에서 bigData.data.value의 값을 가지고 오려고 시도하였고 그래서 java.lang.NullPointerException이 발생하게 된다.

bigData.data.value의 값을 가지고 오려면 먼저 bigData.data의 값을 할당을 해줘야한다.


package ref;

public class NullMain4 {
     public static void main(String[] args) {
         BigData bigData = new BigData();
         bigData.data = new Data();
         System.out.println("bigData.count=" + bigData.count);
         System.out.println("bigData.data=" + bigData.data);
         System.out.println("bigData.data.value=" + bigData.data.value);
	} 
}

실행 결과

bigData.count=0
bigData.data=ref.Data@x002
bigData.data.value=0

실행 과정

bigData.data.value
x001.data.value //bigData는 x001 참조값을 가진다.
x002.value //x001.data는 x002 값을 가진다.
0 // 최종 결과

NullPointerException 이 발생하면 null 값에 . (dot)을 찍었다고 생각하면 문제를 쉽게 찾을 수 있다.


참고

  • 김영한의 실전 자바 기본편
profile
gyery

0개의 댓글