앞서 참조 변수란?
Animal a = new Animal();
위 코드를 보면 동물 객체가 만들어지고, 각 객체를 참조하는 a 변수가 선언되었다.
-> a 변수는 Animal 클래스의 객체를 가리키는 참조 변수이다. (a 변수에 Animal 객체를 생성해서 넣으라는 뜻)
이 변수는 Animal 클래스의 객체가 저장된 메모리 주소값을 가지고 있다. 이제 a 변수를 이용하여 객체에 접근할 수 있다.
그러나 동물 클래스가 생성되었다고 해서 동물이 만들어진 것은 아니다.
new 연산자는 new 연산자 뒤에 나오는 생성자를 이용하여 메모리에 객체를 만들라는 명령이다(객체에 메모리를 올려준다).
메모리에 만들어진 객체(클래스)를 인스턴스라고 하는데, 이렇게 만들어진 객체를 참조하는 변수가 a 변수이다.
- 메모리에 올라간 인스턴스를 가리키는 변수 = 참조하는 변수 = 레퍼런스 하는 변수 = a 변수
예를 들어, 다음과 같이 객체의 인스턴스 변수에 값을 할당할 수 있다.
a.name = "Cat";
name은 Animal 클래스의 인스턴스 변수이다. 이 변수에 값을 할당하려면, 객체에 접근할 수 있어야 한다. 이를 위해서는 객체를 가리키는 참조 변수인 a를 사용한다.
이와 같이, 참조를 이용하여 객체에 접근하는 것은 객체 지향 프로그래밍에서 매우 중요한 개념 중 하나이다.
참조한다라는 것은 레퍼런스 한다와 같다. 변수가 인스턴스를 가지고 있는 게 아니라는 것이다.
'가리킨다'라는 의미이다.
a 변수에는 메모리의 위치 값이 저장되는 것인데, 메모리의 위치값이 저장된다고 하더라도 어떤 메모리에 저장되는 지 그 정보를 알 수 없다. 그렇기 때문에 a 변수는 Animal 인스턴스를 참조한다.
Q1. '참조한다' 라는 건 무슨 말인 것일까?
예를 들어, 아래와 같은 코드가 있다고 하자.
class Animal {
public void move() {
System.out.println("Animal is moving");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dog is walking");
}
public void bark() {
System.out.println("Woof!");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 설명할 코드
animal.move();
}
}
Animal 타입의 변수 animal을 선언하고, 이 변수에 Dog 클래스의 객체를 할당하면, 이 변수는 Animal 클래스의 참조 변수이지만, Dog 클래스의 객체를 참조하게 된다.
이것이 가능한 이유는 Dog 클래스가 Animal 클래스를 상속하고 있기 때문이다. 즉, Dog 클래스는 Animal 클래스의 모든 멤버와 메소드를 상속받았기 때문에 Animal 타입의 변수에 Dog 클래스의 객체를 할당해도, Animal 클래스의 멤버와 메소드를 모두 사용할 수 있다.
하지만 Dog 클래스에서 Animal 클래스의 메소드를 오버라이딩했다면, Dog 클래스에서 오버라이딩한 메소드가 호출된다.
따라서 Animal animal = new Dog(); 코드는 Animal 타입의 변수를 선언하고, 이 변수에 Dog 클래스의 객체를 할당하는 것이다.
이것은 다형성(Polymorphism)의 개념 중 하나로, 상위 클래스 타입의 변수로 하위 클래스의 객체를 참조할 수 있다는 것을 의미한다.
다형성 개념은 이전 글을 참고한다.
클래스 형변환을 하는 이유 (다형성)
Q1. 부모타입으로 자식객체를 참조하게 되면 부모가 가지고 있는 메소드만 사용할 수 있다. 그런데 형변환을 하는 이유가 무엇일까? 부모 타입으로 자식 객체를 참조하면 부모 클래스에 정의된
maiplesyrup.tistory.com
객체 지향 프로그래밍에서 "참조"란, 변수가 객체를 가리키는 것을 의미한다.
일반적으로 변수에 값을 할당할 때, 변수는 해당 값의 복사본을 가지게 됩니다. 예를 들어, int num1 = 10; 이라는 코드에서 num1 변수에는 10이라는 값이 복사되어 저장된다.
하지만 객체는 변수에 직접 값을 저장하지 않는다. 대신, 변수는 객체를 "참조"하는 것이다.
즉, 변수는 객체의 메모리 주소를 저장하고, 이 주소를 통해 객체에 접근한다.
그래서 Animal animal = new Dog(); 코드에서, new 연산자는 Dog 클래스의 객체를 생성하고, 이 객체의 메모리 주소를 반환한다.
이 주소를 Animal 타입의 변수 animal에 할당함으로써, 이 변수는 Dog 클래스의 객체를 "참조"하게 되는 것이다.
따라서, 변수가 객체를 "참조"한다는 것은, 변수가 객체의 메모리 주소를 저장하고 이 주소를 통해 객체에 접근한다는 것을 의미한다.
Q2. 변수가 객체를 가리킨다면, 값은 어디에 저장되는 것인가?
자바나 C#과 같은 객체 지향 프로그래밍 언어에서는 객체가 메모리에 생성될 때, 그 객체는 보통 두 개의 영역 중 하나인 스택(Stack)과 힙(Heap)에 저장된다.
스택은
함수 호출 시에 사용되며, 지역 변수와 매개 변수를 저장한다. 스택의 메모리는 일반적으로 작고, 연속적인 영역이다.
따라서 스택에서는 변수의 크기가 미리 결정되어야 하고, 변수의 수명은 해당 변수가 선언된 함수 또는 블록의 수명과 같다. 함수 또는 블록이 종료되면, 해당 변수는 스택에서 자동으로 제거된다.
힙은
크고 자유로운 공간을 말하며, 객체를 저장하기에 적합한 영역이다.
힙에서는 객체의 크기가 런타임에 동적으로 결정된다. 또한, 객체의 수명은 프로그래머가 직접 제어할 수 있으며, 이 객체가 더 이상 필요하지 않을 때, 프로그래머가 메모리를 해제해야 한다.
따라서 Animal animal = new Dog(); 코드에서,
Animal 타입의 변수 animal은 스택에 저장되며, Dog 클래스의 객체는 힙에 저장됩니다.
변수 animal은 객체의 메모리 주소를 스택에 저장하고, 이 주소를 사용하여 객체를 "참조"한다.
- 변수 - 스택 영역
- 클래스 객체 - 힙 메모리 영역
객체는 일반적으로 힙 메모리에 저장되며, 변수는 해당 객체의 메모리 주소를 저장한다. 변수가 객체의 값을 변경하면, 실제로는 객체의 값이 변경된다.이를 조금 더 자세히 설명하면, 객체의 값은 객체 내부의 인스턴스 변수에 저장된다.
인스턴스 변수는 객체의 상태를 나타내며, 이 변수는 객체가 생성될 때 힙 메모리에 할당된다.
객체의 메모리 주소를 저장하는 변수는 스택 메모리에 할당된다.
따라서, 변수가 객체를 "참조"한다는 것은, 변수가 객체의 인스턴스 변수에 접근할 수 있으며, 이를 통해 객체의 값을 읽거나 변경할 수 있다는 것을 의미한다.
값이 변경되면, 해당 객체의 인스턴스 변수가 변경되며, 변수가 가리키는 메모리 주소는 동일하게 유지된다.
예를 들어, Animal animal = new Animal("Cat"); 이라는 코드에서, Animal 객체의 인스턴스 변수인 aniaml에 "Cat"이라는 값을 저장한다. 변수 animal은 이 객체의 메모리 주소를 가리키며, 이를 통해 animal 변수의 값을 읽거나 변경할 수 있다. 값을 변경하면, 해당 객체의 animal 변수가 변경되며, 변수 animal이 가리키는 메모리 주소는 동일하게 유지되는 것이다.
정리
- 참조는 객체를 사용할 때, 객체의 주소값을 저장하고 있는 변수를 통해 객체에 접근하는 것을 의미한다.
- 객체를 생성하면, 해당 객체는 힙 메모리에 할당되고, 이 객체를 사용하려면 객체의 주소값을 저장하는 참조 변수를 선언해야 한다. 참조 변수는 스택 메모리에 저장된다.
- 참조 변수를 통해 객체에 접근할 때는 참조 변수에 저장된 주소값을 통해 객체의 인스턴스 변수나 메소드에 접근할 수 있다.
- 참조 변수를 사용하여 객체에 접근하면, 객체의 상태를 변경하거나, 객체의 메소드를 호출하여 원하는 작업을 수행할 수 있다.
- 참조 변수를 이용하여 다른 객체를 참조하도록 변경할 수도 있다. 이 경우에는 참조 변수에 새로운 객체의 주소값을 할당하면 된다. 이렇게 하면 이전에 참조하고 있던 객체는 더 이상 참조하지 않게 되며, 쓰이지 않는 객체는 자동으로 메모리에서 해제된다.
'TIL > ☕️ JAVA' 카테고리의 다른 글
TDD 방식으로 자바 연습하기 (0) | 2023.03.12 |
---|---|
Equals()와 == 연산자 (0) | 2023.03.12 |
About 다형성: 인터페이스를 사용하는 이유 (0) | 2023.03.04 |
About 다형성: 클래스 형변환을 하는 이유 (1) | 2023.03.03 |
오버로딩과 오버라이딩 차이 (0) | 2023.03.03 |