[번역] Java의 매개변수 전달 메커니즘으로서의 Pass-By-Value

[번역] Java의 매개변수 전달 메커니즘으로서의 Pass-By-Value

1. 소개

메서드(method)에 인수(argument)를 전달할 때, 가장 널리 사용되는 두 가지 방식은 “passing-by-value”와 “passing-by-reference”입니다. 다양한 프로그래밍 언어는 이러한 개념을 다양한 방식으로 사용합니다. Java에 관한 한 모든 것은 엄격하게 Pass-by-Value 입니다 .

이 튜토리얼에서는 Java가 다양한 유형에 대한 인수를 전달하는 방법을 설명합니다.

2. Pass-by-Value vs Pass-by-Reference

매개변수(parameter)를 함수에 전달하는 다양한 메커니즘부터 시작해 보겠습니다.

  • value
  • reference
  • result
  • value-result
  • name

현대 프로그래밍 언어에서 가장 일반적인 두 가지 메커니즘은 “Pass-by-Value”와 “Pass-by-Reference”입니다. 계속 진행하기 전에 먼저 다음 사항에 대해 논의해 보겠습니다.

2.1. Pass-by-Value

매개 변수가 pass-by-value인 경우 호출자(caller)와 호출 수신자(callee) 메서드는 서로 복사본인 서로 다른 두 변수(variable)에 대해 작동합니다. 한 변수를 변경해도 다른 변수는 수정되지 않습니다.

이는 메소드를 호출하는 동안 호출 수신자 메소드에 전달된 매개변수가 원래 매개변수의 복제물이 됨을 의미합니다. 호출 수신자 메서드에서 수정한 사항은 호출자 메서드의 원래 매개 변수에 영향을 주지 않습니다.

2.2. Pass-by-Reference

매개 변수가 Pass-by-Reference인 경우 호출자와 호출 수신자는 동일한 개체에 대해 작업합니다.

이는 변수가 Pass-by-Reference인 경우 개체의 고유 식별자가 메서드로 전송된다는 의미입니다. 매개변수의 인스턴스 멤버를 변경하면 원래 값도 변경됩니다.

3. Java의 매개변수 전달

모든 프로그래밍 언어의 기본 개념은 “values”과 “references”입니다. Java에서 Primitive(프리미티브, 원시) 타입의 변수는 실제 값을 저장하는 반면 Non-Primitive(논프리미티브, 프리미티브가 아닌) 타입은 참조하는 객체의 주소를 가리키는 참조 변수를 저장합니다. 값과 참조는 모두 스택 메모리에 저장됩니다.

Java의 인수는 항상 passe-by-value입니다. 메서드 호출 중에 value이든 reference이든 각 인수의 복사본이 스택 메모리에 생성된 다음 메서드에 전달됩니다.

Primitive의 경우 값은 단순히 스택 메모리 내부에 복사된 다음 호출 수신자 메서드에 전달됩니다. 기본이 아닌 경우 스택 메모리의 참조는 힙에 있는 실제 데이터를 가리킵니다. 객체를 전달하면 스택 메모리의 참조가 복사되고 새 reference가 메서드에 전달됩니다.

이제 몇 가지 코드 예제를 통해 이것이 실제로 어떻게 작동하는지 살펴보겠습니다.

3.1. 프리미티브 타입의 전달

Java 프로그래밍 언어에는 8가지 기본 데이터 유형이 있습니다 . 프리미티브 변수는 스택 메모리에 직접 저장됩니다.  프리미티브 데이터 타입의 변수가 인수로 전달될 때마다 실제 매개변수는 formal(형식) 인수에 복사되고 이러한 형식 인수는 스택 메모리에 자체 공간을 축적합니다.

이러한 형식 매개변수의 수명은 해당 메서드가 실행되는 동안에만 지속되며, 반환 시 이러한 형식 인수는 스택에서 지워지고 폐기됩니다.

코드 예제를 통해 이를 이해해 보겠습니다.

public class PrimitivesUnitTest {
 
    @Test
    public void whenModifyingPrimitives_thenOriginalValuesNotModified() {
        
        int x = 1;
        int y = 2;
       
        // Before Modification
        assertEquals(x, 1);
        assertEquals(y, 2);
        
        modify(x, y);
        
        // After Modification
        assertEquals(x, 1);
        assertEquals(y, 2);
    }
    
