티스토리 뷰
메소드로 인자값을 넘기는 방법
메소드로 인자값을 넘기는 방법은 크게 두 가지가 있다.
Call by value
Call by reference
Call by value라는 것은 그대로 해석하면 '값에 의한 호출'이다. 메소드로 인자값을 넘길때 그 값을 복사하여 넘기는 형태이다. 따라서 이 방식으로 메소드 호출을 하면 메소드 내에서는 복사된 값으로 작업을 하기 때문에 원래의 값을 변경시키지 않는다.
Call by reference는 '참조에 의한 호출'이다. 인자값을 메소드로 넘겨 줄때 그 객체를 참조하는 주소를 넘겨주는 형태이다. 따라서 메소드 내에서도 원래의 값에 접근이 가능하다.
아래의 예시를 통하여 정확하게 알아보도록 하겠다.
1. 인자값으로 기본형을 사용했을 때
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
int num1 = 10, num2 = 20;
System.out.println("Before");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
swap(num1, num2);
System.out.println("After");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
// 자리 바꾸기 메소드
static void swap(int num1, int num2) {
int temp; // 임시 변수
// 자리 바꾸기
temp = num1;
num1 = num2;
num2 = temp;
}
}
결과
Before
num1 = 10
num2 = 20
After
num1 = 10
num2 = 20
위의 코드를 보면 swap메소드를 호출한 후에도 그대로 값이 유지된 것을 알 수 있다. 이 결과를 통해 알 수 있는 것은 메소드 안에서 아무리 두 수의 위치를 바꾼다고 해서 원본 데이터가 바뀌는 것이 아니라는 것이다. 메소드 내에서 두 수를 바꾸는 행위는 단지 복사된 값을 바꾸는 것에 불과하다. 따라서 자바는 기본형 데이터를 처리할 때 Call by value 형식으로 처리된다는 것을 알 수 있다. 그렇다면 아래에서 객체를 전달했을 때는 어떠한 결과를 도출하는지 알아보도록 하자.
2. 인자값으로 객체를 사용했을 때(wrapper 클래스 사용)
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer num1 = 10, num2 = 20;
System.out.println("Before");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
swap(num1, num2);
System.out.println("After");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
// 자리 바꾸기 메소드
static void swap(Integer num1, Integer num2) {
Integer temp; // 임시 변수
// 자리 바꾸기
temp = num1;
num1 = num2;
num2 = temp;
}
}
결과
Before
num1 = 10
num2 = 20
After
num1 = 10
num2 = 20
위의 기본형의 정수 변수를 wrapper클래스로 생성하여 객체로 다루어 보았다. 먼저 wrapper클래스에 대하여 약간의 설명을 하겠다.
코드에는 생략되어 있지만 Integer num1 = 10; 이라는 코드는 자바 내부적으로 new를 호출하여 힙 영역에 객체를 생성하고 스택영역의 지역 변수로 그 것을 참조한다. 풀어서 쓰자면 Integer num1 = new Integer(10); 과 같은 코드이니 오해하지 말자.
객체를 전달하여 두 수가 참조하는 값을 바꿔 보았다. 결과는 기본형을 사용했을 때와 같았다. 이것으로 보아 객체 또한 메소드로 값이 전달될 때 그 참조값은 Call by value의 형태로 전달된다는 것을 알 수 있다.
여기서 의문점이 생긴다. 자바는 기본적으로 포인터를 프로그래머에게 노출시키지 않는다. 따라서 C언어에서 처럼 주소를 인자값으로 넘기는 방식은 불가능하다. 그렇다면 자바는 위와 같은 방식으로 객체를 다룰 수 없다면 메소드 안에서 어떻게 다룰 수 있을까?
3. 인자값으로 객체를 사용했을 때(커스텀 클래스 사용)
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
NumberClass num1 = new NumberClass(10);
NumberClass num2 = new NumberClass(20);
System.out.println("Before");
System.out.println("num1 = " + num1.num);
System.out.println("num2 = " + num2.num);
swap(num1, num2);
System.out.println("After");
System.out.println("num1 = " + num1.num);
System.out.println("num2 = " + num2.num);
}
// 자리 바꾸기 메소드
static void swap(NumberClass num1, NumberClass num2) {
int temp; // 임시 변수
// 자리 바꾸기
temp = num1.num;
num1.num = num2.num;
num2.num = temp;
}
}
class NumberClass {
int num;
NumberClass (int num) {
this.num = num;
}
}
결과
Before
num1 = 10
num2 = 20
After
num1 = 20
num2 = 10
드디어 원하는 결과를 얻었다. 여기서 위와 다른 점은 객체의 참조값을 직접 바꾼것이 아니라 객체의 참조값을 통해서 그 객체의 멤버변수에 접근하여 그 값을 바꿨다는 것이다.
여기서 알 수 있는 중요한 자바의 특성은 객체를 메소드로 넘길 때 객체를 참조하는 지역변수의 실제 주소를 넘기는 것이 아니라 그 지역변수가 가리키고 있는 힙 영역의 객체를 가리키는 새로운 지역변수를 생성하여 그 것을 통하여 같은 객체를 가리키도록 하는 방식이라는 것이다. 아래의 그림을 보면 이해가 쉬울 것이다.
main메소드에서 참조하는 힙 영역의 데이터를 swap 함수에서도 새로운 지역변수를 통해서 참조하는 것을 볼 수 있다. 이제 자바에서는 명확하게 Call by value로 모든 메소드의 호출을 수행하는 것을 알 수 있을 것이다.
결론
생각해보니 이런 부분을 깊히 생각하지 않고 지금까지 공부를 해왔다. 'C언어에서 사용하던 포인터의 개념을 생각하지 않고 프로그래밍해서 편안하구나!'라는 생각을 막연하게 해왔지만 내부적으로 어떻게 동작하는지는 관심을 가지지 않았었다. 이는 추후에 자바를 더 깊이 공부하게 되고 내가 현업으로 갈 수록 이런 지식이 나에게는 더 큰 도움이 될 것이라는 생각이 들었다. 한번쯤 자신이 배우고 있는 언어를 면밀히 살펴보는 것이 바람직하다는 것을 크게 느낀다.
참고
http://trypsr.tistory.com/74
http://wonwoo.ml/index.php/post/1679
'Programming > Java' 카테고리의 다른 글
[Java]예외(Exception) (0) | 2018.07.11 |
---|---|
[Java]Class Design(Class & Abstract Class & Interface) (1) | 2018.07.11 |
[Java]상속(Inheritance) (0) | 2018.07.04 |
[Java]환경변수(JAVA_HOME & CLASSPATH) (5) | 2018.07.04 |
[Java]JVM 메모리 구조(JVM Memory Structure) (6) | 2018.06.26 |