Java >> Java tutorial >  >> Java

Shallow Copy vs Deep Copy i Java Object Cloning

I dette indlæg vil vi se forskellene mellem overfladisk kopi og dyb kopi i Java-objektkloning. For at forstå forskellene er det meget vigtigt at forstå konceptet med overfladisk kopi og dyb kopi, mens du kloner et objekt, så først vil vi forsøge at forstå konceptet ved hjælp af eksempler på både overfladisk kopi og dyb kopi.

For at vide mere om objektkloning i Java henvises til dette indlæg - Objektkloning i Java Brug af clone() metode

Shallow copy i Java-objektkloning

Når et objekt klones ved hjælp af clone() metoden sker der en lidt klog kopi, hvor hvert felts værdi i det originale objekt kopieres til det klonede objekts tilsvarende felt. Denne måde at objektklone på i Java ved hjælp af standard clone() metoden skaber en Shallow copy .

Dette er kendt som overfladisk kopi, fordi denne proces med at skabe en nøjagtig kopi fungerer fint for primitive værdier, men har en delt tilstand mellem det originale objekt og det klonede objekt når det originale objekt har reference til et andet objekt.

I så fald kopieres referencen, som den er i det klonede objekt, og begge objekter deler denne objektreference.

For eksempel hvis der er en klasse MyClass som har et andet objekt objA som felt.

class MyClass implements Cloneable{
  private ClassA objA;
  ...
  ...
}

Hvis der er et objekt myObj af klassen MyClass, og det er klonet, deles objA-referencen mellem myObj og klonet kopi af myObj.

Shallow copy i Java-eksempel

I eksemplet er der to klasser One og Two. I klasse 2 er der en reference til et objekt i klasse 1.

class One implements Cloneable{
  int i;
  String str;
  One(int i, String str){
    this.i = i;
    this.str = str;
  }
  // overriding clone method
  public Object clone() throws CloneNotSupportedException{
    return super.clone();
  }
  public int getI() {
    return i;
  }
  public void setI(int i) {
    this.i = i;
  }
  public String getStr() {
    return str;
  }
  public void setStr(String str) {
    this.str = str;
  }
}

class Two implements Cloneable{
  int j;
  // Object reference
  One obj;
  Two(int j, One obj){
    this.j = j;
    this.obj = obj;
  }
  public Object clone() throws CloneNotSupportedException{
    Two objCloned =  (Two) super.clone();
    return objCloned;
  }
  public int getJ() {
    return j;
  }
  public void setJ(int j) {
    this.j = j;
  }
  public One getObj() {
    return obj;
  }
  public void setObj(One obj) {
    this.obj = obj;
  }
}

public class CloningDemo {
  public static void main(String[] args) {
    One one = new One(10, "Clone Test");
    Two two = new Two(5, one);
    try {
      Two objCopy = (Two) two.clone();
      System.out.println("Original object- " +  two.getJ() + " " + two.getObj().str);
      System.out.println("Cloned object- " +  + objCopy.getJ() + " " + objCopy.getObj().str);
      // modifying field in the referenced object
      objCopy.getObj().setStr("New Value");
      // Modifying primtive value
      objCopy.setJ(25);
      System.out.println("---After changing value---");
      System.out.println("Original object- " +  two.getJ() + " " + two.getObj().str);
      System.out.println("Cloned object- " +  + objCopy.getJ() + " " + objCopy.getObj().str);
    } catch (CloneNotSupportedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 	
  }
}
Output
Original object- 5 Clone Test
Cloned object- 5 Clone Test
---After changing value---
Original object- 5 New Value
Cloned object- 25 New Value

Som du kan se på outputtet, når feltet i det refererede objekt ændres i kopien, afspejles denne ændring også i det originale objekt. Ændring i primitiv typefelt udført i kopien (værdien af ​​j ændret til 25) afspejles ikke i det originale objekt.

Dyb kopi i Java-objektkloning

For at undgå en sådan deling af tilstand mellem det originale objekt og det klonede objekt, hvor mutationen af ​​det refererede objekt afspejles i begge objekter, kræves en dyb kopi. I tilfælde af dyb kopiering under kloning af et objekt i Java oprettes selv de refererede objekter separat, så begge objekter har deres egne uafhængige referencer.

For eksempel hvis der er en klasse MyClass som har et andet objekt objA som felt.

class MyClass implements Cloneable{
  private ClassA objA;
  ...
  ...
}

Hvis der er et objekt myObj af klassen MyClass og der oprettes en dyb kopi, så er selv objA's separate og uafhængige kopi der for myObj og klonet kopi af myObj.

Dyb kopi i Java-eksempel

Når du laver en dyb kopi, er der to scenarier, som begge diskuteres her med eksempler-

1- Hvis klassen, hvis objekt der henvises til, implementerer en kloningsbar grænseflade, kan du også eksplicit kalde clone()-metoden for det objekt.

I eksemplet kan du se, at klonmetoden er tilsidesat i klasse Two, og clone()-metoden for det refererede objekt kaldes også eksplicit for også at skabe en særskilt kopi af det objekt.

class One implements Cloneable{
  int i;
  String str;
  One(int i, String str){
    this.i = i;
    this.str = str;
  }
  // overriding clone method
  public Object clone() throws CloneNotSupportedException{
    return super.clone();
  }
  public int getI() {
    return i;
  }
  public void setI(int i) {
    this.i = i;
  }
  public String getStr() {
    return str;
  }
  public void setStr(String str) {
    this.str = str;
  }
}

class Two implements Cloneable{
  int j;
  // Object reference
  One obj;
  Two(int j, One obj){
    this.j = j;
    this.obj = obj;
  }
  public Object clone() throws CloneNotSupportedException{
    Two objCloned =  (Two) super.clone();
    // Explicitly calling clone method for
    // object of Class One
    objCloned.obj = (One) obj.clone();
    return objCloned;
  }
  public int getJ() {
    return j;
  }
  public void setJ(int j) {
    this.j = j;
  }
  public One getObj() {
    return obj;
  }
  public void setObj(One obj) {
    this.obj = obj;
  }
}

public class CloningDemo {

