目录
在讨论对象克隆之前, 可能会有人说 , 不能直接new一个吗?为什么要克隆
首先关于这个问题, 直接new一个对象, 这个对象里面包含的成员变量是null的 , 那问题又来了, 我不能去一个个set对象的值吗? 当然可以, 但这样麻烦, 对象克隆采用的是native方法,效率更高
那么该怎样去克隆呢? 克隆又分为浅克隆和深克隆
首先这样的方式不叫克隆 (这种只能叫做引用复制):
- Object obj1 = new Object();
- Object obj2 = obj1;
怎样区分是浅克隆还是深克隆呢?
在java中, 数据类型分为基本类型和引用类型 , 在复制数据的过程中, 基本类型的值会直接被复制过去, 但是引用类型只能复制引用的地址 , 所以深浅克隆的区别就是是否将引用类型所指向的变量也复制了
先来看浅克隆 , 有两种实现方式, 一是重写Object类中的clone() 方法, 二是spring 框架中提供 BeanUtils.copyProperties(source,target); 这里我们演示第一种
- //需实现Cloneable接口
- public class Person implements Cloneable {
-
- int num;
- String name;
-
- public Person() { }
-
- public Person(int num, String name) {
- this.num = num;
- this.name = name;
- }
-
- //重写Object中的clone()方法, 实现克隆
- @Override
- protected Person clone() throws CloneNotSupportedException {
- Person person = (Person) super.clone();
- return person;
- }
- }
- public static void main(String[] args) throws CloneNotSupportedException {
-
- Person p1 = new Person(100,"jim");
- Person p2 = p1.clone(); //调用克隆方法
-
- System.out.println(p1==p2);
-
- }
以上输出结果当然为 false , 因为克隆后虽然包含数据相同, 但仍旧是两个对象, 采用 == 的方式比较, 输出的是两个对象的引用地址是否相同 , 当然为 false
当然在这里肯定有人会提出这样一个问题 : String不是引用类型吗? 怎么值跟着复制了
首先我们回想String的特点, 引用类型没错, 但是String底层的char数组是采用了 final修饰的, 被final修饰的我们叫做常量, 也就是不可改变的, 所以说 , String虽然是引用类型, 但是它的值一旦确定就不能再改变了, 所以值会跟着复制
深克隆会将引用类型所指向对象中包含的数据一同复制, 这里也需实现Cloneable接口
先设计一个Address类
- //实现Cloneable接口
- public class Address implements Cloneable{
-
- String address;
-
- public String getAddress() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address = address;
- }
-
- @Override
- public String toString() {
- return "Address{" +
- "address='" + address + '\'' +
- '}';
- }
- // 重写clone()方法
- @Override
- protected Address clone() throws CloneNotSupportedException {
- return (Address)super.clone();
- }
- }
接着是person类
- //实现Cloneable接口
- public class Person implements Cloneable{
-
- int num;
- String name;
- //在Person中关联Address对象
- Address address;
-
- public Person() {
- }
-
- public Person(int num, String name) {
- this.num = num;
- this.name = name;
- }
-
- // 由于会导致篇幅过长, 此处省略属性的get和set方法
-
- //重写clone()方法实现深度克隆
- @Override
- protected Person clone() throws CloneNotSupportedException {
- Person person = (Person)super.clone();
- //深度复制 联同person中关联的对象也一同克隆
- person.address = (Address)address.clone();
- return person;
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "num=" + num +
- ", name='" + name + '\'' +
- ", address=" + address +
- '}';
- }
- }
test类
- public class Test {
-
- public static void main(String[] args) throws CloneNotSupportedException {
- //先new一个Address类对象并设置值
- Address address = new Address();
- address.setAddress("成都");
- // new 一个Person类对象并设置值(将address属性加入)
- Person p1 = new Person(100,"jim");
- p1.setAddress(address);
- //克隆
- Person p2 =p1.clone();
- p2.setName("tom");
- //改变address所指向对象的值,如果深克隆成功,这个值即使改变也影响不了p2
- address.setAddress("西安");
-
- System.out.println("p1:"+p1);
- System.out.println("p2:"+p2);
- /*
- * 结果 : p1:Person{num=100, name='jim', address=Address{address='西安'}}
- * p2:Person{num=100, name='tom', address=Address{address='成都'}}
- * 深克隆成功
- * */
- }
- }
这就是深克隆, 连同引用对象中包含的值一起复制, 在上述例子中 , 如果是浅克隆, 仅仅复制了引用, 引用一旦发生改变, 原对象和克隆的对象中的address属性都会发生改变(如果浅克隆上述例子p1和p2中的address都会为"西安")
了解了深克隆, 那么有人会想到, 如果对象中套对象, 对象中再套对象, 这样一直连环套该怎样解决呢 ?
最为简单粗暴的一种方式就是 : 手动一层一层克隆, 这样当然也可以, 但是这里我们介绍一种更为简便的方式 : 序列化 , 当然简单的深克隆也是可以去使用序列化这种方式的
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作。
Address类
- //实现Serializable接口
- public class Address implements Serializable {
-
- String address;
-
- public String getAddress() {
- return address;
- }
-
- public void setAddress(String address) {
- this.address = address;
- }
-
- @Override
- public String toString() {
- return "Address{" +
- "address='" + address + '\'' +
- '}';
- }
- }
Person类
- //实现Serializable接口
- public class Person implements Serializable {
-
- int num;
- String name;
- Address address;
-
- public Person() {
- }
-
- public Person(int num, String name) {
- this.num = num;
- this.name = name;
- }
-
- //省略get和set方法
-
- //自定义克隆方法
- public Person myclone() {
- Person person = null;
- try {
- // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(this);
- // 将流序列化成对象
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- person = (Person) ois.readObject();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- return person;
- }
-
-
- @Override
- public String toString() {
- return "Person{" +
- "num=" + num +
- ", name='" + name + '\'' +
- ", address=" + address +
- '}';
- }
- }
测试不变, 不过这里我们克隆使用的是自己定义的myClone()方法 ,使用流的方式完成
关于对象克隆就说到这里, 感谢阅读.