오늘은 자바 언어의 특성 중 기본적인 이야기를 하려고 한다. 항상 기본이 중요함을 뼈저리게 느끼며, 학생때 잘 배웠어야 하는 아쉬움과 지금이라도 다시 정리할 수 있어 다행이라고 생각한다. 아래의 두 가지를 살펴보려고 한다.

  • Pass By Value (= Call By Value)

  • Pass By Reference (= Call By Reference)

프로그래밍 언어를 공부하는 사람들이라면 한번쯤 접하는 표현이다. 이 두가지에 대한 차이를 간략하게 설명하고 자바에서는 이 두가지가 어떻게 적용되는지 확인하고자 한다.

- Pass By Value (= Call By Value)
메소드의 파라미터는 다른 변수의 값에 의해 복사되며, 복사된 개체가 전달되는 것.

- Pass By Reference (= Call By Reference)
실제 매개변수의 Alias 또는 Reference 가 전달된다.

결론부터 말하면 자바는 항상 Pass By Value (= Call By Value) 이다. 왜 이렇게 되는지 알기위해선 JVM의 메모리 구조도 알면 좋다. 우선은 아래의 간단한 예제를 살펴보자.

(1) 풍선 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Balloon{
    private String color;
    
    public Balloon(){}
    
    public Balloon(String c){
        this.color = c;
    }
    
    public String getColor(){
        return color;
    }
    
    public void setColor(String color){
        this.color = color;
    }
}
cs


(2) 메인 메소드

1
2
3
4
public static void main(String[] args) {
    Balloon red = new Balloon("Red");
    Balloon blue = new Balloon("Blue");
}
cs


이렇게 하면 실질적으로 참조변수(레퍼런스) red 와 blue 는 스택영역에 저장되고 실제 Balloon 의 오브젝트는 힙영역에 저장된다. 여기서 아래의 메소드가 시행된다고 생각하자.


(3) swap() 메소드

1
2
3
4
5
6
// o1 <ㅡ> o2
public static void swap(Object o1, Object o2){
    Object temp = o1;
    o1 = o2;
    o2 = temp;
// 메소드 종료
cs


(4) 메인 메소드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
    Balloon red = new Balloon("Red");
    Balloon blue = new Balloon("Blue");
    
    System.out.println("swap() 이전 ㅡㅡㅡㅡ");
    System.out.println("red color = " + red.getColor());
    System.out.println("blue color = " + blue.getColor());
    
    /** SWAP() **/
    swap(red, blue);
    
    System.out.println("\nswap() 이후 ㅡㅡㅡㅡ");
    System.out.println("red color = " + red.getColor());
    System.out.println("blue color = " + blue.getColor());
}
cs


결과는 어떻게 될까?

1
2
3
4
5
6
7
swap() 이전 ㅡㅡㅡㅡ
red color = Red
blue color = Blue
 
swap() 이후 ㅡㅡㅡㅡ
red color = Red
blue color = Blue
cs


변하지 않는다.


그럼 아래의 메소드는 어떨까?


(5) foo() 메소드

1
2
3
4
5
private static void foo(Balloon balloon){
    balloon.setColor("Yellow");
    balloon = new Balloon("Green");
    balloon.setColor("Blue");
}
cs


(6) 메인 메소드

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
    Balloon red = new Balloon("Red");
    Balloon blue = new Balloon("Blue");
    
    System.out.println("\nfoo() 이전 ㅡㅡㅡㅡ");
    System.out.println("blue color = " + blue.getColor());
    
    /** FOO() **/
    foo(blue);
    
    System.out.println("\nfoo() 이후 ㅡㅡㅡㅡ");
    System.out.println("blue color = " + blue.getColor());
}
cs


결과는 어떻게 될까?

1
2
3
4
5
foo() 이전 ㅡㅡㅡㅡ
blue color = Blue
 
foo() 이후 ㅡㅡㅡㅡ
blue color = Yellow
cs


값이 변경되었다. (5) foo() 메소드 를 보자.

  • 2번줄은 인자로 받은 레퍼런스 balloon을 가지고 setColor 로 직접 오브젝트에 접근해서 변경하였다.
  • 3번줄은 foo() 메소드 내부의 local variable 로써 해당 메소드의 스택영역에서 새롭게 Balloon 객체를 만들어주었다. (new 연산자를 쓴 것으로 확인가능 : 메모리 할당)
  • 4번줄은 new 연산자를 통해 새롭게 할당된 객체에 대해서 setColor() 메소드를 구현하였다.
