
1️⃣ 기본형 vs 참조형 1 - 시작
자바의 참조형은 매우 어렵고 이해하기 힘들다. 따라서 기본형과 참조형을 심도있게 정리해보고자 한다.
변수의 데이터 타입은 크게 두 가지로 나뉜다. 사용하는 값을 변수에 직접 넣을 수 있는 기본형, 그리고 이전에 본 Student student1
과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있다.
- 기본형(Primitive Type): int , long , double , boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형이라 한다.
- 참조형(Reference Type): Student student1 , int[] students 와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형이라 한다. 참조형은 객체 또는 배열에 사용된다
즉, 기본형 변수에는 직접 사용할 수 있는 값이 들어있지만, 참조형 변수에는 위치(참조값)가 들어가 있다. 참조형 변수를 통해서 뭔가 하려면 결국 참조값을 통해 해당 위치로 이동해야한다.
기본형 vs 참조형 - 계산
- 기본형은 들어 있는 값을 그대로 계산할 수 있다.
- 참조형은 들어 있는 참조값을 그대로 사용할 수 없다. 주소지만 가지고는 할 수 있는게 없기 때문이다. (주소지에 가야 실체가 있으므로)
참고 - String
자바에서 String 은 특별하다. String 은 사실은 클래스다. 따라서 참조형이다. 그런데 기본형처럼 문자 값을 바로 대입할 수 있다. 문자는 매우 자주 다루기 때문에 자바에서 특별하게 편의 기능을 제공한다
2️⃣ 기본형 vs 참조형 2 - 변수 대입
대원칙 : 자바는 항상 변수의 값을 복사해서 대입한다.
자바에서 변수에 값을 대입하는 것은 변수에 들어 있는 값을 복사해서 대입하는 것이다. 기본형, 참조형 모두 항상 변수에 있는 값을 복사해서 대입한다. 기본형이면 변수에 들어 있는 실제 사용하는 값을 복사해서 대입하고, 참조형이면 변수에 들어 있는 참조값을 복사해서 대입한다.
int a = 10;
int b = a;
변수의 대입은 변수에 들어있는 값을 복사해서 대입한다.
변수 a
에 들어있는 값 10
을 복사해서 변수 b
에 대입한다. 변수 a
자체를 b
에 대입하는 것이 아니다 !
Student s1 = new Student();
Student s2 = s1;
참조형의 경우 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사된다. 쉽게 이야기해서 실제 건물이 복사되는 것이 아니라 건물의 위치인 주소만 복사되는 것이다. 따라서 같은 건물을 찾아갈 수 있는 방법이 하나 늘어날 뿐이다.
package ref;
public class VarChange2 {
public static void main(String[] args) {
Data dataA = new Data();
dataA.value = 10;
Data dataB = dataA;
System.out.println("dataA 참조값=" + dataA);
System.out.println("dataB 참조값=" + dataB);
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//dataA 변경
dataA.value = 20;
System.out.println("변경 dataA.value = 20");
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
//dataB 변경
dataB.value = 20;
System.out.println("변경 dataB.value = 30");
System.out.println("dataA.value = " + dataA.value);
System.out.println("dataB.value = " + dataB.value);
}
}
실행 결과는 다음과 같다.
dataA 참조값 = ref.Data@x001
dataB 참조값 = ref.Data@x001
dataA.value = 10
dataB.value = 10
변경 dataA.value = 20
dataA.value = 20
dataB.value = 20
변경 dataB.value = 30
dataA.value = 30
dataB.value = 30
Data dataB = dataA
라고 했을 때 변수에 들어있는 값을 복사해서 사용한다는 점이다. 그런데 그 값이 참조값이다. 따라서 dataA
와 dataB
는 같은 참조값을 가지게 되고 두 변수는 같은 인스턴스를 참조하게 된다.
3️⃣ 기본형 vs 참조형 3 - 메서드 호출
자바에서 메서드의 매개변수 (파라미터)는 항상 값에 의해 전달된다. 그러나 이 값이 실제 값이냐, 참조 (메모리 주소) 값이냐에 따라 동작이 달라진다.
- 기본형: 메서드로 기본형 데이터를 전달하면, 해당 값이 복사되어 전달된다. 이 경우, 메서드 내부에서 매개변수(파라미터)의 값을 변경해도, 호출자의 변수 값에는 영향이 없다.
- 참조형: 메서드로 참조형 데이터를 전달하면, 참조값이 복사되어 전달된다. 이 경우, 메서드 내부에서 매개변수(파라미터)로 전달된 객체의 맴버 변수를 변경하면, 호출자의 객체도 변경된다.
4️⃣ 기본형 vs 참조형 3 - 메서드 호출 - 활용
메서드에 객체 전달
package ref;
public class Method2 {
public static void main(String[] args) {
Student student1 = createStudent("학생1", 15, 90);
System.out.println("student1=" + student1);
Student student2 = createStudent("학생2", 16, 80);
System.out.println("student2=" + student2);
printStudent(student1);
printStudent(student2);
}
static Student createStudent(String name, int age, int grade) {
Student student = new Student(); //x001
System.out.println("student=" + student);
student.name = name;
student.age = age;
student.grade = grade;
return student; //x001
}
static void printStudent(Student student) {
System.out.println("이름:" + student.name + " 나이:" + student.age + " 성적:" + student.grade);
}
}
createStudent()
라는 메서드를 만들고 객체를 생성하는 부분도 이 메서드 안에 함께 포함했다. 이제 이 메서드 하나로 객체의 생성과 초기값 설정을 모두 처리한다. 메서드 안에서 객체를 생성했으므로 return
을 이용해서 메서드 호출 결과를 반환한다.