    public static void modify(int x1, int y1) {
        x1 = 5;
        y1 = 10;
    }
}

이러한 값이 메모리에 저장되는 방식을 분석하여 위 프로그램의 주장을 이해해 보겠습니다.

  1. main 메소드의 변수 ” x” 와 ” y” 는 프리미티브 타입이며 해당 값은 스택 메모리에 직접 저장됩니다.
  2. 메소드 modify()을 호출하면 이러한 각 변수에 대한 정확한 복사본이 생성되어 스택 메모리의 다른 위치에 저장됩니다.
  3. 이러한 복사본을 수정하면 해당 복사본에만 영향을 미치고 원래 변수는 변경되지 않습니다.
baeldung - 값으로 전달 - 프리미티브 전달

3.2. 객체 참조의 전달

Java에서는 모든 객체가 내부적으로 힙 공간에 동적으로 저장됩니다. 이러한 개체는 참조 변수라는 참조에서 참조됩니다.

프리미티브 타입과 달리 Java 객체는 두 단계로 저장됩니다. 참조 변수는 스택 메모리에 저장되고 참조 변수가 참조하는 객체는 힙 메모리에 저장됩니다.

객체가 인수로 전달될 때마다 원래 참조 변수와 힙 메모리에서 객체의 동일한 위치를 가리키는 참조 변수의 정확한 복사본이 생성됩니다.

결과적으로 메서드에서 동일한 객체를 변경할 때마다 해당 변경 사항이 원래 객체에 반영됩니다. 그러나 전달된 참조 변수에 새 개체를 할당하면 원래 개체에 반영되지 않습니다.

코드 예제를 통해 이를 이해해 보겠습니다.

public class NonPrimitivesUnitTest {
 
    @Test
    public void whenModifyingObjects_thenOriginalObjectChanged() {
        Foo a = new Foo(1);
        Foo b = new Foo(1);

        // Before Modification
        assertEquals(a.num, 1);
        assertEquals(b.num, 1);
        
        modify(a, b);
        
        // After Modification
        assertEquals(a.num, 2);
        assertEquals(b.num, 1);
    }
 
    public static void modify(Foo a1, Foo b1) {
        a1.num++;
       
        b1 = new Foo(1);
        b1.num++;
    }
}
 
class Foo {
    public int num;
   
    public Foo(int num) {
        this.num = num;
    }
}
baeldung - 값 전달 - 프리미티브 전달 - 초기

위 프로그램의 주장을 분석해 보겠습니다. 우리는 동일한 값 1 을 갖는 객체  와  b를 modify()  메소드에  전달했습니다. 처음에 이러한 개체 참조는 힙 공간에서 두 개의 서로 다른 개체 위치를 가리킵니다.

이러한 참조  및  가 modify() 메서드 에 전달  되면 동일한 이전 개체를 가리키는  참조  a1  및  b1 의 미러 복사본이 생성됩니다.

baeldung - 값으로 전달 - 프리미티브 전달 - 메소드 ca 이전

modify() 메서드에서  참조 a1을 수정하면  원래 개체가 변경됩니다. 그러나 참조 b1  의 경우 새 개체를 할당했습니다. 이제 힙 메모리의 새 개체를 가리키고 있습니다.

b1 에 대한 변경 사항은  원본 개체에 아무것도 반영되지 않습니다.

baeldung - 값으로 전달 - 프리미티브 전달 - 메서드 교정 후

4. 결론

이 글에서는 프리미티브 타입과 프리미티브 타입이 아닌 경우 매개변수 전달이 어떻게 처리되는지 살펴보았습니다.

우리는 Java에서 매개변수 전달이 항상 Pass-by-Value라는 것을 배웠습니다. 그러나 우리가 다루는 대상이 프리미티브 타입인지 객체인지에 따라 컨텍스트가 달라집니다.

  1. 프리미티브 타입의 경우 매개변수는pass-by-value 니다.
  2. 객체 타입의 경우 객체 참조는 pass-by-value입니다.

이 기사에 사용된 code snippet은 GitHub에서 찾을 수 있습니다 .

Comments

No comments yet. Why don’t you start the discussion?

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다