Java☞克隆

2019-05-29  本文已影响0人  小明今晚加班

在基类Object中,有个clone方法,可产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,又有了深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆将会将此属性完全拷贝一份;而浅克隆仅仅是拷贝一份此属性的引用。其实在克隆的时候最需要注意的是对象和数组,这两种类型一定要铭记深度克隆的方法。

  1. clone方法是Object类的,并不是Cloneable接口的,Cloneable只是一个标记接口,标记接口是标记实现该接口的类具有某种该接口标记的功能。常见的标记接口有三个:Cloneable、Serializable、RandomAccess,如果没有实现Cloneable接口,那么调用clone方法就会报CloneNotSupportException异常。
  2. 重写clone方法,内部仅仅是调用了父类的clone方法,当然这只是浅克隆的clone方法,深克隆就要修改了。
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
  1. 给出一张前人画的克隆图,简直太容易理解了:


    浅克隆.png
    深克隆.png

给出两种深克隆的方法

1. 实现Cloneable接口,重写clone方法(需要嵌套clone方法)

package cn.ihep.clone;

import java.util.Arrays;

/**
 * 测试克隆---深度克隆,浅度克隆
 * 
 * @author xiaoming
 *
 */
public class CloneTest implements Cloneable {

    private String name;
    private int age;
    private String[] hobby;
    private int[] nums;

    public CloneTest(String name, int age, String[] hobby, int[] nums) {
        super();
        this.name = name;
        this.age = age;
        this.hobby = hobby;
        this.nums = nums;
    }

    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 String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }

    public int[] getNums() {
        return nums;
    }

    public void setNums(int[] nums) {
        this.nums = nums;
    }

    @Override
    protected Object clone() {
        CloneTest clt = null;
        try {
            clt = (CloneTest) super.clone();
            //这里必须要加上hobby和nums的克隆(数组和对象都需要添加该方法)
            clt.hobby = hobby.clone();
            clt.nums = nums.clone();
            return clt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return clt;
    }

    @Override
    public String toString() {
        return "CloneTest [name=" + name + ", age=" + age + ", hobby=" + Arrays.toString(hobby) + ", nums="
                + Arrays.toString(nums) + "]";
    }

    public static void main(String[] args) {
        String name = "xiaoming";
        int age = 17;
        String[] strs = { "a", "b" };
        int[] nums = { 1, 2, 3 };
        CloneTest t = new CloneTest(name, age, strs, nums);
        CloneTest tNew = (CloneTest) t.clone();
        System.out.println(t);
        System.out.println(tNew);
        nums[0] = 250;
        strs[0] = "hahah";
        t.setNums(nums);
        t.setHobby(strs);
        System.out.println(t);
        System.out.println(tNew);
    }
}
-----------------
输出结果 :
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[hahah, b], nums=[250, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]

2. 通过序列化的方法来实现深度克隆

使用这种方法的时候,克隆的对象一定要实现Serializable接口。

package cn.ihep.clone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;

/**
 * 通过序列化来实现深度克隆
 * 
 * @author xiaoming
 *
 */
public class CloneBySerializable implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String[] hobby;
    private int[] nums;

    public CloneBySerializable(String name, int age, String[] hobby, int[] nums) {
        super();
        this.name = name;
        this.age = age;
        this.hobby = hobby;
        this.nums = nums;
    }

    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 String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }

    public int[] getNums() {
        return nums;
    }

    public void setNums(int[] nums) {
        this.nums = nums;
    }

    @Override
    public String toString() {
        return "CloneTest [name=" + name + ", age=" + age + ", hobby=" + Arrays.toString(hobby) + ", nums="
                + Arrays.toString(nums) + "]";
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String name = "xiaoming";
        int age = 17;
        String[] strs = { "a", "b" };
        int[] nums = { 1, 2, 3 };
        CloneBySerializable t = new CloneBySerializable(name, age, strs, nums);
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(t);
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        CloneBySerializable cloneT = (CloneBySerializable) oi.readObject();
        System.out.println(t);
        System.out.println(cloneT);
        nums[0] = 250;
        strs[0] = "hahah";
        t.setNums(nums);
        t.setHobby(strs);
        System.out.println(t);
        System.out.println(cloneT);
    }
}
--------------------------------
输出结果:
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[hahah, b], nums=[250, 2, 3]]
CloneTest [name=xiaoming, age=17, hobby=[a, b], nums=[1, 2, 3]]

通过序列化的方法来实现深克隆还是一个比较常用的操作,比如有一个列表list,里面存储的是User对象,即List<User> list = new ArrayList<>();如果想把列表list给拷贝一份给cloneList,该如何操作呢?
这个时候序列化的方式首选,前提User类要实现Serializable接口。如下:

package cn.ihep.clone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.internal.compiler.batch.Main;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    public User(String name, int age) {
        super();
        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
    public String toString() {
        return "User [name=" + name + ", age=" + age + "]";
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        User u1 = new User("xiaoming", 18);
        User u2 = new User("ahua", 45);
        User u3 = new User("zhangsan", 19);
        User u4 = new User("lisi", 20);
        List<User> list = new ArrayList<>();
        list.add(u1);
        list.add(u2);
        list.add(u3);
        list.add(u4);

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(list);

        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        List<User> cloneList = (List<User>) oi.readObject();
        System.out.println(list);
        System.out.println(cloneList);
        
        list.get(0).setName("zhaoxiaoming");
        list.get(0).setAge(100);
        System.out.println(list);
        System.out.println(cloneList);
    }
}
--------------------------------
输出结果:
[User [name=xiaoming, age=18], User [name=ahua, age=45], User [name=zhangsan, age=19], User [name=lisi, age=20]]
[User [name=xiaoming, age=18], User [name=ahua, age=45], User [name=zhangsan, age=19], User [name=lisi, age=20]]
[User [name=zhaoxiaoming, age=100], User [name=ahua, age=45], User [name=zhangsan, age=19], User [name=lisi, age=20]]
[User [name=xiaoming, age=18], User [name=ahua, age=45], User [name=zhangsan, age=19], User [name=lisi, age=20]]

注意:String类型的变量在克隆时候外表效果看起来和基本数据类型一样,其实不是这样的,因为String类型是不可改变类型,所以,当你修改字符串str的值的时候,是直接在堆上又new出来了一个对象,同时你的引用地址也跟着指向这个新地址了,所以看起来“貌似”不符合上述对象克隆的特征,事实上他们并冲突,就是因为String类型特殊。

上一篇下一篇

猜你喜欢

热点阅读