코딩 공부/Java

[Java] 13_값 복사, 참조 복사

chocbi 2020. 5. 8. 16:17

값복사, 참조복사

기본 자료형 변수간의 대입과 배열간의 대입 혹은 파라미터로서의 사용은 서로 차이가 있다.

기본 자료형: char, boolean, byte, short, int, long, float, double

#01. 값 복사

기본 자료형 변수를 서로 대입하거나 파라미터로 사용할 경우의 현상.

단순 복사가 발생하기 때문에 복사 후 원본이 변경되더라도 복사본에는 영향이 없다. (반대의 경우도 마찬가지)

ValueCopy.java

public class ValueCopy{
    public static void main(String[] args) {
        int a = 10;
        int b = a;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("---------------");

        a += 10;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("---------------");

        b -= 10;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("---------------");
    }
}
출력결과
a = 10
b = 10
---------------
a = 20
b = 10
---------------
a = 20
b = 0
---------------

기본 자료형 변수를 파라미터로 사용하는 경우

메서드가 서로다르면 변수의 유효성 범위(스코프)가 서로 다르므로 변수 이름이 동일하더라도 서로 다른 값으로 구분된다.

아래 두 개의 메서드 중, foo()에서 정의한 파라미터 a와 변수 bbar()메서드가 정의하는 파라미터 a나 변수 b와는 단지 이름만 같을 뿐 서로 다른 값이다.

실제 생성되는 메모리의 위치가 다르기 때문에 구별됩니다.

public static void foo(int a){
    int b = a + 10;
}

public static void bar(int a){
    int b = a - 10;
}

ValueParam

public class ValueParam{
    public static void main(String[] args) {
        int x = 1;
        System.out.println("before x: " + x);

        // 기본자료형을 파라미터로 전달할 때 값이 복사되므로
        // "파라미터 = x"의 의미를 갖는다.
        foo(x);

        // foo를 호출할 때 x값이 복사되어 전달되므로
        // foo 메서드 안에서 값을 수정하더라도 복사본에 대한 변경이다.
        // 그러므로 원본은 변하지 않는다.
        System.out.println("after x: " + x);
    }

    public static void foo(int x) {
        // 이 안에서의 x는 블록이 서로 다르므로 main의 x와 구별된다.
        // 그러므로 전달되는 변수의 이름과 파라미터의 이름은 아무런 연과이 없다.
        x += 100;
        System.out.println("in foo method --> " + x);
    }
}
출력결과
before x: 1
in foo method --> 101
after x: 1

#02. 참조 복사

배열을 서로 대입하거나 파라미터로 사용할 경의 현상.

1) 배열간의 대입

배열간의 대입은 배열의 변수 이름에 원소들을 참조시키기 때문에 복사봄을 수정할 경우 원본도 하몌 수정된다. 반대의 경우도 마찬가지)

ArrayCopy.java

배열간의 복사 실험

public class ArrayCopy {
    public static void main(String[] args) {
        int[] origin = {1, 2};
        int[] copy = origin;

        System.out.println("origin[0]="+ origin[0]);
        System.out.println("origin[1]="+ origin[1]);
        System.out.println("copy[0]="+ copy[0]);
        System.out.println("copy[1]="+ copy[1]);
        System.out.println("-------------------------");

        copy[0] += 100;
        copy[1] += 200;

        System.out.println("origin[0]="+ origin[0]);
        System.out.println("origin[1]="+ origin[1]);
        System.out.println("copy[0]="+ copy[0]);
        System.out.println("copy[1]="+ copy[1]);
        System.out.println("-------------------------");


    }
}
출력결과
origin[0]=1
origin[1]=2
copy[0]=1
copy[1]=2
-------------------------
origin[0]=101
origin[1]=202
copy[0]=101
copy[1]=202
-------------------------
배열의 참조 그림 설명

int[] a;

a라는 변수를 생성함.

이 변수에는 int형 배열의 "위치"만 저장할 수 있다. --> int[]

모든 변수의 "위치"는 정수형이므로 위치를 저장하기 위한 변수는 무조건 4byte로 생성된다.

