Programing

Java에서 개체 복제

crosscheck 2020. 10. 31. 09:23
반응형

Java에서 개체 복제


Java에서 변수를 수정할 때 기반 변수가 변경되지 않는다는 것을 배웠습니다.

int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected

나는 물체에 대해 비슷한 것을 가정했습니다. 이 수업을 고려하십시오.

public class SomeObject {
    public String text;

    public SomeObject(String text) {
        this.setText(text);
    }

    public String getText() {
        return text;
    }   

    public void setText(String text) {
        this.text = text;
    }
}

이 코드를 시도한 후 혼란스러워졌습니다.

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected

개체를 변경하면 다른 개체에 영향을주는 이유를 설명해주세요. 가변 텍스트의 값이 두 개체 모두에 대해 메모리의 동일한 위치에 저장된다는 것을 이해합니다.

변수 값이 독립적이지만 객체에 대해 상관 된 이유는 무엇입니까?

또한 간단한 할당이 작업을 수행하지 않는 경우 SomeObject를 복제하는 방법은 무엇입니까?


Java의 모든 변수는 참조 입니다. 그래서 당신이 할 때

SomeClass s2 = s1;

당신은 단지 가리키는 s2것과 같은 물체를 s1가리 킵니다. 실제로 참조 s1 (의 인스턴스를 가리키는 SomeClass) 값을 s2에 할당합니다 . 을 수정 s1하면 s2이 (가 ) 동일한 객체를 가리 키기 때문에 수정됩니다 .

예외, 기본 유형 : int, double, float, boolean, char, byte, short, long. 값별로 저장됩니다. 따라서을 사용할 때 =값만 할당 할 수 있지만 참조가 아니기 때문에 동일한 객체를 가리킬 수 없습니다. 이것은

int b = a;

전용의 설정 값 b의 값을 a. 당신이 변경하는 경우 a, b변경되지 않습니다.

하루가 끝나면 모든 것이 값에 따라 할당되며, 객체의 값이 아니라 참조의 값일뿐입니다 (위에서 언급 한 기본 유형은 예외).

따라서 귀하의 경우의 사본을 만들고 싶다면 s1다음과 같이 할 수 있습니다.

SomeClass s1 = new SomeClass("first");
SomeClass s2 = new SomeClass(s1.getText());

또는 SomeClass인스턴스를 인수로 사용하여 자체 인스턴스에 복사하는 복사 생성자를 추가 할 수 있습니다 .

class SomeClass {
  private String text;
  // all your fields and methods go here

  public SomeClass(SomeClass copyInstance) {
    this.text = new String(copyInstance.text);
  }
}

이것으로 아주 쉽게 객체를 복사 할 수 있습니다 :

SomeClass s2 = new SomeClass(s1);

@brimborium의 대답은 매우 좋지만 (그에게 +1), 몇 가지 수치를 사용하여 더 자세히 설명하고 싶습니다. 먼저 기본 할당을 보겠습니다.

int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected
int a = new Integer(5);

1- First 문은 값 5의 Integer 객체를 생성합니다. 그런 다음 변수에 할당 할 a때 Integer 객체는 상자가 풀리고 a기본 형식으로 저장됩니다 .

Integer 객체를 생성 한 후 할당 전 :

여기에 이미지 설명 입력

할당 후 :

여기에 이미지 설명 입력

int b = a;

2- 이것은 값을 읽은 a다음에 저장합니다 b.

(이제 Integer 객체는 가비지 콜렉션에 적합하지만 아직이 시점에서 가비지 콜렉션이 반드시 필요한 것은 아닙니다.)

여기에 이미지 설명 입력

b = b + b;

3- 이것은 b두 번의 값을 읽고 함께 더한 다음 새 값을에 배치합니다 b.

여기에 이미지 설명 입력


반면에 :

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected
SomeObject s1 = new SomeObject("first");

1- SomeObject클래스 의 새 인스턴스를 만들고 참조에 할당합니다 s1.

여기에 이미지 설명 입력

SomeObject s2 = s1;

2- 이것은 가리키는 s2객체에 대한 참조 포인트를 만듭니다 s1.

여기에 이미지 설명 입력

s2.setText("second");

3- 참조에 setter를 사용하면 참조가 가리키는 개체가 수정됩니다.

여기에 이미지 설명 입력

System.out.println(s1.getText());
System.out.println(s2.getText());

