[Java/TIL] Java에서의 Pass By Value, Pass By Reference

resilient

·

2024. 3. 6. 22:23

728x90
반응형


이전 포스팅을 찾아보면 pass by value, pass by reference을 주제로 작성한 글이 있습니다.

 

해당 글이 너무 편협한 생각, 상식이라는 사실을 알았고, 아래 포스팅이 왜 부족하고, 잘못된 정보는 무엇이며 정확한 사실이 무엇인가에 대한 글을 써보려고 합니다.

 

 

[개발] Pass By Value vs Pass By Reference 란?

최근 첫 기술면접을 봤습니다. 면접은 무조건 여러 번을 봐야 한다는 말을 새삼 느끼게 되더군요. 면접 질문 중에 Pass by value, Pass by reference에 대해서 아시나요? 가 있었습니다. 이번 기회에 대해

resilient-923.tistory.com

 

0. 위 포스팅 정리

 

먼저 위 포스팅 중 아래와 같이 정리한 부분이 있습니다.

 

Java의 객체 전달 방식이 Pass By Reference가 아닌가 혼란이 오는데요. 결론적으로 원본데이터가 수정되어야 Pass By Reference방법이라고 할 수 있겠네요.

 

이제 위 문장에서 어디가 애매한지 알아보고 확실하게 정의해보도록 하겠습니다.

 

1. 자바는 Pass By Reference 방식으로 동작하지 않습니다.

 

이전까지는 Reference 방식이라고 하면 참조방식 = 객체를 참조 이렇게 단편적으로 생각했었습니다.

 

하지만 자세히 들여다보면, 객체에 대한 참조를 '값'으로 전달하게 됩니다.

 

실제 객체를 가리키는 참조의 두 복사본이 동일한 객체를 가리키기 때문에, 한 참조를 만들어진 변경이 다른 참조를 통해서 보이게 되는 것뿐이죠.

 

2. 원시 타입, 참조 타입이 어떻게 동작하는지 살펴보겠습니다.

 

2-1. 원시 타입의 동작원리

 

아래 코드는 원시 타입(Primitive Type) int로 선언된 변수가 있고, add 함수에서 전달받은 값이 1을 더합니다.

당연히 결과는 True가 나오겠죠.

class PassByValue {
	
    @Test
	void addMethod(){
    	int x = 10;
        add(x);
        assertThat(x).isEqaulTo(10);
    }
    
    private void add(int num){
    	num++;
    }
}

 

자바에서 원시 타입은 스택 영역에 할당이 됩니다.

 

1. x가 스택 영역에 할당이 됩니다.

 

2. add 메서드를 호출합니다. 자바는 값을 복사하여 전달하기 때문에 add 함수에서 사용될 변수 num이 x값이 복사가 됩니다.

그리고 num에 1을 더해주면 'x값을 가져온 num'에 1을 더하는 것이지 'x의 값'에 1을 더하는 것이 아니게 되는 거죠.

 

3. 만약에 자바가 값을 전달하지 않고 참조를 전달하는 방식으로 동작했다면 x의 값도 증가했을 겁니다. 하지만 자바는 값을 복사하여 전달하는 방식으로 동작하기 때문에 기존의 변수인 x는 변화가 없는 것이죠.

 

2-2. 참조 타입의 동작원리

 

아래 코드는 참조 타입(Reference Type)으로 선언된 String 배열 변수 words변수가 있고, add 함수에서는 배열의 1번 인덱스에 값을 할당해 줍니다.

당연히 결과는 True가 나옵니다.

class PassByValue {

    @Test
    void addArray() {
        String[] words = new String[2];
        words[0] = "Hello";
        add(words);

        assertThat(words[1]).isEqualTo("Java");
    }

    private void add(String[] arrays) {
        arrays[1] = "Java";
    }
}

 

자바에서 참조 타입은 스택이 아닌 힙 영역에 할당이 됩니다.

힙 영역을 가리키는 변수 자체는 스택 영역에 생성되고, 배열은 힙 영역에 할당되는 것이죠.

 

그다음 arrays변수는 words에서 복사되어 생성되므로 words와 별개로 존재하게 됩니다.

 

하지만 두 값은 모두 동일한 참조값을 가리키고 있습니다. 따라서 add 함수에서 arrays의 값을 변경하게 되면 같은 값을 참조하고 있는 words도 영향을 받게 되는 것이죠. 이러한 동작은 자바가 참조 '값'을 전달하는 방식으로 동작한다는 것을 알 수 있습니다.

 

3. 정리

자바에서 참조(Reference)라는 개념은 존재하지만, Pass By Reference로 동작하지는 않습니다. 저를 포함한 많은 개발자들이 참조를 전달하는 방식에 혼란을 겪었기에, 제임스 고슬링은 자바에서 의도적으로 이를 빼버렸다.

 

자바 언어에서 참조라는 것은 객체가 힙에 저장된 위치를 가리키는 메모리 주소입니다. 따라서 참조는 실제 객체에 대한 alias가 아니라 실제 객체에 접근하고 조작하는 방법이라고 볼 수 있죠.

 

다음은 자바 언어 스펙(Java Launguage Specification, JLS)에서 정의한 참조(Reference)에 대한 정의입니다.

“참조 혹은 참조 값은 객체에 대한 포인터에 해당하며, 참조할 객체가 없다면 null 참조를 갖는다.”

 

 

 

현실에서는 객체의 참조값을 전달하는 것(pass reference value)을 pass by reference라고 통용하고 있습니다.

 

하지만 pass by reference는 프로그래밍 언어의 설계 방식에 대한 내용이고, FORTRAN과 같은 일부 언어들은 해당 방식을 사용한다고 합니다.

 

결론은 우리가 자바에서의 pass by reference는 참조하고 있는 alias 그 자체가 아닌, 참조 '값'을 할당받아서 사용하게 되는 것입니다.

반응형