나는 아래와 같이 들은 적이 있다.
자바엔 메서드 호출 시 2가지 호출 방식이 있다.
* Call by Value(값에 의한 호출)
* Call by Reference(참조에 의한 호출)
Call by Value 특징 중 하나는 함수 안에서 인자로 값이 변하여도 호출자의 변수는 변하지 않는다.
그와 반대로,
Call by Reference 의 특징 중 하나는 함수 안에서 인자의 값이 변하면 함수 호출 시 있던 변수에도 반영된다.
덕분에 Call by value와 Call by reference를 변하는 것과 변하지 않는 것으로 이해한 채 여지껏 개발을 진행하였고
그러다가 문제를 만나게 되었다.
<문제 발생>
//Main 클래스 내부
List<String> testList = new ArrayList<>(); //1번
System.out.println("메서드 호출 전: "+ testList.size());
TestService testService = new TestService();
testService.convertList(testList);
System.out.println("메서드 호출 후: "+ testList.size()); //4번
//TestService 클래스
public class TestService {
public void convertList(List<String> testList){
List<String> tempList = new ArrayList<>();
tempList.add("1");
tempList.add("2");
//2번
testList = tempList; //3번
System.out.println("메서드 내부: "+ testList.size());
}
}
tempList 생성 후 해당 객체를 testService.converList 호출하여 내부에서 새로운 객체를 할당해주었다.
[예상결과]
난 Call by reference가 적용되었다고 생각했다.
메서드 호출 전: 0,
메서드 내부:2,
메스드 호출 후 :2
[실제 결과]
틀렸네 왜지????
위 흐름을 메모리 구조에 나타내어 보았다.
1번 부분
2번
3번(중요)
아! convertList 메서드 스택에 testList, tempList 새롭게 주소 할당되었고 스택의 testList(0x1010)엔 tempList(0x2020) 주소값이 들어가 해당 주소값의 값을 바라보게 되었다.
즉 , main의 testList(ox1010) 엔 어떠한 영향도 미치지 못한거였다.
4번
<문제 해결>
아래와 같이 수정하니 정상적으로 동작하였다.
//Main 클래스 내부
List<String> testList = new ArrayList<>(); //1번
System.out.println("메서드 호출 전: "+ testList.size());
TestService testService = new TestService();
testService.convertList(testList);
System.out.println("메서드 호출 후: "+ testList.size()); //4번
//TestService 클래스
public class TestService {
public void convertList(List<String> testList){ //2번
testList.add("1");
testList.add("2");
System.out.println("메서드 내부: "+ testList.size()); //3
}
}
1번
2번
3번
4번
요약하면
- call by reference라고 지칭한 것이 결국은 "주소" 값을 참조하는 거였다.
- 메소드 호출 시 메서드 스택에 새롭게 할당되는구나.( 2번 그림참고)
<개선사항>
스택프레임이라는 걸 알아보고 설명 보충해야 한다.
'JAVA' 카테고리의 다른 글
오버로딩과 오버라이딩은 왜 어떤 바인딩인가? (0) | 2023.05.16 |
---|---|
클래스는 언제 로딩될까 (0) | 2023.04.10 |
싱글톤 지연로딩 (0) | 2023.04.07 |
String 리터럴 방식, new연산자 방식 / 문자열 비교 equals == (0) | 2023.02.08 |