본문 바로가기
Java 배우기

참조(reference)와 참조변수(reference variable)

by 노화방지 Anti-aging Hairstyle 2016. 1. 3.
반응형

연산자 new클래스의 새로운 인스턴스에 대한 참조(reference)를 리턴합니다.
The new operator returns a
reference to a new instance of a class.  


* 여기서 참조(reference)참조변수(reference variable)와 다르다는 점을 주목해야 하는데, 

 

참조(reference)참조변수(reference variable)가 아닌 참조값(reference value, 해시코드)입니다.

 

클래스의 조변수(reference variable)참조(reference)할당할 수 있습니다.
This
reference can be assigned to a reference variable of the class.  

 

* 즉, 참조값(reference value)을 클래스의 참조변수(reference variable)에 할당할 수 있습니다.

 

예를 들면, A x = new A();에서 연산자 new가 반환한 참조(reference)는 new A();의 반환값이고, 

참조변수(reference variable)x입니다.

 

즉, new A();의 반환값인 참조(reference)참조변수(reference variable) x에 할당한 것입니다.

 

메모리에 생성되어 저장된 객체를 처리하려면 객체 참조(값)이 있어야 합니다.

An object reference provides a handle to an object that is created and stored in memory.  

 

자바에서는 참조(값)를 통해서만 객체를 관리할 수 있는데, 이 참조(값)변수에 저장될 수 있습니다.

In Java, objects can only be manipulated via references, which can be stored in variables.  

 

 

 

* 참조와 참조변수

 

연산자 new와 생성자를 이용해서 특정 메모리에 인스턴스를 생성했다면 당연히 생성된 인스턴스를 사용할 수 있습니다.

그런데 연산자 new가 인스턴스가 있는 메모리를 생성한 경우, 그 메모리의 주소와 어떻게 연결할 수 있나를 알아야 합니다. 

C++ 언어에서는 new를 이용해서 메모리를 생성하면 주소 그 자체를 직접 넘겨줍니다. 

주소를 저장하기 위한 포인터 변수를 이용해서 주소를 관리하기 때문에 C++에서는 사용자가 주소를 직접 처리합니다.

 

하지만 자바에서는 메모리 주소를 바로 주지 않는데, 즉 인스턴의 메모리 주소 대신 참조을 할당받게 됩니다.

 