4- 두 참조가 동일한 객체를 second참조 s1하고 있으므로 둘 다 인쇄해야 s2합니다 (이전 그림 참조).


당신이 이것을 할 때

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;

동일한 객체에 대한 2 개의 참조가 있습니다. 즉, 어떤 참조 개체를 사용하든 두 번째 참조를 사용할 때 변경 한 내용이 표시됩니다.

다음과 같이 생각해보십시오. 방에 하나의 텔레비전이 있지만 두 개의 리모콘이 있습니다. 어떤 리모콘을 사용하든 상관없이 동일한 기본 개체 (텔레비전)를 변경하게됩니다.


당신이 쓸 때 :

SomeObject s1 = new SomeObject("first");

s1이 아닙니다 SomeObject. 그것은이다 참조 받는 SomeObject객체입니다.

따라서 s2를 s1에 할당하면 단순히 참조 / 핸들을 할당하는 것이며 기본 개체는 동일합니다. 이것은 Java에서 중요합니다. 모든 것이 값에 의한 전달이지만, 절대로 객체를 전달하지 않고 객체에 대한 참조 만 전달합니다.

따라서을 할당 s1 = s2한 다음 s2객체를 변경하는 메서드를 호출 하면 기본 객체가 변경되고를 통해 객체를 참조 할 때 표시됩니다 s1.

이것은 객체의 불변성대한 하나의 주장입니다 . 개체를 불변으로 만들면 개체가 변경되지 않으므로 더 예측 가능한 방식으로 작동합니다. 개체를 복사하려는 경우 가장 쉽고 / 가장 실용적인 방법은 copy()단순히 새 버전을 만들고 필드 위에 복사 하는 메서드를 작성하는 것입니다. 직렬화 / 반사 등을 사용하여 영리한 사본을 만들 수 있지만 분명히 더 복잡합니다.


에서 톱 텐 오류 자바 프로그래머 확인 :

6-값 전달 및 참조 전달에 대한 혼란

코드를 볼 때 참조에 의한 전달을 확신 할 수 있지만 실제로 값에 의해 전달되는 것을 발견 할 수 있기 때문에 이것은 진단하기에 실망스러운 문제가 될 수 있습니다. Java는 둘 다 사용하므로 값으로 전달할 때와 참조로 전달할 때를 이해해야합니다.

char, int, float 또는 double과 같은 기본 데이터 유형을 함수에 전달하면 값으로 전달됩니다. 즉, 데이터 유형의 복사본이 복제되어 함수에 전달됩니다. 함수가 해당 값을 수정하도록 선택하면 복사본 만 수정합니다. 함수가 종료되고 제어가 반환 함수로 반환되면 "실제"변수는 변경되지 않고 변경 사항이 저장되지 않습니다. 기본 데이터 유형을 수정해야하는 경우 함수의 반환 값으로 만들거나 객체 내부에 래핑합니다.

int은 (는) 기본 유형 이기 때문에은 ( 는) 값별 int b = a;복사입니다. 즉 a, b은 (는) 두 개의 다른 객체이지만 값은 같습니다.

SomeObject s2 = s1;make s1s2동일한 객체의 두 참조를 참조하므로 하나를 수정하면 다른 하나도 수정됩니다.

좋은 해결책은 다음과 같은 다른 생성자를 구현하는 것입니다.

public class SomeObject{

    public SomeObject(SomeObject someObject) {
        setText(someObject.getText());
    }

    // your code

}

그런 다음 다음과 같이 사용하십시오.

SomeObject s2 = new SomeObject(s1);

코드 에서 동일한 객체 s1이며 (을 사용하여 하나의 객체 만 만들었습니다 ) 다음 줄에서 동일한 객체를 가리킬 수 있습니다. 따라서 변경할 때 및을 통해 값을 참조하면 둘 다 변경 됩니다 .s2news2texts1s2

+Integers 연산자는 객체를 생성하며 기존 객체를 변경하지 않습니다 (따라서 5 + 5를 추가해도 5에 새 값 10이 주어지지 않습니다 ...).


That's because the JVM stores a pointer to s1. When you call s2 = s1, you are basically saying that the s2 pointer (i.e. memory address) has the same value as the one for s1. Since they both point to the same place in memory, they represent exactly the same thing.

The = operator assigns pointer values. It does not copy the object.

