[JAVA] Call by value Vs. Call by reference

Jay·2022년 1월 28일
0

메서드를 호출하는 두 가지 방식으로 Call by value, Call by reference가 있는데, 메서드에 어떤 타입의 값이 전달되는지에 따라 구분된다.

Call by value(값에 의한 호출)

Call by value는 메서드를 호출하면서 파라미터를 값으로 전달한다.
전달된 값은 호출된 함수에서 지역변수로 저장되며 따라서 이 변수들은 메서드 외부에 영향을 주지 않는다.

public class Value {
    public static void main(String[] args) {
        int math = 80;
        int society = 70;
        System.out.println("main: math = " + math + ", society = " + society);

        addHomework(math, society);
        System.out.println("main: math = " + math + ", society = " + society);
    }

    private static void addHomework(int math, int society) {
        math += 10;
        society += 5;
        System.out.println("addHomeWork: math = " + math + ", society = " + society);
    }
}

>
main: math = 80, society = 70
addHomeWork: math = 90, society = 75
main: math = 80, society = 70

코드 설명
1. main 메서드에서 math, society 변수를 선언한다.
2. addHomework 메서드에 math, society 변수를 파라미터로 담아 전송한다.
3. addHomework 메서드에서 math, society 변수를 받아 각각 10, 5점을 더한다.
4. 하지만 main 메서드의 math, society 변수 값은 addHomework 메서드 호출 전과 동일하다.

여기서 3번 과정을 통해 math, society 변수를 변경해도 4번과 같은 결과가 나오는 이유는 근본적으로 main 메서드와 addHomework 메서드의 math, society 변수가 이름만 같은 완전히 다른 변수이기 때문이다. 2번 과정에서 각 변수가 전달될 때 addHomework 메서드에도 동일한 이름의 math, society 변수가 지역 변수로 생성이 되고, 각각 main 메서드에서 전송한 값(value)으로 초기화가 된다. 하지만 3번 과정을 통해 addHomework 메서드 내의 지역변수 값이 변경되고, 이와 전혀 다른 변수인 main 메서드의 math, society 변수는 (당연히 이 역시 지역변수다) 영향을 받지 않는다. 메서드 안에 있는 메서드가 아닌 독립된 메서드의 지역변수 끼리는 접근이 불가하기 때문이다.

Call by reference(참조에 의한 호출)

Call by reference는 메서드를 호출할 때 파라미터에 전달하려는 값이 저장된 메모리 주소를 전달한다.
그래서 지역변수가 메서드내에 생성되기는 하지만 그 지역 변수에 할당된 값이 메서드 밖에 존재하는 메모리 영역이기 때문에, 메서드 내에서 값을 변경함으로서 외부에 영향을 미칠 수 있다.

public class Reference {
    public static void main(String[] args) {

        Students elio = new Students(70, 90);
        System.out.println("elio = " + elio);

        addHomework(elio);
        System.out.println("elio.math = " + elio.literature);
    }

    private static void addHomework(Students student) {

        student.literature += 30;
        System.out.println("student.math = " + student.literature);
        System.out.println(student);
    }
}

public class Students {

    int literature;
    int music;

    public Students(int literature, int music) {
        this.literature = literature;
        this.music = music;
    }
}

>
elio = com.practice.self.call.Students@36baf30c
student.math = 100
com.practice.self.call.Students@36baf30c
elio.math = 100

코드 설명
1. main 메서드에서 students의 인스턴스를 elio라는 객체 참조 변수에 담아 선언한다.
2. elio 변수를 addHomework 메서드에 인자로 담아 호출한다.
3. addHomework 메서드에서 인자로 들어온 student 변수를 지역 변수로 선언하고, student의 문학 점수에 30점을 더한다.

문학 점수가 상승한 것은 addHomework메서드에서 선언된 지역변수 student인데 메인 메서드에 있는 elio의 문학 점수도 상승했다. 즉, addHomework 메서드의 작업이 main 메서드에도 영향을 미쳤다는 것인데 그 이유는 main 메서드가 인자로 담은 elio 변수를 단순히 value로 전송한 것이 아니라 heap 영역에 저장된 elio 인스턴스의 메모리 주소(reference)를 전송했기 때문이다.

main메서드에서 elio라는 객체 참조 변수를 선언하면 메모리에 있는 힙 영역에 Student 클래스의 인스턴스를 생성하고, elio 변수에 생성한 인스턴스의 메모리 주소를 할당한다. 그래서 main 메서드가 addHomework를 호출할 때 전송한 elio는 메모리 주소 자체다. addHomework를 실행하면 매개변수로 들어온 Students 타입을 가지고 있는 student를 addHomework의 스택 프레임에 생성하고, student에 전송된 elio의 힙 주소를 저장하게 된다. 그래서 student의 문학 점수를 변경하면 main 메서드에 있는 elio의 데이터도 영향을 받는 것이다.

자바는 Call by value

위 예시를 보면 자바는 call by value, call by reference 둘 다 사용하고 있는 것처럼 보이지만 자바는 Call by value 방식을 사용하고 있다.

  • 자바는 전송하려는 값 자체를 전달한다. 즉 위 사례처럼 math와 society 값을 전달하고 싶다면 math, society에 저장된 값을 전달하고 elio를 전달하고 싶다면 elio에 저장된 값을 전달한다. 단지 데이터 타입에 따라 저장된 값을 값으로 해석할지, 메모리 주소로 해석할지에 차이가 있을 뿐이다.
  • 인자가 원시타입인 경우(boolean, char, byte, short, int, long, float, double) 값을 값 자체로 해석하고, 그 외 참조타입(위 원시타입에 속하는 8가지 외의 타입)인 경우에는 메모리 주소로 해석한다.
profile
You're not a computer, you're a tiny stone in a beautiful mosaic

0개의 댓글