본문 바로가기

백앤드 개발/Java & Spring

[Java] 참조형 변수

1. 참조형 변수

참조형변수는 객체의 주소를 저장

배열, 열거, 클래스, 인터페이스 타입 등이 있음

package lec01;

import java.util.Date;

public class DataType03 {
	public static void main(String[] args) {
	// new Date를 통해 객체를 생성하고 생성된 객체를 today라 명명
        Date today = new Date();
	}
}

 

 

2. 메모리 사용 영역

1) 힙 영역

객체, 참조형 변수는 힙 영역에 생성된다.

힙 영역은 JVM 스택 영역을 참조한다.

사용하지 않는 객체는 가비지콜렉터에 의해 자동으로 제거된다.

 

2) 스택 영역

기본 변수는 스택 영역에 생성된다.

변수는 선언된 블록 안에서만 존재하고 블록을 벗어나면 스택에서 제거된다.

 

 

3. == 연산 vs equals()

참조 타입의 변수 값은 결국 객체 주소이며 참조하는 객체가 같다면 == 연산의 결과는 True

두 대상의 값을 비교 하고 싶을 땐 equals() 메소드를 사용

 

 

4. NullPointerException

참조형 변수는 객체를 참조하지 않는 뜻으로 null 값을 가질 수 있다.

대표적으로 원시 타입의 int 자료형은 null 을 가질 수 없지만 참조 타입의 Integer 는 null을 가질 수 있다.

그러나 null 값을 가진 참조형 변수는 사용할 수 없으며 NullPointerException가 발생한다.

NullPointerException 은 가장 많이 발생하는 예외 중 하나이므로 알아두는 것이 좋다.

 

 

5. Call by Value vs Call by Reference

콜바이발류(Call by Value) / 콜바이래퍼런스(Call by Reference): 매개변수를 전달하는 방식을 설명하는 용어.

 

콜바이발류(Call by Value):

매개변수로 값을 전달할 때 '값' 만 전달하는 것을 의미

메소드에서 값이 변경되더라도 원본데이터 변경 X


콜바이래퍼런스 (Call by Reference):

매개변수로 값을 전달할 때 주소까지 전달하는 것을 의미

메소드에서 값이 변경되면 원본데이터 변경

 

자바는 콜바이발류(Call by Value)

자바는 모든 메소드 호출이 값에 의해 호출 된다.

메서드는 인수의 값을 복사하고 원본 변수에는 영향을 주지 않는다.

 

코드 예시

package hello.core;

public class CallByEx {
    public static void main(String[] args) {

        // 1. s2 에 s1 주소값 할당
        String s1 = new String("abc");
        String s2 = s1;

        System.out.println("s1 = " + s1); // s1 = abc
        System.out.println("s2 = " + s2); // s2 = abc
        System.out.println("s1 == s2? : " + (s1==s2)); // s1 == s2? : true


        // 2. s1 의 주소를 바꾼다면?: s2는 그대로 s1 만 새로운 주소를 할당받음
        s1 = new String("def");

        System.out.println("s1 = " + s1); // s1 = def
        System.out.println("s2 = " + s2); // s2 = abc
        System.out.println("s1 == s2? : " + (s1==s2)); // s1 == s2? : false


        // 3. s1에서 새로운 객체를 생성해 같은 값을 할당받으면? : 값은 같더라도 주소는 다름
        s1 = new String("abc");

        System.out.println("s1 = " + s1); // s1 = abc
        System.out.println("s2 = " + s2); // s2 = abc
        System.out.println("s1 == s2? : " + (s1==s2)); // s1 == s2? : false


        // 4-1. 문자열 객체 s4 에 s3 할당
        // 4-2. 메소드 foo() 를 통해 s3 에 새로운 객체 할당
        // 4-3. 자바는 콜바이발류이므로 메소드에 파라미터로 전달되어 값이 변경된다고 해서 원본 데이터를 변경하지는 못함
        // 이는 원시형, 참조형 상관없이 동일함
        String s3 = new String("abc");
        String s4 = s3;

        System.out.println("s3 = " + s3); // s3 = abc
        foo(s3); // s3 = ghi
        System.out.println("s3 = " + s3); // s3 = abc
        System.out.println("s3 == s4? : " + (s3==s4)); // s3 == s4? : true


        // 5-1. arr1 배열 선언 후 arr2에 할당
        // 5-2. arr1 의 첫번째 인자 변경시 arr2 의 인자도 변경됨 (같은 배열을 바라보기 때문)
        // 5-3. 메소드에서 배열의 인자를 변경하면 주소가 아닌 값이 변경되었기 때문에 원본 데이터도 변경
        // 5-4. 자바는 콜바이발류이나 배열의 값이 변경되는 것은 주소가 아닌 값이 변경되므로 원본도 변경
        // 5-5. 배열은 특수한 케이스로 두는게 좋은 것 같다.
        int [] arr1 = new int []{1,2,3};
        int [] arr2 = arr1;
        arr1[0] = 0;

        System.out.println("arr1 = arr2: ?" + (arr1==arr2)); // arr1 = arr2: ?true
        System.out.println(arr1[0]); // 0
        System.out.println(arr2[0]); // 0

        boo(arr2); // boo(arr1); 해도 결과는 같다.
        System.out.println("arr1 = arr2: ?" + (arr1==arr2)); // arr1 = arr2: ?true
        System.out.println(arr1[0]); // 1
        System.out.println(arr2[0]); // 1

    }

    static void foo(String s3) {
        s3 = new String("ghi");
        System.out.println("s3 = " + s3);
    }

    static void boo(int[] arr) {
        arr[0]=1;
    }
}

 

 

6. 기타 코멘트

- 콜바이네임이면 참조 타입인 String 과 배열 모두 메소드에서 값이 변경되더라도 원본은 그대로여야 되는게 아닌가?

- 그런데 왜 String은 변경 안되고 배열만 변경되는가