Java

참조

복제

전자화된 시스템의 가장 중요한 특징은 복제다. 현실의 사물과 다르게 전자화된 시스템 위의 데이터를 복제 하는데는 비용이 거의 들지 않는다. 바로 이러한 특징이 소프트웨어를 기존의 산업과 구분하는 가장 큰 특징일 것이다. 프로그래밍에서 복제가 무엇인가를 살펴보자.

package org.opentutorials.javatutorials.reference;

public class ReferenceDemo1 {

    public static void runValue(){
		int a = 1;
		int b = a;
		b = 2;
		System.out.println("runValue, "+a);	
	}

	public static void main(String[] args) {
		runValue();
	}

}

결과

runValue, 1

결과는 당연하다. 값을 변경한 것은 변수 b이기 때문에 변수 a에 담겨있는 값은 그대로이다. 변수 b의 값에 변수 a의 값이 복제된 것이다. 이를 그림으로 표시하면 아래와 같다.

 

참조

그런데 자연의 산물이 아니라 거대한 약속의 집합인 소프트웨어의 세계에서 당연한 것은 없다. 이것이 당연하지 않은 이유는 다음 예제를 통해서 좀 더 분명하게 드러난다.

package org.opentutorials.javatutorials.reference;
class A{
    public int id;
    A(int id){
		this.id = id;
	}
}
public class ReferenceDemo1 {

	public static void runValue(){
		int a = 1;
		int b = a;
		b = 2;
		System.out.println("runValue, "+a);	
	}
	
	public static void runReference(){
		A a = new A(1);
		A b = a;
		b.id = 2;
		System.out.println("runReference, "+a.id);		
	}

	public static void main(String[] args) {
		runValue();
		runReference();
	}

}

차이점

결과

runValue, 1
runReference, 2

이 코드의 주인공은 아래와 같다.

b.id = 2;
System.out.println("runReference, "+a.id);	

놀라운 차이점이 있다. 변수 b에 담긴 인스턴스의 id 값을 2로 변경했을 뿐인데 a.id의 값도 2가 된 것이다. 이것은 변수 b와 변수 a에 담긴 인스턴스가 서로 같다는 것을 의미하다. 참조(reference)의 세계에 온 것을 환영한다.

앞서 필자는 전자화된 세계에서 가장 중요한 특징으로 복제를 들었다. 그런데 복제만으로 전자화된 시스템을 설명하는 것은 조금 부족하다. 비유하자면 복제는 파일을 복사하는 것이고 참조는 심볼릭 링크(symbolic link) 혹은 바로가기(윈도우)를 만드는 것과 비슷하다. 원본 파일에 대해서 심볼릭 링크를 만들면 원본이 수정되면 심볼릭 링크에도 그 내용이 실시간으로 반영되는 것과 같은 효과다. 심볼릭 링크를 통해서 만든 파일은 원본 파일에 대한 주소 값이 담겨 있다. 누군가 심볼릭 링크에 접근하면 컴퓨터는 심볼릭 링크에 저장된 원본의 주소를 참조해서 원본의 위치를 알아내고 원본에 대한 작업을 하게 된다. 다시 말해서 원본을 복제한 것이 아니라 원본 파일을 참조(reference)하고 있는 것이다. 덕분에 저장 장치의 용량을 절약할 수 있고, 원본 파일을 사용하고 있는 모든 복제본이 동일한 내용을 유지할 수 있게 된다. 참조는 전자화된 세계의 극치라고 할 수 있다.

프로그래밍에서 광범위하게 사용하는 라이브러리라는 개념도 일종의 참조라고 할 수 있다. 공용 라이브러리를 사용하게 되면 하나의 라이브러리를 여러 애플리케이션에서 공유해서 사용하게 된다. 라이브러리의 내용이 변경되면 이를 참조하고 있는 애플리케이션에도 내용이 반영되게 된다. 또 우리가 변수를 사용하는 이유도 말하자면 참조를 위해서라고 할 수 있을 것이다. 본질을 파악하면 이해력도 높아지고 암기할 것도 줄어든다.

아래 두 개의 구문의 차이점을 생각해보자.

int a = 1;
A a = new A(1);

무엇일까? 전자는 데이터형이 int이고 후자는 A이다. int는 기본 데이터형(원시 데이터형, Primitive Data Types)이다. 자바에서는 기본 데이터형을 제외한 모든 데이터 타입은 참조 데이터형(참조 자료형)이라고 부른다. 기본 데이터형은 위와 같이 복제 되지만 참조 데이터형은 참조된다. new를 사용해서 객체를 만드는 모든 데이터 타입이 참조 데이터형이라고 생각해도 된다. (단 String은 제외다) 이를 그림으로 나타내면 아래와 같다.

