克隆
概述
克隆出现的原因: 在实际开发过程中,一个对象obj已经包含一些有用信息,这是我们需要将obj的信息复制到obj2中,使得obj和obj2对象具有两个完全不同的地址,修改一个对象的值,另一个对象不受影响。
实现克隆的方式
实现Cloneable接口,并重写object类中的clone方法,可以实现浅克隆,也可以实现深克隆
1 被克隆对象所属类必须实现Cloneable接口
public class 类名 implements Cloneable{}
2 所属类必须重写Object类的clone方法
@Override
public Object clone() throws CloneNotSupportedException{
类名 obj =(类名)super.clone();
obj.属性名 = (属性类)属性名.clone();
return obj;
}
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆
public class 类名 implements Serializable{}
1 创建序列化流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象
类名 obj = new 类名();
3 使用序列化流的writeObject方法将对象写入指定的ObjectOutputStream流中
oos.writeObject(obj);
4 释放资源
oos.close();
1 创建反序列化流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化流的readObject方法从ObjectInputStream流中读取一个对象
Object obj = ois.readObject();
3 强制转化对象类型
类型 obj1 = (类型)obj;
4 释放资源
ois.close();
利用BeanUtils,apache和spring都提供了bean工具,它是浅克隆
浅克隆(Shallow Clone) 和 深克隆(Deep Clone)
两种克隆方式主要区别在于是否支持引用类型的成员变量的复制。 java中的数据类型分为:基本数据类型(数值型{整型、浮点型、字符型}) 和 引用数据类型(接口、类、数组)
浅克隆(Shallow Clone)
浅克隆中,对象的复制只是复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。 实现方法 覆盖Object类中的Clone()方法实现。
1 被复制的类需要实现Cloneable接口
public class 类名 implements Cloneable{}
2 覆盖clone()方法,方法调用super.clone()方法得到需要的复制对象,输入clone有选项可以选择,自动生成。 修改为public访问级别
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
实验分析:
1 被复制的类没有实现Cloneable接口,而直接使用 类名 对象2 = (类名)对象1.clone(); 会报错
报clone()方法在java.lang.Object中是protected访问控制。
2 被复制的类实现Cloneable接口,使用 类名 对象2 = (类名)对象1.clone(); 也会报错,
需要抛出异常CloneNotSupportException异常
案例:
public class Student implements Cloneable{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class StudentDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("汪苏泷",35);
Student s2 = (Student)s1.clone();
System.out.println(s1.getName() + ", " + s1.getAge());
System.out.println("--------------");
System.out.println(s2.getName() + ", " + s2.getAge());
System.out.println(s1 == s2);
}
}
深克隆(Deep Clone)
深克隆中,无论原型对象的成员变量的类型是值类型还是引用数据类型,都会复制一份给克隆对象。 实现方法: 方法一:通过覆盖Object类中的clone()方法实现
1 被复制对象的类 需要实现Cloneable接口
public class 类名 implements Cloneable{}
2 重写Object类中的clone()方法,输入clone有选项可以选择 修改为public访问级别
@Override
public Object clone() throws CloneNotSupportedException{
Outer obj = (Outer)super.clone();
obj.inner = (Inner)inner.clone();
return obj;
}
方法二:使用序列化(Serialization)实现,序列化流ObjectOutputStream、反序列化流ObjectInputStream
1 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象
类名 obj1 = new 类名();
3 使用序列化方法writeObject(Object obj)
oos.writeObject(obj1);
4 释放资源
oos.close();
1 创建反序列化对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化方法readObject()
Object obj = ois.readObject();
3 强制类型转换 Object =》 类名
类名 obj2 =(类名)obj;
4 释放资源
ois.close();
方法一案例:
public class Address implements Cloneable {
private String address;
public Address() {
}
public Address(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Student implements Cloneable{
private String name;
private int age;
private Address addr;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student stu = (Student)super.clone();
stu.addr = (Address)addr.clone();
return stu;
}
}
public class StudentDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("西安市");
Student s1 = new Student("汪苏泷",35);
s1.setAddr(addr);
Student s2 = (Student)s1.clone();
System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());
System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());
addr.setAddress("重庆市");
System.out.println("修改之后");
System.out.println("--------------");
System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());
System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());
}
}
细节对比点
new操作符创建对象和clone方法复制对象的区别
具体操作 | new | clone |
---|
分配内存 | 根据new操作符后面的类型分配对应大小的内存空间 | 分配与源对象相同大小的内存空间 | 填充对象的域 | 调用构造方法,填充对象的各个域(对象的初始化) | 使用源对象中对应的各个域填充新对象的域 | 对象创建完毕 | 构造方法返回创建完毕的对象,并将其引用地址发布到外部(栈内存) | clone()方法返回对象,并将其引用发布到外部(栈内存) |
复制引用 和 复制对象 区别
A 是 源对象,B 是 新对象 复制引用 = A 和 B 地址值 相同,说明是一个对象 复制对象 = A 和 B 地址值 不相同,说明是两个对象
克隆两种实现方式的区别
区别项 | 实现Cloneable接口的方式 | 序列化和反序列化方式 |
---|
是否可以实现深度克隆 | 可以, 若对象属性嵌套很多引用类型,则需要在类中写多个.clone()方法,使用不是很方便 | 可以,使用方便 | 是否支持编译时检测异常 | 不支持,是在运行时抛出异常的 | 支持,通过泛型限定,可以检查出克隆的对象是否支持序列化,在编译阶段完成 |
|