8. Java의 참조 변수


javalogo

Overview

노드와 포인터에서 설명한 것들을 Java 환경에서 살펴보자.

Java에서는 8개의 원시타입(primitive type)을 제외하고 모두 참조 타입(reference type)이다.

참조 변수(reference variable)는 객체가 만들어져 있는 메모리 주소를 저장하고 있는 변수이다.

이것은 다른 프로그래밍 언어의 포인트 변수와 비슷하지만 포인터 변수가 일부 산술 연산이 허용되는 것에 반해 참조변수는 허용되지 않는다.


참조 변수의 성질

참조 변수의 성질을 이해하기 위해 객체를 생성하고 참조 변수에 지정해 보자.

class Point {
    int x;
    int y;

    public Point() {
        x = 0;
        y = 0;
    }

    public Point(int x1, int y1) {
        x = x1;
        y = y1;
    }
}

다음과 같은 두 개의 객체 생성 명령문을 생각해 보자.

Point p1 = new Point();
Point p2 = new Point(0, 8);

클래스 Point는 2개의 정수 데이터 필드 x, y로 정의되어 있다.

Point 객체를 나타내는 변수 p1에 대한 첫 번째 명령문에서 무인자 생성자를 이용한 객체 생성식 new Point();는 3가지 일을 한다.

  1. 새로운 Point객체를 위해 메모리를 할당한다.
  2. 이 객체의 모든 데이터 필드들을 0으로 초기화한다. 즉, x=0, y=0인 지정문을 실행한다.
  3. 이 객체에 대한 참조(주소)를 반환하여 Point타입으로 선언된 참조 변수 p1의 초기 값으로 저장하도록 한다. 변수 p2에 대한 두 번째 명령문도 비슷한 기능을 한다. 즉 2개의 매개 변수를 가진 생성자를 이용하여 새로운 Point 객체를 만들고 데이터 필드 값을 x=0, y=8으로 초기화한 뒤에 이 객체에 대한 참조를 새로 Point 타입으로 선언된 참조 변수 p2의 값으로 저장한다.

fig_1

Java에서 각 변수는 이름과 값을 가지고 있다. 위 그림의 변수 p1을 보면 p1은 변수의 값을 가지고 있는 박스에 대한 레이블로 사용되고 있음을 알 수 있다.

값 박스는 Java 참조 값을 나타내는 화살표의 꼬리를 가지고 있다.

화살표는 Point 객체를 나타내는 박스를 가리키고 있다.

각 Point 객체는 Point 클래스의 한 인스턴스로서 2개 데이터 필드 x와 y를 위해 메모리 구역을 할당받고 있다.

그림에서는 이해를 돕기 위해 이 두 필드들에 대한 이름들을 저장된 필드 값 위에 표시하였다.

이제 점 표기법으로 두 Point 변수 p1과 p2의 데이터 필드를 참조해 보자.

p2.y는 변수 p2가 가리키는 객체의 필드 y의 값 8이 될 것이고 다음과 같은 지정문은 객체 p1의 데이터 필드 x와 y의 값을 변경시키게 된다.

p1.x = 5;
p2.y = 8;

이 지정문들을 실행하고 나면 객체 p1과 p2의 데이터 필드들은 다음 그림과 같이 된다.

fig_2

여기서 한 가지 유의할 점은 이 점 연산자에 의해 자동적으로 Java에 있는 객체의 데이터 필드를 접근하거나 값을 접근한다는 것이다.

다시말해 p1.x라고 쓸 때 p1은 한 Point 객체가 저장되어있는 메모리에 대한 참조(포인터)를 가지고 있는 변수지만 이 p1.x 식은 그 Point 객체에 할당된 메모리에 대한 포인터를 따라가 데이터 필드 x의 내용을 접근하는 동작을 나타낸다.

위 그림의 상황에서 다음과 같은 지정문을 실행해보자.

p1 = p2;

참조 변수 p1이 가리키는 객체의 모든 데이터 필드들의 내용이 아래 그림의 (a)와 같이 참조 변수 p2가 가리키는 객체의 데이터 필드 값들로 데체되는 것인지 아니면 (b) 그림과 같이 p1의 참조 값이 p2의 참조 값으로 대체 되는 것인지를 확인 해둘 필요가 있다.

fig_3