a[0] = 1;
a[1] = 2;
---------a에 저장된 위치를 찾아가서
그 중 0번째 영역에 1을 대입
1번째 영역에 2을 대입

    할당할 때 int[2]로 했으므로
    0번째 영역은 8칸 중 앞 4칸
    1번째 영역은 그 다음 4칸.

boolean[] k;
double[] z;
k와 z자체는 4byte

3) 배열을 파라미터로 받는 메서드

마찬가지로 참조 형태로 전달된다.

ArrayParam

public class ArrayParam {
    public static void main(String[] args) {
        int[] origin = {1, 2};
        System.out.println("origin[0]=" + origin[0]);
        System.out.println("origin[1]=" + origin[1]);
        System.out.println("-----------------------");

        // 배열을 파라미터로 전달.
        foo(origin);
        System.out.println("origin[0]=" + origin[0]);
        System.out.println("origin[1]=" + origin[1]);
    }

    public static void foo(int[] copy) {
        // 배열 파라미터 역시 참조 형태로 전달되므로,
        // 이 메서드 안에서 배열의 원소를 변경하면
        // main에 있는 원본까지 함께 변경된다.
        for (int i=0; i<copy.length; i++) {
            copy[i] += 100;
        }
        System.out.println("copy[0]=" + copy[0]);
        System.out.println("copy[1]=" + copy[1]);
        System.out.println("---------------------");
    }
}
출력결과
origin[0]=1
origin[1]=2
-----------------------
copy[0]=101
copy[1]=102
---------------------
origin[0]=101
origin[1]=102

ArrayCopy2

public class ArrayCopy2 {
    public static void main(String[] args) {
        int[] origin = {1, 2};
        // 원본과 동일한 사이즈의 배열 생성
        int[] copy = new int[origin.length];


        // 각각의 원소를 개별적으로 복사해야 한다.
        copy[0] = origin[0];
        copy[1] = origin[1];    
        // 길어질 경우 for문을 이용하여 값을 넣는다
        System.out.println("origin[0]="+ origin[0]);
        System.out.println("origin[1]="+ origin[1]);
        System.out.println("copy[0]="+ copy[0]);
        System.out.println("copy[1]="+ copy[1]);
        System.out.println("-------------------------");


        // 복사본을 수정하더라도 원본의 변화가 없다.
        copy[0] += 100;
        copy[1] += 200;

        System.out.println("origin[0]="+ origin[0]);
        System.out.println("origin[1]="+ origin[1]);
        System.out.println("copy[0]="+ copy[0]);
        System.out.println("copy[1]="+ copy[1]);
        System.out.println("-------------------------");
    }
}
출력결과
origin[0]=1
origin[1]=2
copy[0]=1
copy[1]=2
-------------------------
origin[0]=1
origin[1]=2
copy[0]=101
copy[1]=202

ArrayCopy3

public class ArrayCopy3{
    public static void main(String[] args) {
        int[] origin = {1, 2, 3, 4, 5};
        int[] copy = new int[origin.length];
        // origin의 0번째 부터 copy의 1번째에 3칸을 복사
        System.arraycopy(origin, 0, copy, 1, 3);
        for ( int i=0; i<origin.length; i++) {
            System.out.printf("origin[%d]=%d \t copy[%d]=%d \n", i, origin[i], i, copy[i]);            
        }
        System.out.println("----------------------------");
        // 주로 다음과 같이 사용됨.
        System.arraycopy(origin, 0, copy, 0, origin.length);
        for ( int i=0; i<origin.length; i++) {
            System.out.printf("origin[%d]=%d \t copy[%d]=%d \n", i, origin[i], i, copy[i]);            
        }
    }
}
출력결과
origin[0]=1      copy[0]=0 
origin[1]=2      copy[1]=1 
origin[2]=3      copy[2]=2 
origin[3]=4      copy[3]=3 
origin[4]=5      copy[4]=0 
-----------------------------
origin[0]=1      copy[0]=1 
origin[1]=2      copy[1]=2 
origin[2]=3      copy[2]=3 
origin[3]=4      copy[3]=4 
origin[4]=5      copy[4]=5 

참고

다른 설명에서는 다음과 같이 설명하기도 합니다.

참조복사 (Call by Reference) 값 복사 (Call by Value)
얕은복사 (Shallow Copy) 깊은복사 (Deep copy)