ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - Call by Value & Call By Reference
    Language/Java 2022. 2. 21. 13:23

    Call by Value & Call by Reference

    Call by Value(값에 의한 호출)
    void function(int number) {
        number = 20;
    }
    
    void main() {
        int number = 10;
        function(number);
        printf("%d", number);   // 출력되는 값은 10
    }

     - 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시공간이 생성되고, 종료 시 해당 공간이 사라진다.

     - 해당 호출 방식은 함수 호출 시 전달되는 변수 값을 복사해서 함수 인자로 전달한다. 이때, 복사된 인자는 함수 안에서 지역적으로 사용되기 때문에 local value의 속성을 가진다.

     - 따라서, 함수 안에서 인자 값이 변경되더라도, 외부 변수 값은 변경되지 않는다.

     

    Call by Reference(참조에 의한 호출)
    void function(int *number) {
        *number = 20;
    }
    
    void main() {
        int number = 10;
        function(&number);
        printf("%d", number);   // 출력되는 값은 20
    }

     - 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.

     - 참조에 의한 호출 방식은 함수 호출 시 인자로 전달되는 변수의 레퍼런스(주소)를 전달한다.

     - 따라서, 함수 안에서 인자의 값이 변경되면, 인자로 전달된 외부 변수의 값도 변경된다.


    Java의 함수 호출 방식

    Java 기본형(Primitive Type)의 함수 호출 방식
    public class Main {
        public static void main(String[] args) {
            int x = 1;
            int y = 1;
            
            plus(x, y);
            
            System.out.println("x = " + x + ", " + "y = " + y);   // "x = 1, y = 1" 출력
        }
        
        static void plus(int a, int b) {
            a = a + 1;
            b = b + 1;
        }
    }

     - 위 코드와 같이 두 개의 변수를 인자로 받아 1씩 증가시키는 메서드를 만든 후 실행해 보았다.

     - 실행을 하면 확인해볼 수 있듯이 인자로 넘겨준 두 변수들의 값이 변경되지 않고, 그대로 출력하는 것을 알 수 있다.

     - 이러한 결과가 나온 이유를 알기 위해서는 Java의 기본형은 JVM의 Stack 영역에 저장된다는 것을 이해해야 한다.

     

     

     - main 함수가 실행되면서 String[] args 매개변수부터 Stack 영역에 쌓이게 된다.

     - 그다음 x, y 변수를 생성하고 plus() 메서드의 인자 값으로 넘겨준다. 이때, 넘어온 인자 값은 x, y 변수가 아닌 변수의 담긴 값 1만 복사해서 넘겨주는 방식이다.

     - 그 후, 매개변수 a, b가 Stack 영역에 쌓이게 된다.

     

     

     - plus() 메서드가 종료되면 위 그림과 같이 a, b는 Stack 영역에서 사라지게 된다.

     - 그래서 결국 x, y 변수는 1을 출력하게 된다.

     - JVM의 Stack 영역의 동작 과정을 살펴보면서 Java의 기본형은 Call by Value 방식으로 처리되는 것을 확인해보았다.

     

    Java 참조형(Reference Type)의 함수 호출 방식

     - Java 참조형의 경우 객체의 "주소 값"을 매개변수로 전달하기 때문에 Call by Reference가 아니냐는 의문을 가질 수 있다. 하지만, 정확하게 말하면 "주소 값"이 아닌, "주소를 가리키는 참조값"을 넘겨주는 방식이다.

     - 또한, 주소 값 자체를 "복사 없이" 인자로 전달하는 것이 아닌, 자기 자신이 가지고 있는 값을 "복사해서" 전달한다.

     - 결국, 기본형 변수나 참조형 변수 모두 자기 자신이 가지고 있는 값을 복사해서 전달하기 때문에 Call by Value 방식이라고 할 수 있다.

     

    class Sample {
        int number;
        
        public Sample(int number) {
            this.number = number;
        }
        
        public int getNumber() {
            return number;
        }
        
        public void setNumber(int number) {
            this.number = number;
        }
    }

     - 필드가 하나 있는 Sample 클래스를 선언하였다.

     

    public class Main {
    	public static void main(String[] args) throws IOException {
    		Sample x = new Sample(1);
    		System.out.println("x = " + x);   // "x = Main$Sample@279f2327" 출력
    		System.out.println("x.number = " + x.getNumber());   // "x.number = 1" 출력
    
    		change(x);
    
    		System.out.println("x = " + x);   // "x = Main$Sample@279f2327" 출력
    		System.out.println("x.number = " + x.getNumber());   // "x.number = 1" 출력
    	}
    
    	static void change(Sample a) {
    		a = new Sample(2);
    	}
    }

     - Sample 타입의 변수 x를 생성하고, number 필드를 1로 초기화하였다.

     - change() 메서드를 이용해서 인자로 넘어온 인스턴스를 새로운 값으로 초기화한 후 각각의 참조값과 필드 값을 출력해 보면, 필드 값이 바뀌지 않고 기존의 값을 유지하는 것을 확인할 수 있다.

     - 이런 부분을 보면 Java가 참조형 또한 Call by Value임을 알 수 있다.

     - 이러한 결과가 나오는 것을 이해하기 위해서는 Java의 참조형은 JVM의 Heap 영역에 저장된다는 것을 이해해야 한다.

     

     

     - 먼저 Sample 객체의 인스턴스 x를 생성하면, 참조 변수와 참조값이 Stack 영역에 생성되고 실제 인스턴스 값은 Heap 영역에 생성된다.

     - change() 메서드가 실행되면서 2라는 필드 값을 가진 Sample 객체의 새로운 인스턴스를 생성한 후 a에 넣는다.

     - 참조 변수 a에는 참조값이 저장되고 실제 인스턴스 값은 Heap 영역에 저장된다.

     - 따라서, 메서드가 종료되었을 때 참조 변수 x는 기존에 자신이 가리키고 있던 인스턴스를 계속해서 참조하고 있는 것이다.

     

    메서드 내부에서 참조형의 필드 값을 변경하는 경우
    public class Main {
    	public static void main(String[] args) throws IOException {
    		Sample x = new Sample(1);
    		System.out.println("x = " + x);   // "x = Main$Sample@279f2327" 출력
    		System.out.println("x.number = " + x.getNumber());   // "x.number = 1" 출력
    
    		change(x);
    
    		System.out.println("x = " + x);   // "x = Main$Sample@279f2327" 출력
    		System.out.println("x.number = " + x.getNumber());   // "x.number = 2" 출력
    	}
    
    	static void change(Sample a) {
    		a.setNumber(2);
    	}
    }

     - 이번에는  메서드 내에서 객체를 새로 생성하는 것이 아닌, 필드 값을 setNumber() 메서드를 이용해서 바꾸어 보았다.

     - 그런데, 이 경우에는 기존의 객체가 가지고 있는 필드 값과 다른 필드 값을 출력하는 것을 확인할 수 있었다.

     - 어떻게 해서 이러한 결과가 나오는지 그림을 통해서 알아보겠다.

     

     

     - 먼저, Sample 인스턴스를 생성하면, 참조 변수 x와 참조값 279f2327이 Stack 영역에 들어간다. 그리고 실제 인스턴스 값은 Heap 영역에 저장된다.

     - change() 메서드가 실행되면 매개변수로 들어간 참조값 279f2327이 복사되고 참조 변수 a와 참조값 6cd8737이 Stack 영역에 들어가게 된다.

     - 결국, 참조값이 같기 때문에 두 참조 변수는 Heap 영역의 같은 값을 바라보고 있는 것이다.

     

     

     - 따라서, 위 그림과 같이 a의 값을 setNumber() 메서드를 이용해서 변경하면 x도 같은 값을 바라보고 있기 때문에 변경이 되는 것이다.

     - 결국, Java는 기본형이든 참조형이든 모두 Call by Value 방식을 사용한다고 알 수 있다.


    결론

    Java는 기본형 변수(Primitive Type)와 참조형 변수(Reference Type)가 있는데 둘 다 Call by Value 방식으로 처리한다.

    기본형 변수는 변수가 가지고 있는 값을 복사해서 넘겨주는 방식이고, 참조형 변수의 경우 값의 레퍼런스가 복사돼서 넘겨주는 방식이다.


    출처

    https://gyoogle.dev/blog/computer-language/Java/Call%20by%20value%20&%20Call%20by%20reference.html

    https://jackjeong.tistory.com/37

    https://siyoon210.tistory.com/104

     

    728x90

    'Language > Java' 카테고리의 다른 글

    Java - static  (0) 2022.03.04
    Java - 동일성(identity) & 동등성(equality)  (0) 2022.03.03
    Java - 캐스팅(Casting)  (0) 2022.02.19
    Java - 문자열 클래스  (0) 2022.02.19
    Java - Boxing & Unboxing  (0) 2022.02.19

    댓글

Designed by Tistory.