오늘은 자바 언어의 특성 중 기본적인 이야기를 하려고 한다. 항상 기본이 중요함을 뼈저리게 느끼며, 학생때 잘 배웠어야 하는 아쉬움과 지금이라도 다시 정리할 수 있어 다행이라고 생각한다. 아래의 두 가지를 살펴보려고 한다.
Pass By Value (= Call By Value)
Pass By Reference (= Call By Reference)
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() 메소드를 구현하였다.
변수는 레퍼런스 혹은 포인터이며, 메소드에는 레퍼런스 혹은 포인터의 복사본이 인자로 넘어가는 것이라고, 결국 자바는 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[0] instanceof 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());
'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 |