Call By Value

: 값에 의한 호출형태로 값을 복사하는 것

public class CallByValue {
    public static void main(String[] args) {
				int a = 10, b = 20; //step 1, step 2
				System.out.println(plus(a, b)); // step 3
				System.out.println(a); //10
    }

    public static int plus(int x, int y) {
				x += y; //step 4
        return x;
    }
}

step 별 메모리 변화

step 별 메모리 변화

두개의 int형 인자값을 더해서 반환하는 간단한 덧셈 정적 메서드와 이 메서드에 10, 20을 인자값으로 전달하여 반환값을 출력하고 있다.

이 때, 각각의 인자값 10과 20은 기본 타입(primitive type)으로 값은 복사되어 전달되기에 a에 b를 더해줬지만 전혀 영향을 받지 않는다. 그렇기에 plus 메소드 호출 후 다시 a를 출력해도 결과 값은 10이 나온다.

즉, Call By Value에서는 메서드 호출 시 사용되는 인자의 메모리에 저장되어 있는 값을 복사하여 보낸다.

Call By Reference

: 값이 아닌 주소(Address)를 보내면서 전달받은 곳에선 해당 주소를 참조하여 데이터에 접근한다.

public class CallByReference {
    public static void main(String[] args) {
        Food apple = new Food("사과", 1000);
        System.out.println("apple = " + apple);

        updatePriceByShelfLife(apple);

        System.out.println("apple = " + apple);
    }

    private static void updatePriceByShelfLife(Food food) {
        final Period period = food.getOverShelfLife(LocalDate.of(2021, Month.MAY, 23));
        if (!period.isNegative()) {
            food.changePrice(food.getPrice() - 500);
        }

    }

    private static class Food {
        private String name;
        private int price;
        private LocalDate shelfLife;

        public Food(String name, int price) {
            this.name = name;
            this.price = price;
            shelfLife = LocalDate.of(2021, Month.MAY, 20);
        }

        public String getName() {
            return name;
        }

        public int getPrice() {
            return price;
        }

        public Period getOverShelfLife(LocalDate targetDate){
            return Period.between(shelfLife, targetDate);
        }

        public void changePrice(int price) {
            this.price = Math.max(price, 0);
        }

        @Override
        public String toString() {
            return "[name: " + name + ", price: " + price + "]";
        }
    }
}

⇒ update 메소드 호출 전 : [name: 사과, price: 1000]

⇒ update 메소드 호출 후: [name: 사과, price: 500]

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5ec03f7e-0143-4853-bd46-e384885823ec/Untitled.png

로직의 updatePriceByShelfLife() 정적 메소드를 통해 해당 음식의 유통기한을 검사해 유통기간이 지났을 경우 500원을 깎는 로직이다. 여기서 main 로직에 있는 apple과 updatePriceByShelfLife() 에 인자값으로 전달된 apple은 같은 참조주소를 바라본다.

그렇기에 apple을 매개변수로 받은 정적 메서드 updatePriceByShelfLife에서 apple의 값을 수정하면,

main로직의 apple도 동일하게 가격이 변경되있는 것이다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/320870f5-5bc9-47d1-93cd-b4300bd7e47d/Untitled.png

그래서 Java의 참조변수는 call by value인가 call by reference 인가?

위 예제를 보고 생각을 하면 당연히 Call By Reference 라는 생각이 들 수 있지만, 이런 고민을 해 볼 필요가 있다. updatePriceByShelfLife(apple); 이 메서드를 호출하면서 apple을 넘겨줄 때 참조 주소 값을 넘겨주고 매개변수를 받은 메서드측에서도 해당 참조주소를 참조해서 객체를 참조한다.