(예제)
클래스 Sun을 선언합니다.
public class Sun { 
    public int distance; 
    public int data; 


객체의 참조변수를 선언하는데, 

메모리가 생성되기 전이기 때문에 null로 초기화합니다. 


Sun s1 = null; 
Sun s2 = null; 

아래와 같이 new로 메모리를 생성합니다. 

s1 = new Sun(); 
s2 = new Sun(); 

메모리가 생성되면 내부 인덱스 테이블(Index Table)에 주소를 맵핑하는 참조값이 하나씩 만들어집니다. 
이렇게 만들어진 참조값이 참조변수 s1과 s2에 할당됩니다. 

참조값(해시코드)은 누가 만드나? 

참조값은 가상머신(Virtual Machine)에서 자동으로 생성되며, 객체구분을 위한 유일한 Key 값이 됩니다. 
실제 s1과 s2에 참조값이 들어 있는 지를 확인하기 위해서 s1과 s2를 출력해 보도록 하겠습니다.

public class SunMain{ 
    public static void main(String[] args){ 
        Sun s1 = null;            // 객체 변수 선언 
        Sun s2 = null;            // 객체 변수 선언 
        s1 = new Sun();          // 메모리 할당 
        s2 = new Sun();          // 메모리 할당 
        System.out.println("Sun의 변수 s1의 값은:" + s1);  //객체 변수 출력 
        System.out.println("Sun의 변수 s2의 값은:" + s2);  //객체 변수 출력 
    } // 메인함수의 끝  end of main 
} // 클래스 SunMain의 끝  end of SunMain class 
 
/*** 
Sun s1:Sun@139a55 
Sun s2:Sun@1db9742 
***/ 

위와 같이 

@ 다음에 16진수의 숫자값이 표시되는데 이것이 바로 조값입니다. 


C나 C++의 메모리 생성 작업과 자바의 메모리 생성 작업은 의미가 전혀 다릅니다. 
C++에서 new 연산자를 이용해서 메모리를 생성하면 해당 메모리의 주소를 갖게 되지만 
자바는 주소 대신 

주소에 연결된 참조값을 갖게 됩니다. 

이런 의미에서 자바의 객체변수를 참조변수라 하며, 참조변수에는 참조값이라는 정수값이 할당됩니다


참조변수의 특징


참조변수의 개념을 이해하는 것은 아주 중요합니다. 자바를 이해하는데 있어서 클래스를 이해하는 것만큼이나 중요한 것이 바로 참조변수입니다. 클래스를 이해할 수 없으면 객체지향 프로그램을 할 수 없으며,

 
클래스 개념을 안다 하더라도 참조변수의 개념을 알지 못하면 자바가 어려워집니다.  


참조값(Reference Value) 

참조이란 객체의 메모리를 생성했을 때 메모리와 연결된 유일한 숫자값 입니다. 
숫자값을 참조변수가 받으며, 참조값을 갖고 해당 객체를 처리합니다.  
참조값으로 작업하면 내부에서 참조값에 연결된 메모리로 작업하는 것과 같은 효과 입니다. 

인스턴스를 만들면 자바 실행 시스템에서 인스턴스의 실제 주소에 연결된 숫자(참조값) 하나를 줍니다.
숫자(참조값)를 인스턴스가 받게 되며, 인스턴스는 이 숫자(참조값)를 이용해서 해당 인스턴스의 메모리에 값을 할당하거나 메소드를 호출할 수 있습니다. 


참조값은 4바이트짜리 정수값입니다. 
그렇기 때문에 인스턴스 메모리를 할당할 때는 4바이트의 정수을 객체변수에 할당하게 됩니다. 
객체변수 선언이 무엇인지 아래 과정을 통해 생각해봅니다. 

Sun s1 = null; 

단순히 메모리 없는 객체변수를 선언한 것일까요? 
참조의 의미를 배웠으니 다음과 같이 해석해야 합니다.  

 

s1은 4바이트짜리 참조변수가 만들어진 것입니다. 

s1 자체는 4바이트의 메모리 생성의 의미를 담고 있습니다. 
s1은 현재 4바이트의 메모리에 null값이 들어있습니다.  
s1에는 객체의 실제 메모리와 연결된 Sun형의 참조값을 넣을 수 있습니다. 

참조변수가 만들어졌다면 다음 구문을 해석해 보시기 바랍니다. 

s1 = new Sun(); 

s1이라는 참조변수에 참조값을 할당 받을 수 있습니다. 
new Sun()이라고 했을 때 참조값이 생성되어 s1에 할당되는 것입니다. s1이라는 참조변수는 

단순히 정수형 숫자를 담을 수 있는 4바이트짜리 메모리입니다.

 

그렇다면 실제 참조값을 누가 만드는가? new를 이용해서 객체를 생성했을 때 자바 시스템 내부에서 자동으로 참조값을 만들어 줍니다. 
아래 구문을 참조변수와 연결해서 해석해 보면, 다음과 같은 절차에 의해서 참조값이 인스턴스 s1과 연결된 메모리로 할당됩니다. 

s1.distance = 1000 

s1에는 참조값이 할당되어 있습니다. 
s1의 참조값과 연결된 주소를 검색하기 위해서 자바 시스템 내부의 인덱스 테이블을 검색합니다. 
s1의 참조값에 연결된 주소를 인덱스 테이블에서 찾았다면 해당 메모리에 distance의 자리를 찾습니다. 
s1의 [참조값]-[메모리]-[distance]를 찾았다면 데이터 1000을 할당합니다. 

이것이 내부에서 이루어지기 때문에 자바를 배우는 분들이 잘 모르고 지나칠 수도 있습니다. 


참조변수끼리의 할당


참조변수의 의미를 알았으니 이번에는 참조변수의 할당을 알아보겠습니다. 일반적인 변수의 경우 다음과 같이 값이 간단하게 할당됩니다. 

int a = 5; 
int b = a; 

참조에도 이 법칙을 적용해 보도록 하겠습니다. 

먼저 알아야 할 것은 자바에서 참조값을 만드는 방법은 연산자 new를 이용하는 방법밖에 없다는 것입니다. 참조값을 사용자가 직접 만들 수는 없고, 

가상머신의 허락을 얻어야만 참조값을 얻을 수 있습니다. 

그 허락을 받는 것이 바로 연산자 new입니다.  

참조값을 테스트하기 위해서 다음과 같이 단순한 클래스를 하나 만들도록 하겠습니다. 

public class MotorCycle { 
    private int id; 
    private int speed; 
    public void setData(int i, int s){ 
        id = i; 
        speed = s; 
    } 
    public void drive(){ 
        System.out.println("이 오토바이의 번호판은 " + id + " 입니다."); 
        System.out.println("오토바이는 현재 " + speed + " Km 속도로 달립니다."); 
    } 


오토바이의 번호판(id)과 속도(speed)를 셋팅한 후 운전할 수 있는 클래스 MotoCycle을 만듭니다. 
다음과 같이 메모리가 있는 객체와 메모리가 없는 객체를 만들도록 하겠습니다. 

메모리 없는 객체 m과 메모리 있는 객체 c의 생성 

MotorCycle m = null; 
MotorCycle c = new MotorCycle(); 

현재 m은 참조값을 저장할 수 있는 4바이트의 메모리만 확보한 상태로 
m에 연결된 객체는 없습니다. 그리고 객체 c는 참조변수에 실제 객체가 연결되어 있습니다. 기본 데이터 타입 변수의 할당의 방법처럼 참조변수끼리 할당해 봅니다. 

참조변수끼리의 할당 

m = c; 

이렇게 하면 뭐가 복사될까요? 
참조값이 복사 됩니다. c에는 참조값이 들어 있기 때문에 c의 참조값이 m으로 값복사되어 들어갑니다. 값복사는, 2개의 메모리가 존재하고, 한 쪽 메모리에 들어 있는 값을 다른 쪽에 값만을 복사하는 것입니다.m = c 에서 c에 들어 있는 정수값 즉 4바이트의 참조값을 m에 복사하는 것입니다. 결국 m과 c는 똑같은 메모리를 가리키고 있는 것입니다. 
이것을 증명하는 예를 만들면 아래와 같습니다.  

public class MotorCycleMain { 
    public static void main(String[] args){ 
        MotorCycle c = new MotorCycle(); // 메모리 있는 객체 변수 선언 
        c.setData(9872, 150); 
        c.drive(); 
        System.out.println("객체 c : " + c); // 객체 출력 
        System.out.println();//한줄 추가 

        MotorCycle m = c; // 참조값복사 
        m.drive(); // 복사된 참조값을 이용한 메서드 호출 
        System.out.println("객체 m : " + m); //객체 출력 
    } //end of main 



이 오토바이의 번호판은 9872 입니다. 
오토바이는 현재 150 Km 속도로 달립니다. 
객체 c : MotorCycle@139a55 

이 오토바이의 번호판은 9872 입니다. 
오토바이는 현재 150 Km 속도로 달립니다. 
객체 m : MotorCycle@139a55 


위의 예제에서 객체 c를 m에 참조값 복사를 하고 있습니다.  

MotorCycle m = c; // 참조값 복사 

참조값 복사 이후에 m은 새로운 객체의 메모리를 생성한 것이 아니라 c의 참조값을 이용하고 있는 것입니다. 
1개의 메모리를 2개의 참조변수가 가리키는 것입니다.  

객체변수참조변수 

객체변수는 참조변수이며 참조변수끼리의 할당은 참조값 복사가 됩니다. 참조값끼리 아무리 복사를 하더라도 객체 내부의 메모리끼리의 복사는 이루어지지 않습니다. 단지 참조값만 복사됩니다. 이러한 이유에서 자바에서는 값에 의한 호출(Call by Value)만 존재합니다. 참조값 복사도 값복사입니다!

지금까지 참조변수에 대해서 알아보았습니다. 
객체를 생성하는 단 하나의 구문으로 상당한 시간을 투자한 것입니다. 단 한줄이지만 그만한 가치가 있었을 것입니다.

MotorCycle m = new MotorCycle(); 
    MotorCycle: 새로운 사용자 정의 데이터 타입 
    m: MotorCycle 데이터 타입으로 선언한 참조변수(객체변수) 
    new: 메모리를 생성하는 연산자, 참조값을 리턴합니다. 
    MotorCycle(): 메모리 생성 후 해당 메모리의 초기화 작업을 담당하는 생성자 

결론 

객체변수는 참조변수입니다. 
객체의 이름에 참조값(Reference)이 할당되기 때문에 다른 참조값을 할당한다 하더라도 실제 객체에 연결된 내부의 메모리는 복사되지 않습니다. 
단순히 참조값에 대한 값복사만 이루어집니다.  
참조값만 갖고 있으면 해당 객체를 핸들할 수 있습니다. 

클론(clone)이라는 기법을 이용하면 연결된 메모리까지 복사할 수 있습니다.(메모리 차원의 객체 복사 기법) 

객체의 실제 메모리를 복사하려면서 클론(clone)이라는 기법을 사용합니다. 
이 절에서 나오는 객체의 할당에서 가장 중요한 부분은 바로 객체는 참조값이라는 것입니다. 
주소의 참조값 즉 레퍼런스(Reference)라는 것은 할당을 하더라도 단순한 레퍼런스만 복사될 뿐 객체의 메모리는 복사되지 않습니다. 객체를 복사할 수 없기 때문에 자바는 객체에 대한 메모리 복사를 위한 Cloneable이라는 인터페이스를 제공합니다. 
객체끼리 할당하고자 하면 당연히 데이터 타입이 같아야 하며, 할당이 이루어질 때 참조값만 복사된다는 것을 알아야 합니다.

반응형

'Java 배우기' 카테고리의 다른 글

Modifier 접근제한자  (0) 2016.01.04
Member field(variable) method 멤버 필드(변수) 메소드  (0) 2016.01.04
Method 메소드  (0) 2016.01.04
자바 쉬운 예제  (0) 2016.01.04
영어 원문과 함께 Java 배우기 (1)  (0) 2015.12.17

댓글