내가 참고한 링크에 따르면 아래와 같이 말한다. 

just remember that variables are references or pointers and it’s copy is passed to the methods, so java is always pass by value. 


변수는 레퍼런스 혹은 포인터이며, 메소드에는 레퍼런스 혹은 포인터의 복사본이 인자로 넘어가는 것이라고, 결국 자바는 pass by value 이다.


위의 내용이 이해가 가지 않는다면 아래의 동영상을 보면 좋으며 추가로 스택과 힙메모리에 대해서도 공부하는것이 좋다.


+) 추가내용 (예제 살피기)

(1) Person Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person{
    private String name = null;
    
    public Person(){}
    
    public Person(String name){
        this.name = name;
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
}
cs


(2) 메인 메소드 및 출력 메소드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
    Person[] personList = new Person[3];
    Arrays.fill(personList, new Person());
    
    display(personList);
    personList[0].setName("이순신");
    display(personList);
}
 
public static void display(Object[] objList){
    if(objList[0instanceof Person){
        for(int i = 0; i < objList.length; i++){
            System.out.println(i + "번째 사람의 이름 : " + ((Person)objList[i]).getName());
        }
    }
    
    System.out.println("ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ\n");
}
cs


Arrays.fill 메소드는 해당 배열리스트에 모든 값을 한번에 초기화시켜준다. new 연산자 키워드를 사용하여 3개의 length 를 가진 배열에 메모리 할당을 해주었다. 5번줄과 7번줄에 메소드 호출에 대한 내용을 유추하자.


1
2
3
4
5
6
7
8
9
0번째 사람의 이름 : null
1번째 사람의 이름 : null
2번째 사람의 이름 : null
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
 
0번째 사람의 이름 : 이순신
1번째 사람의 이름 : 이순신
2번째 사람의 이름 : 이순신
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
cs


결과는 위와 같다. 왜 이런지 알고 있는지 곰곰히 생각해보자. 다른 예제도 살펴보자.


1
2
3
4
5
6
7
8
public static void main(String[] args) {
 
    /** 중간 생략 **/
 
    personList[0= new Person("신사임당");
    personList[1= new Person("율곡이이");
    display(personList);
}
cs


어떻게 출력될 것인가? 생각해보면 답은 뻔하다.


1
2
3
4
0번째 사람의 이름 : 신사임당
1번째 사람의 이름 : 율곡이이
2번째 사람의 이름 : 이순신
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
cs


new 연산자로 인해 힙영역에 메모리를 할당받지만, 위의 두 예시는 할당이라는 개념이 적용되지만 한편으로 배열 레퍼런스에 대한 각각의 요소들의 참조 값이 하나라는 것이다. 또 다른 예제도 살펴보자.


(1) 메인 메소드 : (HashMap & ArrayList)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SuppressWarnings("unchecked")
public static void main(String[] args) {
    
    HashMap<Integer, Object> map = new HashMap<Integer, Object>();
    ArrayList<Integer> list = new ArrayList<Integer>();
    
    for(int i = 1; i <= 3; i++){
        list.add(i);
        map.put(i, list);
        list.clear();
    }
    
    ArrayList<Integer> list1 = (ArrayList<Integer>)map.get(1);
    ArrayList<Integer> list2 = (ArrayList<Integer>)map.get(2);
    ArrayList<Integer> list3 = (ArrayList<Integer>)map.get(3);
    
    System.out.println(list1.get(0));
    System.out.println(list2.get(0));
    System.out.println(list3.get(0));
}
cs


결과가 어떻게 될까? 아래의 내용을 순차저으로 실행해보자.

  • 10번째줄의 list.clear() 메소드를 삭제하고 출력해보자
  • 7번째줄과 8번째줄 사이에 list = new ArrayList<Integer>();
  • 해쉬코드를 각각 출력해보자
    System.out.println(list1.hashCode());
    System.out.println(list2.hashCode());
    System.out.println(list3.hashCode());
이와 더불어서 공부하고 싶다면, Object 함수의 equals() 와 hashcode()를 공부하는 것이 좋을듯하다. 매번 느끼지만 기초가 중요하다.


'jvm lang' 카테고리의 다른 글

20181003 Java Process Runtime exec - deadlock/hang  (0) 2018.10.03
20170714 엑셀 poi library  (0) 2018.07.14
20180504 TDD  (0) 2018.05.04
20180503 콜백 메소드에 대해서  (0) 2018.05.03
20180412 자바 CMD로 실행  (0) 2018.04.12
Posted by doubler
,