java拷贝

1.为什么要拷贝java对象呢?
在某些情况下,我们需要保存当前对象的某种状态,那么我们需要将状态的值赋给一个新的对象。首先想到的是将变量一个一个的赋值给新对象,但是这样做如果变量多的情况下,会很麻烦。那么有没有好一点的办法呢?那就是通过拷贝的方式,来实现一次性将全部变量赋值。

注意:我们常见的Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍然指向了同一个对象。而通过clone方法赋值的对象跟原来的对象时同时独立存在的

2.实现拷贝

java拷贝分为浅拷贝和深拷贝

2.1浅拷贝步骤
(一) 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
(二) 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)
具体实现如下

//Address类
public class Address {

    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
}

//student类
public class Student implements Cloneable {

    private String name;
    private int age;
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    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 Object clone() throws CloneNotSupportedException {

        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (Exception e) {
            // TODO: handle exception
        }

        return student;
    }
}
    @Test
    public void test3() throws CloneNotSupportedException {

        Address address = new Address();
        address.setAdd("上地三街金融科贸大厦");
        Student student1 = new Student();
        student1.setAge(22);
        student1.setName("zpf");
        student1.setAddress(address);

        Student student2 = (Student) student1.clone();

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2 年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());
        //注意这里
        student2.setAge(23);
        student2.setName("ww");
        student2.getAddress().setAdd("定福黄庄村");

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2  年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());


        System.out.println(student1 == student2);

    }

打印结果如下
student1 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student2 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student1 年龄:22姓名:zpf地址:定福黄庄村
student2 年龄:23姓名:ww地址:定福黄庄村
false

从打印结果中可以看出 浅拷贝出来的对象与之前的对象内存地址不同;
但是我在修改student2中的参数时,年龄和名字没有影响到student1,但是修改地址时,影响了student1

那么我们看下浅拷贝的规则:
1、 基本类型
如果变量是基本很类型,则拷贝其值,比如int、float等。
2、 对象
如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量。
3、 String字符串
若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有字符串对象保持不变。

那么我们怎么解决这个问题呢?那就需要深拷贝了。

2.2 深拷贝

我们修改一下代码

public class Address implements Cloneable{

    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
    //该类实现cloneable接口  并重写clone方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        Address address = null;

        address = (Address) super.clone();

        return address;
    }

}


public class Student implements Cloneable {

    private String name;
    private int age;
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    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 Object clone() throws CloneNotSupportedException {

        Student student = null;
        try {
            student = (Student) super.clone();
            //添加深度赋值代码
            student.address = (Address) address.clone();
        } catch (Exception e) {
            // TODO: handle exception
        }

        return student;
    }
}


    @Test
    public void test3() throws CloneNotSupportedException {

        Address address = new Address();
        address.setAdd("上地三街金融科贸大厦");
        Student student1 = new Student();
        student1.setAge(22);
        student1.setName("zpf");
        student1.setAddress(address);

        Student student2 = (Student) student1.clone();

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2  年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());
        student2.setAge(23);
        student2.setName("ww");
        student2.getAddress().setAdd("定福黄庄村");

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2  年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());


        System.out.println(student1 == student2);

    }

student1 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student2 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student1 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student2 年龄:23姓名:ww地址:定福黄庄村
false

可以看出 以上的问题完美解决了。但是如果student类中有多个类的引用,如果想实现深度拷贝的话,就会每个类中都实现cloneable接口,并重写clone方法,感觉太麻烦。有没有什么方法解决呢?

3.利用序列化实现对象的拷贝

看下面代码的实现

public class Utils {
    public static <T extends Serializable> T clone (T t){
        T cloneObj = null;
        try {
            //往内存中写入字节流
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(t);
            objectOutputStream.close();
            //从内存中读取字节流
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            cloneObj =  (T) objectInputStream.readObject();

            objectInputStream.close();
            inputStream.close();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return cloneObj;
    } 
}


public class Address2 implements Serializable{

    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
}



public class Student2 implements Serializable {

    private String name;
    private int age;
    private Address2 address;

    public Address2 getAddress() {
        return address;
    }

    public void setAddress(Address2 address) {
        this.address = address;
    }

    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;
    }
}


@Test
    public void test4() throws CloneNotSupportedException {

        Address2 address = new Address2();
        address.setAdd("上地三街金融科贸大厦");
        Student2 student1 = new Student2();
        student1.setAge(22);
        student1.setName("zpf");
        student1.setAddress(address);



        Student2 student2 = Utils.clone(student1);

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2  年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());

        student2.setAge(23);
        student2.setName("ww");
        student2.getAddress().setAdd("定福黄庄村");

        System.out.println("student1  年龄:"+student1.getAge()+"姓名:"+student1.getName()+"地址:"+student1.getAddress().getAdd());
        System.out.println("student2   年龄:"+student2.getAge()+"姓名:"+student2.getName()+"地址:"+student2.getAddress().getAdd());


        System.out.println(student1 == student2);

    }

打印结果如下
student1 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student2 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student1 年龄:22姓名:zpf地址:上地三街金融科贸大厦
student2 年龄:23姓名:ww地址:定福黄庄村
false

可以看到 用序列化来实现对象的拷贝还是比较方便的。

参考文章链接:
https://www.cnblogs.com/Qian123/p/5710533.html
https://www.cnblogs.com/chenssy/p/3382979.html

猜你喜欢

转载自blog.csdn.net/qdh186/article/details/80454934