深浅拷贝,如何实现深拷贝(深克隆)

一:为什么用clone:

在某一时刻对象A中已经包括了一些有效值
,此时可能会需要一个和A完全相同的新对象B,并且对B任何改动不会影响到A的值
A和B是两个对象,但B的初始值由A确定.
在Java中用简单的赋值语句不能满足这样的需求,
可以new一个新对象B,给B赋值,但是这样比较麻烦
clone()最简单最高效

二:new对象和clone()一个对象的过程区别

new的本意是分配内存,程序执行到new操作符时,实现去看new操作符后面的类型,
因为真的了类型才知道分配多大的内存空间,分配内存后
,再调用构造函数,填充对象的各个域
这就是对象的初始化
构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象

clone第一步也是分配内存,分配的内存和原对象相同
,然后使用原对象中对应的各个域,填充新对象的域
,然后clone方法返回,一个新的相同的对象创建,同样可以把这个新对象的引用发布到外部

三:在Object类中clone()方法是被protected修饰的

protected修饰的类和属性对于自己,本包及其子类可见 //这样理解是片面的
protected的成员或方法,要分子类和超类是否在同一个包下
不在同一个包中的子类:
只能访问自身从基类继承而来的受保护成员
不能访问基类实例本身的受保护成员
在同一个包下:
protected和public是一样的

如果直接使用调用clone()方法
编译器会报错误:‘clone() has protected access in ‘java.lang.Object’’
\\

public class classroom {
    public static void main(String... args){
        classroom a = new classroom ();
       a.clone();
    }
}
class student {
}//注意这里是classroom 调用clone(),不是student调用,编译器就不报protected

四:在Object类中的clone()方法是这样的:

在这里插入图片描述

而java.lang.Cloneable的源码:
///**
// * A class implements the Cloneable interface to
// * indicate to the {@link java.lang.Object#clone()} method that it
// * is legal for that method to make a
// * field-for-field copy of instances of that class.
// */
//public interface Cloneable {
//}
这就是一个标识接口

如果不实现Cloneable接口
编译器会报错误:Unhandled exception:java.lang.CloneNotSupportedException

五:native关键字

底层是c++语言,处理快

六:什么是深拷贝什么是浅拷贝?

浅拷贝(浅克隆):直接将原对象中的引用值拷贝给新对象需要引用的字段,
(A对象拷贝后是A1,B对象拷贝后是B1,A对象引用值是B对象)
浅拷贝(浅克隆):
直接将A对象中引用B对象的引用值拷贝给A1对象,
深拷贝(深克隆):
根据A对象中引用指向的B对象创建一个新的相同的B1对象
,将对象A1的引用指向新创建的B1对象

public class Player implements Cloneable {

    private String name;
    private int age;
    private Coach coach;

