定义
- 通过将一个原型对象传给要发动创建的对象,这个要发动的创建的对象通过请求原型对象了拷贝他们自己实现创建,即 对象.clone()
- 用原型实例指定创建对象的种类,并且通过拷贝,创建新的对象
- 缺点:需要为每一类配置一个克隆方法,对已有类改造,需修改源代码,违背了ocp原则
浅拷贝
- 定义:而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
三种代码实现方式
- 通过拷贝构造方法实现浅拷贝:
public class test1 {
public static void main(String[] args) {
Age age= new Age(1);
Person p1 = new Person(age, "小黄");
Person p2 = new Person(p1);
System.out.println("p1是"+p1.toString());
System.out.println("p2是"+p2.toString());
age.setAge(2);
// age.setAge(new Age(2)); 这里创建了一个新的对象,就不会改变原来的对象了,数据就不会变动了
p1.setName("小青");
//String类型属于引用数据类型,不属于基本数据类型,但是String类型的数据是存放在常量池中的,
//也就是无法修改的!也就是说,当我将name属性修改后,
//并不是修改了这个数据的值,而是把这个数据的引用从指向”小黄“这个常量改为了指向”大小青“这个常量。在这种情况下,另一个对象的name属性值仍然指向”小黄“不会受到影响。
System.out.println("修改后的p1是"+p1.toString());
System.out.println("修改后的p2是"+p2.toString());
/**
p1是Person{age=age{age=1}, name='小黄'}
p2是Person{age=age{age=1}, name='小黄'}
修改后的p1是Person{age=age{age=2}, name='小青'}
修改后的p2是Person{age=age{age=2}, name='小黄'}
成功复制了吗,但是引用数据类型的数据变动了,如果如果在拷贝构造方法中,
对引用数据类型变量逐一开辟新的内存空间,创建新的对象,或者使用深拷贝
*/
}
}
class Person {
//使用引用数据类型
private Age age;
//使用常量
private String name;
public Person(Age age, String name) {
this.age = age;
this.name = name;
}
public Person(Person p) {
this.age = p.getAge();
this.name = p.getName();
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Age{
private int age;
public Age(int age){
this.age=age;
}
public int getAge() {
return age;
}
public void setAge(Age age) {
this.age = age.getAge();
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "age{" +
"age=" + age +
'}';
}
}
通过重写clone()方法进行浅拷贝:
Object类是类结构的根类,其中有一个方法为protected Object clone(),这个方法就是进行的浅拷贝。
1. Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。
2. 使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。
3. 对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。
代码
public class test1 {
public static void main(String[] args) {
Age age = new Age(1);
Person p1 = new Person(age, "小黄");
Person p2 = (Person) p1.clone();
System.out.println("p1是" + p1.toString());
System.out.println("p2是" + p2.toString());
age.setAge(2);
// age.setAge(new Age(2)); 这里创建了一个新的对象,就不会改变原来的对象了,数据就不会变动了
p1.setName("小青");
System.out.println("修改后的p1是" + p1.toString());
System.out.println("修改后的p2是" + p2.toString());
}
}
class Person implements Cloneable {//实现一个接口
//使用引用数据类型
private Age age;
//使用常量
private String name;
public Person(Age age, String name) {
this.age = age;
this.name = name;
}
public Person(Person p) {
this.age = p.getAge();
this.name = p.getName();
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写Object类的clone方法
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (Exception e) {
System.out.println("报错了:" + e);
}
return obj;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Age {
private int age;
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(Age age) {
this.age = age.getAge();
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "age{" +
"age=" + age +
'}';
}
}
深克隆:
- 定义:简单地说,深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间
通过重写clone方法来实现
public class test1 {
public static void main(String[] args) {
Age age = new Age(1);
Person p1 = new Person(age, "小黄");
Person p2 = (Person) p1.clone();
System.out.println("p1是" + p1.toString());
System.out.println("p2是" + p2.toString());
// age.setAge(new Age(2));
age.setAge(2);
p1.setName("小青");
System.out.println("修改后的p1是" + p1.toString());
System.out.println("修改后的p2是" + p2.toString());
}
}
class Person implements Cloneable {//实现一个接口
//使用引用数据类型
private Age age;
//使用常量
private String name;
public Person(Age age, String name) {
this.age = age;
this.name = name;
}
public Person(Person p) {
this.age = p.getAge();
this.name = p.getName();
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写Object类的clone方法
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (Exception e) {
System.out.println("报错了:" + e);
}
Person p = (Person) obj;
p.setAge((Age) p.getAge().clone());//学生类实例的Age对象属性,调用其clone方法进行拷贝
return obj;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Age implements Cloneable {
private int age;
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(Age age) {
this.age = age.getAge();
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "age{" +
"age=" + age +
'}';
}
//重写Object的clone方法
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
通过对象序列化实现深拷贝:
- 虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。
- 将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。
- 注意的是,如果某个属性被transient修饰,那么该属性就无法被拷贝了
import java.io.*;
public class test1 {
public static void main(String[] args)throws IOException, ClassNotFoundException {
Age age = new Age(1);
Person p1 = new Person(age, "小黄");
Person p2 = (Person)serializations(p1);
System.out.println("p1是" + p1.toString());
System.out.println("p2是" + p2.toString());
// age.setAge(new Age(2));
age.setAge(2);
p1.setName("小青");
System.out.println("修改后的p1是" + p1.toString());
System.out.println("修改后的p2是" + p2.toString());
}
public static Object serializations(Object o1) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(o1);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
return ois.readObject();
}
}
class Person implements Serializable {//实现一个接口
//使用引用数据类型
private Age age;
//使用常量
private String name;
public Person(Age age, String name) {
this.age = age;
this.name = name;
}
public Person(Person p) {
this.age = p.getAge();
this.name = p.getName();
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class Age implements Serializable {
private int age;
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(Age age) {
this.age = age.getAge();
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "age{" +
"age=" + age +
'}';
}
}