原型设计模式 关键字Cloneable

Java中要想自定义类的对象可以被复制,自定义类就必须实现Cloneable中的clone()方法,如下:

https://github.com/yzmaodeng/java-keypointknowledge/commit/45e9136a51912dfd62b9d89ff95cc470ef132abe
浅clone
  1. 实现Cloneable接口的类都具备被拷贝的能力,拷贝在内存中进行,比new生成对象性能明显提升;
  2. clone方法是Object类的protected方法,也就是在用户编写的代码中不能直接调用。为此必须重写定义clone方法,并声明为public,
  3. Cloneable接口的出现与接口正常使用没有关系,是一个空接口,它只是一个标记表示一个对象需要克隆,如果一个对象需要克隆,而没有实现Cloneable接口,就会产生一个CloneNotSupportedException异常
  4. Clone()方法存在与Object对象中,但是其不会将对象的全部属性都拷贝,而是有选择的拷贝
    1. 基本类型:
    1. 对象:如果变量是一个实例对象,只拷贝地址引用;
    2. String字符串:拷贝地址引用,但是修改时,会从字符串池中重新生成新的字符串,原来的保持不变;
  1. 对于Clone实现对象的拷贝:需要新建大量的对象,工程量大可以使用序列化来实现对象的拷贝

 

package com.zl.se;


public class ShadowStudent implements Cloneable {
      private String name;
  
  private int age;
  
  private ShadowProfessor shadowProfessor;


  public String getName() {
      return name;
  }


  public void setName(String name) {
      this.name = name;
  }


  public int getAge() {
      return age;
  }


  public void setAge(int age) {
      this.age = age;
  }


  public ShadowProfessor getProfessor() {
      return shadowProfessor;
  }


  public void setProfessor(ShadowProfessor shadowProfessor) {
      this.shadowProfessor = shadowProfessor;
  }


  @Override
  public String toString() {
      return "shadowStudent [name=" + name + ", age=" + age + ", shadowProfessor="
              + shadowProfessor + "]";
  }
  
  public Object clone() throws CloneNotSupportedException{
      return super.clone();
  }


}

其中,Professor类同样为自定义类:

 

然而,当自定义类的字段的类型不是基本数据类型时,上面实现了clone()方法会导致问题,不信看下面的代码:

【程序实例1】

复制代码
 1 public class ShadowCopy {
 2 
 3     public static void main(String[] args) {
 4     		//引用类型发生变化
//		ShadowProfessor p1 = new ShadowProfessor();
//		p1.setName("Professor Zhang");
//		p1.setAge(30);
//
//		ShadowStudent s1 = new ShadowStudent();
//		s1.setName("xiao ming");
//		s1.setAge(18);
//		s1.setProfessor(p1);
//
//		System.out.println(s1);
//
//		try {
//			ShadowStudent s2 = (ShadowStudent) s1.clone();
//			ShadowProfessor p2 = s2.getProfessor();
//			p2.setName("Professor Li");
//			p2.setAge(45);
//			s2.setProfessor(p2);
//			System.out.println("复制后的:s1 = " + s1);
//			System.out.println("复制后的:s2 = " + s2);
//		} catch (CloneNotSupportedException e) {
//			e.printStackTrace();
//		}
		

26 
27     }
28 
29 }
复制代码

【运行结果1】

1 
//shadowStudent [name=xiao ming, age=18, shadowProfessor=ShadowProfessor [name=Professor Zhang, age=30]]
//复制后的:s1 = shadowStudent [name=xiao ming, age=18, shadowProfessor=ShadowProfessor [name=Professor Li, age=45]]
//复制后的:s2 = shadowStudent [name=xiao ming, age=18, shadowProfessor=ShadowProfessor [name=Professor Li, age=45]]

【结果分析】

学生s1的导师为30岁的Professor Zhang,恰好学生s2与学生s1同名同岁,但是s2的导师为45岁的Professor Li,于是我们顺理成章地复制复制s1并复制给s2,再修改下s2的导师的信息。可是,问题出现了,当我们修改了s2的导师后,s2的信息是对了,但是s1的导师信息也跟着修改了,这可不是我们期望的。

