대충은 알 것 같은 게 변수가 가지고 있는 주소로 찾아갔을 때 진짜 온전히 주소 값만 알면 그 주소로 갔을 때 어떤 데이터를 어떻게 가지고 와야 할 지 모를 것 이다.

쉬운 예로 기본 자료형 타입은 참조변수가 가리키는 주소에 찾아가서 선언된 타입만큼씩 데이터를 읽어오면 된다.

ex) int[] arr = new int[3]{1, 2, 3};

arr에 heap 메모리 주소 100이 저장되어 있다 한다면 arr[0]은 주소 100으로 가서 4byte만큼의 크기를 읽고, arr[1]은 100에서 4byte 떨어진 곳에서 4byte를 읽으면 된다. 그렇게 가지고 온 데이터를 int 타입으로 읽으면 된다.

그렇다면 배열 말고 다른 클래스 참조 변수는 어떤 식으로 heap 영역의 데이터에 접근하게 될까?

이 질문이 중요한 이유

이 답을 알게 되면 상속에서 배우는 캐스팅에 대해 더 잘 이해할 수 있게 된다.

변수의 데이터 타입에 따라 사용 할 수 있는 필드, 메서드가 달라진다. 예를 들어 A라는 클래스가 있고 A클래스를 상속받는 B클래스가 있다고 해보자.

A a = new B();

-객체지향 프로그램에서 말하는 다형성이다. B클래스로 인스턴스를 생성했지만 B클래스의 부모 클래스인 A클래스의 타입으로 변수를 선언해 주었다. 하지만 이때는 A클래스의 멤버에만 접근 할 수 있다.

B클래스의 멤버를 사용하려면 강제 캐스팅 해줘야 한다.

B b = (B)a;

이렇게 하면 B클래스 타입 b변수로 다시 B클래스의 객체 멤버들을 사용할 수 있다.

밑에는 캐스팅과 데이터 타입에 관해 조금 더 생각해 볼 수 있는 코드다.

특히 주목 할 점은 변수를 처음 선언해 줄 때 데이터 타입을 정해주는데 그 변수의 데이터 타입은 절대 바꿀 수 없다. 즉 캐스팅이 필요할 경우 새로운 변수를 만들어줘야 한다.

class A{
	int a = 1;
}

class B extends A{
	int b = 2;
}

public class Test{
	
	public static void main(String[] args) {
		A a1 = new B();
		
		System.out.println(a1.a);
//		System.out.println(a1.b);
//		System.out.println((B)a1.b);
		System.out.println(((B)a1).b);
		if(a1 instanceof B) {
			System.out.println(((B)a1).b);
		}
		
		a1 = (B)a1;
		B b1 = (B)a1;
		
		System.out.println(b1.b);
//		System.out.println(a1.b);
	}
}