메서드 내부에서 인스턴스를 생성한 후에 참조값을 메서드 외부로 반환했다. 이 참조값마 있으면 해당 인스턴스에 접근할 수 있다. 여기서는 student1에 참조값을 보관하고 사용한다.
변수와 초기화
맴버변수와 지역변수의 차이
구분 | 멤버 변수 (필드) | 지역 변수 |
---|---|---|
선언 위치 | 클래스 내부 (메서드 외부) | 메서드 또는 블록 내부 |
생명 주기 | 객체가 살아있는 동안 유지 | 블록이 끝나면 소멸 |
초기화 여부 | 자동 초기화 (null , 0 , false 등) |
자동 초기화 X (직접 초기화 필요) |
사용 범위 | 클래스 전체에서 사용 가능 | 선언된 블록 내에서만 사용 가능 |
메모리 저장 위치 | 힙(Heap) 영역 | 스택(Stack) 영역 |
접근 제한자 사용 | 가능 (private , public 등) |
불가능 |
변수의 값 초기화
- 맴버 변수 : 자동 초기화
- 인스턴스의 맴버 변수는 인스턴스를 생성할 때 자동으로 초기화된다.
- 숫자 (
int
)=0
,boolean
=false
, 참조형 =null
(null
값은 참조할 대상이 없다는 뜻으로 사용된다.) - 개발자가 초기값을 직접 지정할 수 있다.
- 지역 변수 : 수동 초기화
- 지역변수는 항상 직접 초기화해야 한다.
null
참조형 변수에는 항상 객체가 있는 위치를 가리키는 참조값이 들어간다. 그런데 아직 가리키는 대상이 없거나, 가리키는 대상을 나중에 입력하고 싶다면 어떻게 해야할까 ?
참조형 변수에서 아직 가리키는 대상이 없다면 null
이라는 특별한 값을 넣어둘 수 있다. null
은 값이 존재하지 않는, 없다는 뜻이다.
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);
}
}
GC - Garbage Collection (아무도 참조하지 않는 인스턴스의 최후)
아무도 참조하지 않는 인스턴스는 사용되지 않고 메모리 용량만 차지한다. 따라서 JVM의 GC가 더이상 사용하지 않는 인스턴스라고 판단하면, 해당 인스턴스를 자동으로 메모리에서 제거해준다.
객체는 해당 객체를 참조하는 곳이 있으면, JVM이 종료할 때 까지 계속 생존한다. 그런데 중간에 해당 객체를 참조하는 곳이 모두 사라지면 그때 JVM은 필요 없는 객체로 판단되고 GC(가비지 컬렉션)를 사용해서 제거한다.
NullPointerException
NullPointerException
은 이름 그대로 null
을 가리키다(Pointer
) 인데, 이 때 발생하는 예외이다. null
은 없다는 뜻이므로 결국 주소가 없는 곳을 찾아갈 때 발생하는 예외다.
객체를 참조할 때는 .
(dot)을 사용한다. 이렇게 하면 참조값을 사용해서 해당 객체를 찾아갈 수 있다. 그런데 참조값이 null
이라면 값이 없다는 뜻이므로, 찾아갈 수 있는 객체가 없다.

public class NullMain4 {
public static void main(String[] args) {
BigData bigdata = new BigData();
bigdata.data = new Data(); // Data 인스턴스를 만들어줌
System.out.println("bigData.count=" + bigdata.count);
System.out.println("bigData.data=" + bigdata.data);
// Bigdata.data 맴버변수에 참조값을 할당하여 NullPointerException 해결
System.out.println("bigData.data.value" + bigdata.data.value);
}
}
'Language > Java' 카테고리의 다른 글
[Java] if-else와 try-catch에 대해서 (0) | 2025.03.14 |
---|---|
[Java] 제네릭(Generic) (0) | 2025.03.06 |
[Java] 익명 클래스 (Annoymous Class)와 람다 (Lamda) (0) | 2025.03.05 |
[Java] 컬렉션 (Collection) (0) | 2025.03.04 |
[Java] 클래스와 데이터 (0) | 2025.02.27 |