【问题分析】

程序实例1中的问题出在哪儿呢?我们已经对Student类实现了clone()方法,怎么还是出问题了呢?我们在看下面的代码:

【程序实例2】

复制代码
 1 public class ShadowCopy {
 2 
 3     public static void main(String[] args) {
		//基本类型不变
//		ShadowProfessor p1 = new ShadowProfessor();
//		p1.setName("Professor Zhang");
//		p1.setAge(30);
//
//		ShadowStudent s1 = new ShadowStudent();
//		s1.setName("xiao ming");
//		s1.setAge(18);
//		s1.setProfessor(p1);
//
//		System.out.println(s1);
//
//		try {
//			ShadowStudent s2 = (ShadowStudent) s1.clone();
//			ShadowProfessor p2 = s2.getProfessor();
//			 s2.setName("xiao hong");
//			 s2.setAge(17);
//			p2.setName("Professor Li");
//			p2.setAge(45);
//			s2.setProfessor(p2);
//			System.out.println("复制后的:s1 = " + s1);
//			System.out.println("复制后的:s2 = " + s2);
//		} catch (CloneNotSupportedException e) {
//			e.printStackTrace();
//		}
28 
29     }
30 
31 }
复制代码

【运行结果】

//shadowStudent [name=xiao ming, age=18, shadowProfessor=ShadowProfessor [name=Professor Zhang, age=30]]
//复制后的:s1 = shadowStudent [name=xiao ming, age=18, shadowProfessor=ShadowProfessor [name=Professor Li, age=45]]
//复制后的:s2 = shadowStudent [name=xiao hong, age=17, shadowProfessor=ShadowProfessor [name=Professor Li, age=45]]
//结论 :如果想要clone()得到的新对象的修改不会影响被复制的对象的字段时,我们就需要实现深复制(deep copy)

【结果分析】

这次,我们在clone后,又修改了s2的name和age,从结果可以看出,s1的name和age并没有因为s2的修改而改变。

 

结合程序实例1和程序实例2,我们发现Student的字段如果不是一个引用时,修改clone()得到对象的该字段(name, age)时并不会影响原来的对象,但是当字段为一个引用时,修改clone()得到对象的该字段(professor)时并会影响原来的对象。上面实现的clone()方法为浅复制(shadow copy)。

 

如果想要clone()得到的新对象的修改不会影响被复制的对象的字段时,我们就需要实现深复制(deep copy),代码修改如下:

public class DeepProfessor implements Cloneable {
private String name;

   private int age;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }
   
   @Override
   public String toString() {
       return "Professor [name=" + name + ", age=" + age + "]";
   }

   public Object clone() throws CloneNotSupportedException{
       return super.clone();
   }



}
复制代码
public class DeepStudent  implements Cloneable{
private String name;
    
    private int age;
    
    private DeepProfessor professor;


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }


    public DeepProfessor getProfessor() {
        return professor;
    }


    public void setProfessor(DeepProfessor professor) {
        this.professor = professor;
    }


    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", professor="
                + professor + "]";
    }
    
    public Object clone() throws CloneNotSupportedException{
    	DeepStudent newStudent = (DeepStudent) super.clone();
        newStudent.professor = (DeepProfessor) professor.clone();
        return newStudent;
    }


}



复制代码

再次运行【程序实例2】得到的结果为:

//Student [name=xiao ming, age=18, professor=Professor [name=Professor Zhang, age=30]]
//复制后的:s1 = Student [name=xiao ming, age=18, professor=Professor [name=Professor Zhang, age=30]]
//复制后的:s2 = Student [name=xiao hong, age=17, professor=Professor [name=Professor Li, age=45]]

可以看到:修改clone()得到的s2的任何字段都不会影响s1的字段,这也就是深复制的作用。

猜你喜欢

转载自blog.csdn.net/yz18931904/article/details/80576574
今日推荐