정리하면 변수에 담겨있는 데이터가 기본형이면 그 안에는 실제 데이터가 들어있고, 기본형이 아니면 변수 안에는 데이터에 대한 참조 방법이 들어있다고 할 수 있다.

참조 데이터 형과 매개 변수

그럼 일종의 변수할당이라고 할 수 있는 메소드의 매개변수는 어떻게 동작하는가를 살펴보자. 조금 복잡하므로 꼼꼼하게 살펴봐야 한다. 예제를 보자.

package org.opentutorials.javatutorials.reference;

public class ReferenceParameterDemo {
    
	static void _value(int b){
		b = 2;
	}
	
	public static void runValue(){
		int a = 1;
		_value(a);
		System.out.println("runValue, "+a);
	}
	
	static void _reference1(A b){
		b = new A(2);
	}
	
	public static void runReference1(){
		A a = new A(1);
		_reference1(a);
		System.out.println("runReference1, "+a.id);		
	}
	
	static void _reference2(A b){
		b.id = 2;
	}

	public static void runReference2(){
		A a = new A(1);
		_reference2(a);
		System.out.println("runReference2, "+a.id);		
	}
	
	public static void main(String[] args) {
		runValue(); // runValue, 1
		runReference1(); // runReference1, 1
		runReference2(); // runReference2, 2
	}

}

결과는 아래와 같다.

runValue, 1
runReference1, 1
runReference2, 2

위의 예제는 3가지 경우를 설명하고 있다. 하나씩 살펴보자. 

아래 코드는 _value의 매개변수로 기본 데이터형(int)를 전달했다. 

runValue();

메소드 _value의 인자로 a를 전달했다. 인자 a는 매개변수 b가 되어서 _value 안으로 전달되고 있다. _value 안에서 b의 값을 변경했다. _value가 실행된 후에 runValue에서 a값을 출력해본 결과 값이 변경되지 않았다. 호출된 메소드의 작업이 호출한 메소드에 영향을 미치지 않고 있다. 어찌보면 자연스러운 결과다.

두번째 코드를 보자. 아래 코드는 _reference1의 매개변수로 참조 데이터 타입을 전달하고 있다. 

runReference1(); 

메소드 _reference1 안에서 매개변수 b의 값을 다른 객체로 변경하고 있다. 이것은 지역변수인 b의 데이터를 교체한 것일 뿐이기 때문에 runReference1의 결과에는 영향을 미치지 않는다. 

그런데 다음의 코드는 호출된 메소드의 작업이 호출한 메소드의 변수에 영향을 미친다. 

runReference2();

매개변수 b의 값을 다른 객체로 교체한 것이 아니라 매개변수 b의 인스턴스 변수 id 값을 2로 변경하고 있다. 이러한 맥락에서 _reference2의 변수 b는 runReference2의 변수 a와 참조 관계로 연결되어 있는 것이기 때문에 a와 b는 모두 같은 객체를 참조하고 있는 변수다.

매개변수를 다른 객체로 변경하는 것과 참조 데이터 타입의 매개변수에 담겨 있는 객체에 접근하는 것은 완전히 다른 의미를 가지기 때문에 두가지 경우의 차이점을 확실하게 이해하도록 하자.

참조

댓글

