栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一文深入Java浅拷贝和深拷贝

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一文深入Java浅拷贝和深拷贝

值类型和引用类型

理解浅拷贝之前我们需要分清楚值类型(int、float…)、包装类(Integer、Double…)以及自己定义类等类,其实就就是值类型和引用类型两种.
就像上面这个图中,int a=1024是值类型的所以是变量a就是直接等于实际的值1024,而object obj1、object obj2显然是引用类型,obj存储的不是实际的对象,而是对象在堆中的地址。 原型模式-浅拷贝和深拷贝图解

浅拷贝:根据上面来讲解,原型对象在被克隆后克隆出来的新对象和原型对象的地址是不一样的,在克隆出来对象中基础类型的直接复制一份的,也就是说,克隆对象修改值类型和原型对象修改各自的属性是没有半毛钱关系的,而其中的引用对象就不一样了,克隆出来的对象引用地址和原型对象的是同一个,也就说其中任意一个更改引用属性的时候都会影响到对方的属性,都是引用同一个对象。(包装类型比较特殊的引用类型,克隆之后双方都是互不干扰的)。深拷贝:原型对象在通过深拷贝之后,基础类型仍旧是各自独立的,而各自引用对象的地址指向却是不同的,也就说在克隆对象和原型对象有任意一方修改引用参数都不会影响到对方。 先上个简单的demo

public  class IdCard {
    private String cName;

    public IdCard(String cName) {
        this.cName = cName;
    }

    public String getcName() {
        return cName;
    }

