자료구조와 알고리즘을 공부하다보니 레퍼런스(Reference)라는 단어를 듣게 되었습니다.
한글로는 참조라고도 합니다.
예전 웹 개발 교육을 들었을때 참조에 대해서 공부를 했었던 기억이 나서 복습을 해보려고 합니다.
"레퍼런스(Reference)"는 객체 지향 프로그래밍에서 객체를 가리키는 포인터나 주소를 의미합니다. Java와 같은 언어에서 변수는 실제 데이터를 저장할 수도 있고, 객체를 참조(참조 변수)할 수도 있습니다. 이때 레퍼런스는 객체의 메모리 위치를 가리키는 값을 의미합니다.
Java에서의 레퍼런스
Java에서 기본 데이터 타입(예: int, char, float)은 변수 자체에 값을 저장하지만, 객체 타입(예: 클래스, 배열)은 변수에 객체의 레퍼런스를 저장합니다.
class Person {
String name;
Person(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Alice"); // person1은 Person 객체의 레퍼런스를 가짐
Person person2 = person1; // person2는 person1이 가리키는 동일한 객체를 가리킴
person2.name = "Bob"; // person2를 통해 이름을 변경하면
System.out.println(person1.name); // person1의 이름도 변경됨 (출력: "Bob")
}
}
이 예제에서 person1과 person2는 Person 객체를 가리키는 레퍼런스입니다. person2에 person1을 할당하면, 두 변수는 동일한 객체를 가리키게 됩니다. 따라서 person2를 통해 객체의 속성을 변경하면 person1에서도 그 변경이 반영됩니다.
1.간접 참조
래퍼런스는 변수나 객체의 실제 값을 직접적으로 다루지 않고, 그 값이 저장된 메모리 위치(주소)를 간접적으로 참조합니다. 이로 인해 래퍼런스를 통해 참조하는 값이 변경되면, 참조된 모든 곳에서 해당 변경 사항이 반영됩니다.
2.메모리 안전성
래퍼런스는 직접적으로 메모리 주소를 다루지 않기 때문에 메모리 안전성이 높습니다. 잘못된 메모리 접근이나 주소 계산 오류로 인한 프로그램 크래시 가능성이 낮아집니다.
3.자동 메모리 관리
자바와 같은 언어에서 래퍼런스는 가비지 컬렉션 시스템과 함께 사용되어, 객체가 더 이상 참조되지 않을 때 자동으로 메모리가 회수됩니다. 프로그래머는 메모리 할당과 해제에 대해 신경 쓸 필요가 없습니다.
4.초기화가 필요
자바에서는 래퍼런스를 사용하기 전에 객체를 생성해야 합니다. 초기화되지 않은 래퍼런스를 사용하려 하면 컴파일러가 오류를 발생시킵니다.
5.객체 지향적
자바와 같은 객체 지향 언어에서는 래퍼런스를 통해 객체를 다루며, 객체의 메서드와 필드에 접근할 수 있습니다.
자바의 래퍼런스와 C 언어의 포인터의 차이점을 표로 정리해보겠습니다.
특징 | 자바 래퍼런스 (Reference) | C 언어 포인터 (Pointer) |
---|---|---|
메모리 접근 방식 | 객체나 변수의 주소를 간접적으로 참조함 | 변수의 메모리 주소를 직접 참조 및 조작 가능 |
메모리 연산 | 래퍼런스에 대한 산술 연산 불가능 | 포인터에 대한 산술 연산 가능 (예: 증가, 감소) |
초기화 | 초기화된 객체를 참조해야 하며, 초기화되지 않은 참조 사용 시 오류 발생 | 초기화되지 않은 포인터는 임의의 값을 가질 수 있음 |
NULL 처리 | NULL 참조 시 프로그램 오류 발생 | NULL 포인터를 사용할 수 있으며, 잘못된 사용 시 런타임 오류 발생 가능 |
안전성 | 메모리 주소를 직접 다루지 않기 때문에 안전성 높음 | 메모리 주소를 직접 다루기 때문에 오류 가능성 있음 |
메모리 관리 | 가비지 컬렉션에 의해 자동으로 메모리 관리 | 동적 메모리 할당 및 해제를 프로그래머가 직접 관리 |
사용 언어 | 주로 자바와 같은 객체 지향 언어에서 사용 | C, C++, 그리고 일부 저수준 언어에서 사용 |
사용법 | 간단하고 직관적, 별도의 연산자 필요 없음 | * (역참조), & (주소 연산자) 등의 연산자 필요 |
유연성 | 객체나 변수의 참조만 가능, 메모리 조작 불가 | 메모리 주소를 자유롭게 조작 가능 |
오류 가능성 | 낮음 (메모리 접근의 안전성 보장) | 높음 (잘못된 포인터 연산 및 NULL 포인터 가능성) |