    public Player(String name, int age, Coach coach) {
        this.name = name;
        this.age = age;
        this.coach = coach;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Coach getCoach() {
        return coach;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    //根据Object的clone()的注解,我们子类重写超类的clone()方法
/*
    public Player clone() {
        Player player = null;
        try {
            player = (Player) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return player;
    }*/

    public static void main(String... args) {

        //深拷贝or浅拷贝


        Coach coach = new Coach("Popvinch");
        Player duncan = new Player("Duncan", 42, coach);


        Player clone = null;
        try {
            clone = (Player) duncan.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        //Player clone = (Player) duncan.clone();此处引用99-109---------------------------
        clone.getCoach().setName("Kerr");
        clone.setName("Curry");
        clone.setAge(30);
        System.out.println("His name is " + duncan.getName() + ",His age is " + duncan.getAge() + ",His coach is " + duncan.getCoach().getName());
        //His name is Duncan,His age is 42,His coach is Kerr
        System.out.println("His name is " + clone.getName() + ",His age is " + clone.getAge() + ",His coach is " + clone.getCoach().getName());
        //His name is Curry,His age is 30,His coach is Kerr
        System.out.println(duncan.getCoach() == clone.getCoach());//true
        String result = duncan.getCoach() == clone.getCoach() ? "clone是浅拷贝" : "clone是深拷贝";
        System.out.println(result);


        //从上面结果可知,克隆出来的Player对象里的name和age是新的,
        //通过setName和setAge方法可以改变,但是coach是和原来的共享的,
        //对克隆对象的coach进行修改同样会影响到原来对象的coach,
        //这就是浅克隆。


        //如何进行深克隆?
        //请看Boby


        //-----------------------
        //复制对象or复制引用
        Player p = new Player("zhangs", 18, coach);
        Player p2 = null;
        try {
            p2 = (Player) p.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println("p " + p);//cloneAndNew.Player@49097b5d
        System.out.println("p2 " + p2);//cloneAndNew.Player@6e2c634b
        //地址不同,创建了新对象,不是把原对象的地址赋值给了一个新的引用变量


        Player p3 = new Player("zhangs", 18, coach);
        Player p4 = p3;
        System.out.println("p3 " + p3);//cloneAndNew.Player@17a7cec2
        System.out.println("p4 " + p4);//cloneAndNew.Player@17a7cec2
        //地址一样,所以是同一个对象,
        //p3,p4只是引用他们俩都指向相同的对象Player("zhangs",18,coach);
        //这就是:  引用的赋值
    }
}

class Coach implements Cloneable {

    private String name;

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

    public String getName() {
        return name;
    }

    public Coach(String name) {
        this.name = name;
    }
}

在这里插入图片描述

七:clone()方法对字符串的克隆

clone()方法对对象中的String的克隆应该属于深克隆。原因在于String的不可变,比如String str = “java”;,除非你用反射,否则你无法改变“java”任何一个字符。所以 ,当克隆的时候把原型对象中的String引用复制给新对象的引用时,虽然新对象和原型指向相同的内容,但是原型不会影响到克隆对象的内容,从这一角度来说,对String应用的克隆属于深度克隆。

八:clone()方法对对象是浅拷贝,对String是深拷贝,那么怎么样才能实现对对象的深拷贝呢?

有两种方式:
一:
1.克隆类与克隆类的引用类都实现Cloneable
2.克隆类与引用类重写Object的clone()方法
3.参数
二:
1.克隆类与克隆类的引用类都实现Serialelzable接口
2.用对象的序列化和反序列化流读写对象

下面是第一种方式:

package cloneAndNew;

//实现深拷贝
//1:克隆类与克隆类的引用类都实现Cloneable
//2:克隆类的引用类重写Object的clone()方法
//3:测试


public class Boby implements Cloneable {
    private Head head;

    public Boby() {
    }

    public Boby(Head head) {
        this.head = head;
    }

    public Head getHead() {
        return head;
    }

    public void setHead(Head head) {
        this.head = head;
    }

    @Override
    protected Object clone() {
        Boby newBoby = null;
        try {
            newBoby = (Boby)
                    super.clone();

          newBoby.head = (Head) head.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return newBoby;
    }

    public static void main(String[] args) {
        Boby boby = new Boby(new Head("zhangs"));

        Boby newBoby = null;
        try {
            newBoby = (Boby) boby.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("boby == newBoby: " + (boby == newBoby));//boby == newBoby: false
        System.out.println("boby.head == newBoby.head: " + (boby.head == newBoby.head));

    }
}

class Head implements Cloneable {
    private String name;

    public Head() {
    }

    public Head(String name) {
        this.name = name;
    }

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

在这里插入图片描述
拷贝前后引用的对象不同说明拷贝前后引用的对象也发生了拷贝,这样就证明实现了深拷贝.

下面是第二种方式:

//方式二:
//1.实现Serialilzable接口
//2.对象的序列化和反序列化克隆

public class Person implements Serializable {
    private static final long serialVersionUID = 123131221321313L;
    private String name;
    private int age;
    private Car car;

    public Person() {
    }

    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    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() {
        final StringBuffer sb = new StringBuffer("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", car=").append(car);
        sb.append('}');
        return sb.toString();
    }

    public static void main(String[] args) {

        try {
            Person person = new Person("zhangs", 18, new Car("hah"));

            Person newPerson = Person.clone(person);
            System.out.println(person.getCar().getCarname());
            newPerson.getCar().setCarname("sussess");
            System.out.println(newPerson.getCar().getCarname());
            System.out.println(person.getCar()== newPerson.car?"不是深克隆":"是");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj)throws Exception{

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (T)ois.readObject();

    }
}
class Car implements Serializable{
    private static final long serialVersionUID = 123131221321313L;
private String Carname;

    public Car() {
    }

    public Car(String carname) {
        Carname = carname;
    }



    public String getCarname() {
        return Carname;
    }

    public void setCarname(String carname) {
        Carname = carname;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Car{");
        sb.append("Carname='").append(Carname).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

在这里插入图片描述
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对
象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object 类的 clone 方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时

九:@SuppressWarnings注解

这也是一个标识接口
@SuppressWarnings(“unchecked”)用在方法上,表示压制本方法的警告
@SuppressWarnings(“all”)用在类上,表示压制本类范围的警告
在这里插入图片描述

欢迎批斗

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值