본문 바로가기

Java

JAVA - 함수 호출 방식 call by value, call by reference

파이썬을 잠깐 찍먹해보고 자바를 사용하던 나에게 함수 호출 방식은 쉬우면서도 어려운 개념이었다. 

이번 기회에 자바의 함수 호출 방식에 대해 정리해보려고 한다.

 

우선 편의상 함수는 매서드라고 부르겠다. 매서드 호출 방식이란 매서드의 인자를 호출할 때 어떤 방식으로 호출하는지는 말한다.

 

call by value -  인자의 값을 호출

call by reference -  인자의 참조를 호출 

자바의 매서드 호출 방식은 call by value 해당한다.

class Person {
    int myAge;
    int myWeight;

    public void oneYearAgo(int age, int weight) {
        age = age + 1;
        weight = weight + 1;
        System.out.println("메서드 내부 " + age + " " + weight);
    }

    public void oneYearAgoV2(int age, int weight) {
        this.myAge = age + 1;
        this.myWeight = weight + 1;
        System.out.println("메서드 내부 " + this.myAge + " " + this.myWeight);
    }
}


void oneYearLater() {
    Person person = new Person();
    person.myAge = 20;
    person.myWeight = 65;

    System.out.println("1년이 흐르기 전 :" + person.myAge + " " + person.myWeight);
    person.oneYearAgo(person.myAge, person.myWeight);
    System.out.println("1년이 흐른 후 :" + person.myAge + " " + person.myWeight);

}

OUTPUT

1년이 흐르기 전 :20 65
메서드 내부 21 66
1년이 흐른 후 :20 65

 

자바만을 사용한 사람이라면 너무나도 당연해서 헷갈릴 수 있는 내용이다. 코드를 살펴보자

 

 public void oneYearAgo(int age, int weight) {
        age = age + 1;
        weight = weight + 1;
        System.out.println("메서드 내부 " + age + " " + weight);
    }

person.oneYearAgo(person.myAge, person.myWeight);

 

oneYearAge 매서드의 인자로  person.myAge, person.myWeight를 받습니다.
person.myAge 가 참조하고 있는 20이라는 값을 호출합니다.
person.myWeight 가 참고하고 있는 65라는 값을 호출합니다.

age 매개변수가 스택 영역에 올려지고 20이라는 값을 참조합니다.
weight 매개변수가 스택 영역에 올려지고 65라는 값을 참조합니다.

 

이렇기 때문에 person 의 객체의 필드인 myAge, myWeight 에는 어떠한 변화도 존재하지 않습니다. 

 

그저 age, weight는 매소드 내부에서 변화가 일어나고

매소드의 마지막 줄인 출력문이 호출된 뒤 스택 영역에서 사라지게 됩니다.

System.out.println("메서드 내부 " + age + " " + weight);

 

간단해보이는 내용인데 헷갈리는 이유는 아래와 같은 이유일지도 모르겠습니다.

(아니면 제가 이해를 잘 못하고 있는 것일수도 있겠네요 ㅠ)

 

 


 

아래 코드는 실제 필드 값의 변화가 일어나는 코드입니다.

 

class Person {
    int myAge;
    int myWeight;

    public void oneYearAgo(int age, int weight) {
        age = age + 1;
        weight = weight + 1;
        System.out.println("메서드 내부 " + age + " " + weight);
    }

    public void oneYearAgoV2(int age, int weight) {
        this.myAge = age + 1;
        this.myWeight = weight + 1;
        System.out.println("메서드 내부 " + this.myAge + " " + this.myWeight);
}


    void oneYearLater() {
        Person person = new Person();
        person.myAge = 20;
        person.myWeight = 65;

        System.out.println(person.myAge + " " + person.myWeight);
        person.oneYearAgoV2(person.myAge, person.myWeight);
        System.out.println(person.myAge + " " + person.myWeight);

    }

 

OUTPUT

1년이 흐르기 전 :20 65
메서드 내부 21 66
1년이 흐른 후 :21 66

 

값이 변했다고 해서 call by reference 인 것은 아닙니다. 매서드 내 인자에는 이전과 동일하게 call by value 방식으로 인자를 호출했습니다.

 

public void oneYearAgoV2(int age, int weight) {
    this.myAge = age + 1;
    this.myWeight = weight + 1;
    System.out.println("메서드 내부 " + this.myAge + " " + this.myWeight);
}

person.oneYearAgoV2(person.myAge, person.myWeight);

 

oneYearAgeV2 매서드의 인자로  person.myAge, person.myWeight를 받습니다.
person.myAge 가 참조하고 있는 20이라는 값을 호출합니다.
person.myWeight 가 참고하고 있는 65라는 값을 호출합니다.

age 매개변수가 스택 영역에 올려지고 20이라는 값을 참조합니다.
weight 매개변수가 스택 영역에 올려지고 65라는 값을 참조합니다.
this.myAge 참조 변수가 스택 영역에 선언되고 (age 매개변수가 참조하는 20이라는 값 + 1)을 참조하게 됩니다.
this.myWeight 참조 변수가 스택 영역에 선언되고 (weight 매개변수가 참조하는 65라는 값 + 1)을 참조하게 됩니다.

출력문이 호출되고
매소드 내 인자 age, weight는 사라지게됩니다.

중요한 점은 매서드의 인자는 이전과 같이 값을 참조한다는 점입니다. 

값이 변한 이유는 인스턴스의 필드 변수를 호출하여 참조 값을 변경해주었기 때문입니다.