댓글 본문
작성자
비밀번호
  1. 오잉
    runReference1() 메소드가 헷갈리네요.

    new A(1) 인스턴스를 참조하고있던 변수 a가 _reference1(a) 메소드를 통해서 인자로 들어가고,
    거기서 new A(2) 인스턴스를 새롭게 참조하고 있는데 그렇게 되면 현재 참조하고있지 않는
    new A(1) 인스턴스는 소멸되어야 하지 않나요?

    오히려 기본형 데이터타입처럼 갑자기 _reference1 메소드 내에 new A(2) 인스턴스를 참조하는
    새로운 변수가 뜬금없이 생성 된 것과 그로인해 출력 된 1 이라는 결과가 좀 이해하기 어렵습니다.

    혹시 new A(1) 인스턴스를 참조 중인 a 변수가_reference1 메소드의 인자로 전달된 후
    값자기 new A(2) 라는 새로운 인스턴스를 만나면서 지역변수화 된 것은 아닐까요?
    _reference1 내에서만 사용되는 지역변수요...

    알쏭달쏭 하네요 ㅠㅠ
  2. 짱슈아빠
    이보슈 학창시절에 글케 졸았으면 이젠 그만 집중하는 방법도 좀 길러보슈. 개인적으로 졸리면 세수하고 이빨 닦고 다시 보슈.
    대화보기
    • circlestar
      c언어의 포인터랑 비슷한개념이네요
    • supersonic
      새로운 시각을 갖게 해주신 생활코딩에 감사합니다.
    • Weaver
      강의 감사합니다~
    • JustStudy
      고맙습니다
    • 카이사르
      감사합니다. 너무 좋은 비유로 여러번 설명해주셔서 이해하는데 많은 도움이 되었습니다!
    • 감사합니당
    • Jinwon Jake Kim
      처음부터 여기까지 왔는데 정말...주옥같은 비유도 많고 설명을 잘해주셔서 큰 어려움없이 잘왔던것 같습니다. 친구들에게도 생활코딩 많이 추천하고 있구요. 정말 좋은 사이트입니다.
    • 김트라슈
      감사합니다!
    • 찐똥구리구리
      뿌옇던 것이 점점 걷히네요. 재밌는 건 뿌옇다고 생각도 못하던 때가 있었을 텐데 말이죠. 좋은 강의 감사드립니다.
    • 오빠는다르다
      감사합니다!!!!
    • 좋은선생님이군여!
      김대호님의 말에 동의합니다
    • supreme_dk
      리눅스에서 언급하는 하드링크, 소프트링크가 생각나네요 잘보고갑니다 :)
    • 박첩구드
      감사합니다. 자바에서 포인터를 대신에서 사용될 것 같군요!
    • 김대호
      정말 감사합니다!

      불과 몇 달전 Java에 관련된 지식이 아무것도 없는 상태에서 들었는데,

      강의 하나하나 이해가 안돼는 부분이 없었고

      정말 아무것도 모르는 초심자들이 듣기 좋은 강의인 것 같습니다

      항상 좋은 컨텐츠를 주셔서 감사합니다 응원하겠습니다!
    • 이진아
      늘 애매한 부분 이었는데 이제 명확히 이해 되었습니다. 정말 감사합니다
    • 지흔킴
      아 정말 ㄹ너무도 감사합니다
      이곳을알수있어서 행운이고 자바를포기하던 찰나에 이고잉님덕에 다시한번더 꿈을위해 도전하고있습니다
    • Sung Gil Yun
      정말...
      감사합니다.
    • 센송
      zz 선비정신

      쎈송합니다
      대화보기
      • 개발괴발
        정말 많은 도움이 되었어요 ㅎㅎ
        이 개념도 정확히 없으면서 현업에서 뛰고 있습니다.......
        얼마전에는 스프링으로 게시판을 구현못해서 털렸죠..

        무튼 ㅎㅎ 감사해요 많이 배워가겠습니다
      • egoing
        쿵야님의 피드백에 대해서 다른분들이 의견을 주고 계신데요~ 우선 감사드립니다. 하지만 쿵야님도 아마도 나쁜 의도가 있는 것은 아닐 것 같습니다.저도 기분 상하지는 않았기 때문에 이에 대해서는 논란이 없는 것이 좋을 것 같아요. 좀 더 상큼한 말투 준비해볼께요~ ㅎㅎ
      • TE31
        쿵야님 지금 웹사이트 어디를 뒤져봐도 자바에 대한 이런 강의는 찾아볼수 없습니다..
        두꺼운 책 하나 사는것 보다 이 분 강의 듣는게 훨씬 좋네요
      • 어이무
        쿵야님 제정신으로 하는 말인가요? 상당한 수준의 강의를 무료로 해주시는 분한테 말투 지적이라니 그 정신머리와 그 개념으로 잘도 사회생활 하시겠네요 온갖 쌍시옷 욕 쓰고싶지만 이 곳을 더럽히기 싫어서 곱게 글 적습니다.
        머리에 지식만 쌓지말고 기본 예의 먼저 쌓고 오세요. 초등 수준도 안되는 예의 수준의 뇌로 지식을 쌓으려 하면 됩니까
      • olvidar
        의지의 차이 아닌가요..

        무상으로 이런 퀄리티 강의 제공해주시는 분에게 너무한 댓글이라고 생각합니다
        대화보기
        • 쿵야
          죄송한데, 말투가 너무 졸립습니다.
        • jeyul
          복제와 참조, 이 번 강의를 들으면서 부족했던(의심스러웠던) 부분이 해소된 것 같습니다.^^

          "자바에서는 기본 자료형을 제외한 모든 자료형을 참조 자료형이라고 부른다. 기본 자료형은 복제되지만 참조 자료형은 참조된다. new를 사용해서 만들어진 객체의 데이터 타입을 참조 자료형이라고 생각해도 된다.(단 String은 제외다)"
        • 동영상 감사합니다 참조까지 보다가 공동공부에 참여해서 봤어요를 계속 누르는데 왠지 조작하는 느낌이(?)들어 멈추었네요 집중 한바퀴 돌고 있습니다
        • ㅇㅇㅇㅇyuk
          쓰레드 has .. is 이런건 없나용 책 다른거로바서요
        • Sirhc
          정성스런 답변 감사해요
          짧게 두가지만 질문할께요

          저는 참조형 변수(키)를 포인터라고 생각했답니다
          결국 b = a
          둘다 변수이고 a가 값을 전달했다면 그 값은 주소
          혹은 주소와 매핑되는 어떤 값이 되겠죠.

          1.둘다 같은 주소값을 갖게 된 것
          이것을 두개의 키를 갖게 된걸로 표현드린다면
          이게 swallow copy(얕은복사)가 아닐까요?
          결국 캐비넷을 열수있는 키가 두개가 생겼으므로
          이것도 일종의 복사다.
          2.만약 이후에 a=null; 해서 키를 없앤다면
          b라는 열쇠로 그 캐비넷을 열수 없을까요?
          아마 가능할것같아요, 캐비넷은 그 위치에
          그대로 있을테니까요

          그래서 참조형의 변수의 대입을 어쨌든 복사라고
          정리했는데, 만일 b = a 가 포인터의 포인터를
          복사한거라면 우와우~~~
          제 생각이 완전 틀린거군요!!!
          a=null하면 b도 그 캐비넷을 열수는 없겠네요?
          대화보기
          • 토닥
            참조자료형에서 b = a는 전적으로 같기 때문에, 동작도 똑같이 할 겁니다.
            프로그램의 동작만을 생각한다면 굳이 그런 코드가 필요없다는 이야기죠!

            그리고 열쇠의 비유로도, 조금 '거시기'한 것이.. (조금 길어질 듯 하니 시간이 있다면 읽어보세요. 초보자의 설명은 장황한법.. ㅎㅎ)
            복사의 개념부터 짚어볼게요.
            원본 한 개를 본떠서 두 개로 만들었다. 이 때 '복사'라는 말을 쓸 수 있겠죠?
            그럼 '복사'의 결과로 나온 결과물은 뭐라고 부를 수 있을까요? '원본'과 '복사본'이면 괜찮겠죠?
            그렇습니다. '복사'하면 '원본'과 '복사본'이 나옵니다. 그럼 이제 우리가 이 원본, 복사본을 가지고 무슨 작업을 할 거라고 상상해 보자구요. 복사한 시점에는 원본과 복사본이 동일하겠지만, 그 이후로 원본과 복사본에 어떤 작업을 한다면? 한쪽 열쇠를 반으로 쪼개는 작업을 했다면? 원본과 복사본은 더 이상 같지 않은 상태가 되겠죠? 네, 바로 그거예요.
            물론 어떤 작업을 할지 말지는 모릅니다. 그렇지만 우린 최소한 이렇게 말할 수 있을 겁니다. "복사한 이후로 원본과 복사본의 값(상태)은 달라질 수 있다."
            이것이 '='연산자로 기본자료형 변수를 복사한 결과입니다.
            그럼 참조자료형은 어떨까요? 참조자료형을 '='연산자로 연산하게 되면, '복사'는 되지 않습니다.
            원본과 복사본은 생기지 않습니다. 어떤 이유로 그렇게 말할 수 있을까요?
            참조자료형의 원본과 복사본은 복사한 시점 이후로도 쭉 동일합니다. 한쪽 열쇠를 부러뜨리면, 다른쪽 열쇠를 부러뜨린 것과 같습니다. a 열쇠를 부러뜨렸다!라고 말할 수 있는 사람이라면, b 열쇠를 부러뜨렸다!라고 말해도 무방하다는 의미입니다. 이렇게 정리할 수 있겠죠. "참조자료형을 복사('='연산)하면, 원본과 또 다른 원본이 생긴다."

            제 정리 때문에 더 헷갈리실까요? ㅎㅎ
            여튼~ 마무리하자면 참조자료형에서 b = a는 '의도하는 대로 복사되지 않습니다'.
            복사하는 것이 아니니까, 복사하려는 의도로 사용하지는 않겠죠~? ^^;
            대화보기
            • Sirhc
              결국 참조형 데이터 타입에서의

              b = a; 는...

              열쇠하나 더 복사한거네요...

              그 이유가 뭘까요?
            • 도로시
              매개변수 부분은 묘하게 헷갈리네요 ㅎㅎ
              영상을 보면서 곰곰 생각해 보면 이해가 잘 되는데..
              앞으로 매개변수 볼 때 직관적으로만 생각했다가 낭패 보지 않게 잘 따져봐야겠네요!
            • egoing
              ^^ 피드백 감사합니다.
              대화보기
              • Diew
                헷갈리는 개념인데도 불구하고 그림과 비유를 통해 정말 쉽게 설명해주셧네요! 감사합니다 ㅎㅎ!!
              버전 관리
              egoing
              현재 버전
              선택 버전
              graphittie 자세히 보기