Java에서 이 지정문 p1 = p2;의 의미는 변수 p2에 저장되어 있는 참조 값을 복사해서 변수 p1의 새로운 참조 값으로 저장하라는 것이다.

따라서 이 지정문을 실행하고나면 정확하게 위 그림의 (b)가 된다.

이렇게 되면 변수 p1의 값으로 사용되었던 Point 객체에 대한 메모리는 어떻게 되는지 알아보자.

만일 Java 시스템에서 이 객체를 가리키는 참조 변수가 더 이상 아무 곳에도 없으면 이 노드는 접근할 수 없는 Garbage가 된다.

이런 것들은 Java 시스템이 메모리가 부족하게 될 때 Garbage Collector라는 시스템을 가동시켜 수집해서 재활용하게 된다.

이런 과정은 다른 프로그래밍 언어와 달리 Java 시스템에서 자동적으로 이루어지기 때문에 프로그래머는 신경 쓸 필요가 없다.

(b) 그림과 같은 상황에서는 변수 p1과 p2가 하나의 객체를 공동으로 참조하고 있는 것이기 때문에 어느 한 변수를 통해 겍체의 필드 값을 변경하면 다른 변수로 이 필드 값을 접근할 때 변경된 값이 접근된다.

예를 들어 (b) 그림과 같은 상황에서 다음과 같은 지정문을 실행시킨다고 하자.

p1.x = 3;

이 결과는 다음 그림과 같이 된다.

fig_4

따라서 p1.xp2.x를 접근하게 되면 모두 똑 같은 정수 3을 얻게 된다.

그러면 다음에는 스트링 리스트에 대한 연결 표현을 위한 노드 구조를 Java로 정의해보자.

스트링 데이터 필드 name과 링크 필드 link로 구성된 리스트 노드는 다음과 같은 ListNode 클래스로 정의할 수 있다.

class ListNode {
    String name;
    ListNode link;
}

이제 ListNode 클래스의 객체들은 인스턴스들로서 각자 두 개의 필드를 갖는다.

즉, String 데이터 값을 갖는 name 필드와 다른 ListNode 객체를 가리키는 참조 값을 갖는 link 필드가 그것이다.

리스트는 바로 이 ListNode 클래스의 객체들이 link 필드를 통해 연결된 것을 말한다.

이 ListNode 클래스는 아무런 메소드를 가지고 있지 않다.

이 클래스의 목적은 단지 두 데이터 값, 즉 name과 link를 하나의 단위로 묶어 이에 대한 컴퓨터 메모리의 한 구역을 할당받아 하나의 참조 값으로 이것을 가리키게 하려는 것이다.

Java에서는 어떤 객체도 참조하고 있지 않다는 것을 명시적으로 표현하는 공백 포인터 값으로 null을 사용한다.

따라서 리스트에 있는 마지막 노드의 link 필드 값으로 null을 지정하면 그 리스트의 마지막 노드, 리스트의 끝이라는 것을 나타내고 LiskNode 타입 변수가 null값을 가지면 공백 리스트를 나타낸다.

그러면 실제로 간단한 연결 리스트를 만드는 예를 들어보자.

이를 위해 무인자 생성자 ListNode()와 지정문 왼쪽에 점 표기식을 사용할 수 있다.

  1. L을 List Node 타입의 변수로 선언하고 하나의 새로운 ListNode 객체를 생성하여 초기화한다.

    ListNode L = new ListNode();
    이 식이 실행되면 다음과 같이 참조 변수 L이 생성된 객체를 참조하게 된다.
    fig_5
    여기서 ListNode 객체가 생성되면서 name과 link 필드는 기정 생성자(default constructor), 즉 ListNode()에 의해 null로 초기화 된다.

  2. 노드 L의 name 필드에 “Kim”을 지정하고 link 필드에는 null을 지정한다.

    L.name = “Kim”;
    L.link = null;
    fig_6

  3. 리스트 L에 “Lee”와 “Park”에 대한 두 개의 새로운 ListNode를 차례로 첨가시킨다.

    L.link = new ListNode();
    fig_7
    L.link = new ListNode();
    fig_8
    L.link.link.name = “Park”;
    L.link.link.link = null;
    fig_9


참고 문서

  • 이석호. (2004). 자료 구조와 JAVA. 정익사.





© 2019. by RaP0d

Powered by aiden