    public void setcName(String cName) {
        this.cName = cName;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "cName='" + cName + ''' +
                '}';
    }
}

public class Student implements  Cloneable {
    private int id;
    private String name;
    private IdCard idCard;

 	.....省略get set方法
 	
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", idCard=" + idCard +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        IdCard cId = new IdCard("hhhhh");
        Student student1 = new Student(12,"student1", cId);
        Student student2 = (Student) student1.clone();

        student2.setId(1);
        student2.setName("student2");
        student2.idCard.setcName("xxxxx");
        System.out.println(student1);
        System.out.println(student2);


    }
}

运行结果如下:
包装类型和值类型的如图可知在浅拷贝后是互不相干的,但是我们自定义的IdCard引用对象一个改了就会影响另一个,这就是由于是同一个对象缘由。 那么为什么我们自定义对象是可变包装类又特殊不可变呢?

其原因是遵守不可变原则,即Immutable设计模式:

    类添加final修饰符,保证类不被继承。保证所有成员变量必须私有,并且加上final修饰(不可变指的是引用不可变,也就是不可以重新指向其他对象)不提供改变成员变量的方法,包括setter通过构造器初始化所有成员,进行深拷贝(deep copy)在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝

比如我们随便拿一个包装类Double类分析一下

public final class Double extends Number implements Comparable {
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;

    public static final double NEGATIVE_INFINITY = -1.0 / 0.0;

    public static final double NaN = 0.0d / 0.0;

    public static final double MAX_VALUE = 0x1.fffffffffffffP+1023; // 1.7976931348623157e+308

    public static final double MIN_NORMAL = 0x1.0p-1022; // 2.2250738585072014E-308

    @SuppressWarnings("unchecked")
    public static final Class   TYPE = (Class) Class.getPrimitiveClass("double");

    public static String toString(double d) {
        return FloatingDecimal.toJavaFormatString(d);
    }

    public static Double valueOf(String s) throws NumberFormatException {
        return new Double(parseDouble(s));
    }

 
    public static Double valueOf(double d) {
        return new Double(d);
    }

    public static double parseDouble(String s) throws NumberFormatException {
        return FloatingDecimal.parseDouble(s);
    }


    public static boolean isNaN(double v) {
        return (v != v);
    }

    public static boolean isInfinite(double v) {
        return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
    }

    public static boolean isFinite(double d) {
        return Math.abs(d) <= DoubleConsts.MAX_VALUE;
    }

    private final double value;


    public Double(double value) {
        this.value = value;
    }


    public Double(String s) throws NumberFormatException {
        value = parseDouble(s);
    }


    public boolean isNaN() {
        return isNaN(value);
    }

    public boolean isInfinite() {
        return isInfinite(value);
    }

首先这个Double类类是使用final关键字修饰表示不可继承,然后所有成员变量都是使用final修饰表示引用不可变,再其次就是不提供setter方法,即满足不可变原则。 如何浅拷贝

如上面IdCard和Student类就是实现Cloneable接口,并且重写了clone的方法,就可以实现。 如何深拷贝

有两种方法:一种继承Cloneable接口或者序列化继承Cloneable接口

public class IdCard implements Cloneable{
    private  Double cName;

    public IdCard(Double cName) {
        this.cName = cName;
    }

    public Double getcName() {
        return cName;
    }

    public void setcName(Double cName) {
        this.cName = cName;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "cName='" + cName + ''' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public  class Student implements  Cloneable {
    private int id;
    private String name;
    private IdCard idCard;

    public Student(int id, String name, IdCard idCard) {
        this.id = id;
        this.name = name;
        this.idCard = idCard;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", idCard=" + idCard +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    	//需要将引用的对象也clone
        Student student = (Student)super.clone();
        student.idCard = (IdCard)idCard.clone();
        return student;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        IdCard cId = new IdCard(12.5);
        Student student1 = new Student(12,"student1", cId);
        Student student2 = (Student) student1.clone();

        student2.setId(1);
        student2.setName("student2");
        student2.idCard.setcName(10.2);
        System.out.println(student1);
        System.out.println(student2);


    }
}

执行结果:
使用序列化和反序列化的方式深度拷贝,和Cloneable相比可以解决多层继承带来的深度克隆失效的问题

public class IdCard implements Serializable {

    private static final long serialVersionUID = 1L;

    private  Double cName;

    public IdCard(Double cName) {
        this.cName = cName;
    }

    public Double getcName() {
        return cName;
    }

    public void setcName(Double cName) {
        this.cName = cName;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "cName='" + cName + ''' +
                '}';
    }

}

public  class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private int id;
    private String name;
    private IdCard idCard;

    public Student(int id, String name, IdCard idCard) {
        this.id = id;
        this.name = name;
        this.idCard = idCard;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", idCard=" + idCard +
                '}';
    }


    public Student deepClone() {
        Student student = null;
        try {
            // 序列化
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
             objectOutputStream.writeObject(this);
             //反序列化
             ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
             student = (Student) objectInputStream.readObject();
        }catch (ClassNotFoundException | IOException e){
            e.printStackTrace();
        }
        return student;

    }

    public static void main(String[] args) throws CloneNotSupportedException {
        IdCard cId = new IdCard(12.5);
        Student student1 = new Student(12,"student1", cId);
        Student student2 = (Student) student1.deepClone();

        student2.setId(1);
        student2.setName("student2");
        student2.idCard.setcName(10.2);
        System.out.println(student1);
        System.out.println(student2);


    }
}

执行结果:
集合浅拷贝和深拷贝 linkedList浅拷贝

通过构造方式

        IdCard ggc = new IdCard(1, "ggc");
        List list1 = new linkedList<>();
        list1.add(ggc);
        List list2 = new linkedList<>(list1);
        list2.get(0).setName("mm");
        System.out.println(list1);
        System.out.println(list2);

执行结果:
通过forEach一个个插入

        IdCard ggc = new IdCard(1, "ggc");
        List list1 = new linkedList<>();
        list1.add(ggc);
        List list2 = new linkedList<>();
        list1.forEach(s->list2.add(s));
        list2.get(0).setName("mm");
        System.out.println(list1);
        System.out.println(list2);

执行结果:
数组通过System.arraycopy

        IdCard ggc = new IdCard(1, "ggc");
        IdCard[] arr1 = new IdCard[]{ggc};
        IdCard[] arr2 = new IdCard[1];
        System.arraycopy(arr1,0,arr2,0,arr1.length);
        arr2[0].setName("hhhh");
        Arrays.asList(arr1).forEach(System.out::println);
        Arrays.asList(arr2).forEach(System.out::println);

执行结果
linkedList深拷贝

通过list自带的addAll()方法

        List list1 = new linkedList<>(Arrays.asList(3,5,2,1));
        List list2 = new linkedList<>();
        list2.addAll(list1);
        list2.remove(0);
        System.out.println(list1);
        System.out.println(list2);

执行结果:
通过序列化和反向序列化

public class IdCard implements Serializable {
    private static final long serialVersionUID = 1L;
    private int Id;
    private String name;


    public IdCard(int id, String name) {
        Id = id;
        this.name = name;
    }

    public int getId() {
        return Id;
    }

    public void setId(int id) {
        Id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "Id=" + Id +
                ", name='" + name + ''' +
                '}';
    }
}

    public static  List deepClone(List source) throws IOException, ClassNotFoundException {
    //序列化
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(source);
    //反向序列化
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream);
    List o = (List)inputStream.readObject();
    return o;

}
    public static void main(String[] args) throws IOException, ClassNotFoundException {

    List list1 = new linkedList<>();
    list1.add(new IdCard(1,"123"));
    list1.add(new IdCard(2,"1233"));
    List list2 = deepClone(list1);
    list2.remove(0);
    System.out.println(list1);
    System.out.println(list2);
}
执行结果:
实现Cloneable接口,重写clone方法
    @Override
    public Object clone(){
        IdCard clone =null;
        try {
            clone = (IdCard) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
        IdCard idCard = new IdCard(1, "213");
        IdCard idCard2 = (IdCard) idCard.clone();
        idCard2.setName("gccc");
        System.out.println(idCard);
        System.out.println(idCard2);

执行结果:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/731408.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号