  public static void main(String[] args) {
    One one = new One(10, "Clone Test");
    Two two = new Two(5, one);
    try {
      Two objCopy = (Two) two.clone();
      System.out.println("Original object- " +  two.getJ() + " " + two.getObj().str);
      System.out.println("Cloned object- " +  + objCopy.getJ() + " " + objCopy.getObj().str);
      // modifying field in the referenced object
      objCopy.getObj().setStr("New Value");
      // Modifying primtive value
      objCopy.setJ(25);
      System.out.println("---After changing value---");
      System.out.println("Original object- " +  two.getJ() + " " + two.getObj().str);
      System.out.println("Cloned object- " +  + objCopy.getJ() + " " + objCopy.getObj().str);
    } catch (CloneNotSupportedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 	
  }
}
Output
Original object- 5 Clone Test
Cloned object- 5 Clone Test
---After changing value---
Original object- 5 Clone Test
Cloned object- 25 New Value

Som du kan se, når feltet i det refererede objekt ændres i kopien, afspejles denne ændring ikke i det originale objekt, når en dyb kopi er udført.

2- Hvis en klasse, hvis objekt der henvises til, ikke implementerer Cloneable-grænsefladen, kastes CloneNotSupportedException, hvis clone()-metoden kaldes på den. I dette tilfælde skal du oprette et nyt objekt og tildele værdier til felterne eksplicit i clone()-metoden.

class One{
  int i;
  String str;
  One(int i, String str){
    this.i = i;
    this.str = str;
  }
  // overriding clone method
  public Object clone() throws CloneNotSupportedException{
    return super.clone();
  }
  public int getI() {
    return i;
  }
  public void setI(int i) {
    this.i = i;
  }
  public String getStr() {
    return str;
  }
  public void setStr(String str) {
    this.str = str;
  }
}

class Two implements Cloneable{
  int j;
  // Object reference
  One obj;
  Two(int j, One obj){
    this.j = j;
    this.obj = obj;
  }
  public Object clone() throws CloneNotSupportedException{
    Two objCloned =  (Two) super.clone();
    // Creating new object
    One newObj = new One(12, "New Value");
    // assigning new oject to the clone
    objCloned.setObj(newObj);
    return objCloned;
  }
  public int getJ() {
    return j;
  }
  public void setJ(int j) {
    this.j = j;
  }
  public One getObj() {
    return obj;
  }
  public void setObj(One obj) {
    this.obj = obj;
  }
}

public class CloningDemo {

  public static void main(String[] args) {
    One one = new One(10, "Clone Test");
    Two two = new Two(5, one);
    try {
      Two objCopy = (Two) two.clone();
      System.out.println("Original object- " +  two.getJ() + " " + two.getObj().getI() + " " + two.getObj().getStr());
      System.out.println("Cloned object- " +  + objCopy.getJ() + " "  + objCopy.getObj().getI() + " " + objCopy.getObj().getStr());
    } catch (CloneNotSupportedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 	
  }
}
Output
Original object- 5 10 Clone Test
Cloned object- 5 12 New Value

Shallow copy vs Deep Copy i Java

  1. Grunde kopier er billige og nemme at implementere. Dybe kopier er dyre, da hvert refereret objekt skal oprettes separat. Det er også mere komplekst, da objekttræet kan være meget langt.
  2. I tilfælde af overfladisk kopi oprettes der dog en særskilt kopi af et objekt med dets eget sæt felter, men objektreferencer deles. I tilfælde af dyb kopi oprettes der separate kopier, selv for de refererede objekter.
  3. Som standard opretter objektklassens klonmetode en overfladisk kopi. For at oprette en dyb kopi skal du tilsidesætte klonmetoden og også kalde klonmetoden på de refererede objekter.
Relaterede indlæg
  • Java Pass by Value eller Pass by Reference
  • Java switch case-erklæring med eksempler
  • Konstruktørkæde i Java
  • Initialiseringsblok i Java
  • Java var Type (Local Variable Type Inference)
  • Array i Java
  • JShell i Java
  • Java String valueOf()-metode med eksempler

Det er alt for emnet Shallow Copy vs Deep Copy i Java-objektkloning . Hvis der mangler noget, eller du har noget at dele om emnet, så skriv en kommentar.


Java tag