Cloning objects is a complicated matter in itself. Every object has a clone() method which you might be tempted to use but it does a shallow copy (which basically means that it makes a copy of the top-level object but any object that are contained within it are not cloned). If you want to play around with object copies, definitely read Joshua Bloch's Effective Java.


Let's start with your second example:

This first statement assigns a new object to s1

SomeObject s1 = new SomeObject("first");

When you do the assignment in the second statement (SomeObject s2 = s1), you are telling s2 to point at the same object s1 is currently pointing at, so you have two references to the same object.

Note that you have not duplicated the SomeObject, rather two variables are just pointing to the same object. So if you are modify s1 or s2, you are actually modifying the same object (note if you did something like s2 = new SomeObject("second") they would now be pointing at different objects).

In your first example, a and b are primitive values, so modifying one will not affect the other.

Under the hood of Java, all objects do work using pass by value. For objects, you are passing the "value" you are passing is the object's location in memory (so it appears to have a similar effect of pass by reference). Primitives behave differently and just pass a copy of the value.


The answers above explain the behaviour you are seeing.

In answer to "Also, how to duplicate SomeObject, if simple assignment does not do the job?" - try searching for cloneable (it's a java interface that provides one way to copy objects) and 'copy constructors' (an alternative and, arguably, better approach)


Object assignment to a reference does not clone your object. References are like pointers. They point to an object and when operations are invoked it is done on the object pointed by the pointer. In your example, s1 and s2 are pointing to the same object and setters alter the state of the same object and the changes are visible across references.


Change you class in order to create new reference instead of using the same one:

public class SomeObject{

    public String text;

    public SomeObject(String text){
        this.setText(text);
    }

    public String getText(){
        return text;
    }   

    public void setText(String text){
        this.text = new String(text);
    }
}

You can use something like this (I don't pretend to have ideal solution):

public class SomeObject{

    private String text;

    public SomeObject(String text){
        this.text = text;
    }

    public SomeObject(SomeObject object) {
        this.text = new String(object.getText());
    }

    public String getText(){
        return text;
    }   

    public void setText(String text){
        this.text = text;
    }
}

Usage:

SomeObject s1 = new SomeObject("first");
SomeObject s2 = new SomeObject(s1);
s2.setText("second");
System.out.println(s1.getText()); // first
System.out.println(s2.getText()); // second

int a = new Integer(5) 

In the case above a new Integer is created. Here Integer is a non-primitive type and the value within it is converted (to an int) and assigned to an int 'a'.

SomeObject s1 = new SomeObject("first");  
SomeObject s2 = s1;

In this case both s1 and s2 are reference types. They are not created to contain a value like primitive types, rather they contain the reference of some object. For the sake of understanding we can think reference as a link which indicates where I can found the object being referenced.

Here the reference s1 tell us where we can found the value of "first" (which is actually stored in the memory of computer in an instance of SomeObject).In otherwords s1 is the address of the object of class "SomeObject". By the following assignment -

SomeObject s2 = s1;

we are just copying the value stored in s1 to s2 and we now know s1 contains the address of the string "first". After this assigenment both println() produces the same output because both s1 and s2 refrencing the same object.

Along with copy constructor you can copy object with clone() method if you are a java user. Clone can be used in the following manner -

SomeObject s3 = s1.clone(); 

For more information on clone() this is a useful link http://en.wikipedia.org/wiki/Clone_%28Java_method%29


That is because s1 and s2 work only as references to your objects. When assigning s2 = s1 you only assign the reference, meaning that both will point to the same object in memory (the object that has the current text "first").

When you do a setter now, either on s1 or s2, both will modify the same object.


When you assign and object to a variable you really are assigning the reference to that object. So both variables s1 and s2 are referring the same object.


객체가 참조에 의해 참조 되었기 때문입니다. 따라서 s2 = s1을 쓰면 참조 만 복사됩니다. C에서와 마찬가지로 메모리 주소로만 처리합니다. 그리고 메모리의 주소를 복사하고이 주소 뒤에있는 값을 변경하면 두 포인터 (참조)가 메모리의이 지점에서 하나의 값을 변경합니다.


두 번째 줄 ( SomeObject s2 = s1;)은 단순히 두 번째 변수를 첫 번째 변수에 할당합니다. 이로 인해 두 번째 변수가 첫 번째와 동일한 개체 인스턴스를 가리 킵니다.

참고 URL : https://stackoverflow.com/questions/12072727/duplicating-objects-in-java

반응형