精华内容
下载资源
问答
  • Java中Cloneable的使用

    2020-03-20 10:37:19
    Java中Cloneable的使用 1. Cloneable的用途 Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就...

    Java中Cloneable的使用

    1. Cloneable的用途

    Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。

    2. 克隆的分类

    • 浅克隆(shallow clone),浅克隆是指拷贝对象时仅仅copy对象本身对象中的基本变量,而不拷贝对象包含的引用指向的对象。

    • 深克隆(deep clone),不仅copy对象本身,而且copy对象包含的引用指向的所有对象。

      举例:对象X中包含对Y的引用,Y中包含对Z的引用。浅拷贝X得到X1,X1中依然包含对Y的引用,Y中依然包含对Z的引用。深拷贝则是对浅拷贝的递归,深拷贝X得到X1,X1中包含对Y1(Y的copy)的引用,Y1中包含对Z1(Z的copy)的引用。

    3. 克隆代码举例

    要让对象可以被克隆,应具备以下2个条件:

    • 让该类实现java.lang.Cloneable接口;

    • 重写(Override)Object的clone()方法;

    
    package com.lgl;
    
    public class Info implements Cloneable {
    
        private int id;
    
        private String text;
    
    
    
        public Info(int id, String text) {
    
            this.id = id;
    
            this.text = text;
    
        }
    
    
    
        @Override
    
        public boolean equals(Object obj) {
    
            if (this == obj) {
    
                return true;
    
            }
    
            if (obj == null) {
    
                return false;
    
            }
    
            if (obj.getClass()!= getClass()) {
    
                return false;
    
            }
    
            Info temp = (Info) obj;
    
            if (id != temp.id) {
    
                return false;
    
            }
    
            if (text == null) {
    
                if (temp.text != null) {
    
                    return false;
    
                }
    
            } else if (!text.equals(temp.text)) {
    
                return false;
    
            }
    
            return true;
    
        }
    
    
    
        @Override
    
        protected Object clone() throws CloneNotSupportedException {
    
            return super.clone();
    
        }
    
    
    
        public static void main(String[] args) throws CloneNotSupportedException {
    
            Info info1 = new Info(1, "I am Colyn Lu.");
    
            Info info2 = (Info) info1.clone();
    
    
    
            System.out.println(info1.getClass() == info2.getClass());//true
    
            System.out.println(info1 == info2);//false
    
            System.out.println(info1.equals(info2));//true
    
        }
    
    }
    
    

    4. 浅克隆

    
    package com.lgl;
    
    public class MyFile implements Cloneable {
    
        private String path;
    
        private Info info;
    
    
    
        public MyFile(String path, Info info) {
    
            this.path = path;
    
            this.info = info;
    
        }
    
    
    
        @Override
    
        public boolean equals(Object obj) {
    
            if (this == obj) {
    
                return true;
    
            }
    
            if (obj == null) {
    
                return false;
    
            }
    
            if (obj.getClass()!= getClass()) {
    
                return false;
    
            }
    
            MyFile temp = (MyFile) obj;
    
            if (path == null) {
    
                if (temp.path != null) {
    
                    return false;
    
                }
    
            } else if (!path.equals(temp.path)) {
    
                return false;
    
            }
    
            if (info == null) {
    
                if (temp.info != null) {
    
                    return false;
    
                }
    
            } else if (!info.equals(temp.info)) {
    
                return false;
    
            }
    
            return true;
    
        }
    
    
    
        @Override
    
        protected Object clone() throws CloneNotSupportedException {
    
            return super.clone();
    
        }
    
    
    
        public static void main(String[] args) throws CloneNotSupportedException {
    
            Info info = new Info(2, "Hello world.");
    
            MyFile file1 = new MyFile("c:", info);
    
            MyFile file2 = (MyFile) file1.clone();
    
    
    
            System.out.println(file1.getClass() == file2.getClass());//true
    
            System.out.println(file1 == file2);//false
    
            System.out.println(file1.equals(file2));//true
    
            System.out.println(file1.info.getClass() == file2.info.getClass());//true
    
            System.out.println(file1.info == file2.info);//true
    
            System.out.println(file1.info.equals(file2.info));//true
    
        }
    
    }
    
    

    5.深克隆

    深克隆需要重写(Override)Object类的clone()方法,并且在方法内部调用持有对象的clone方法。

    
    package com.lgl;
    
    public class MyFile2 implements Cloneable {
    
        private String path;
    
        private Info info;
    
    
    
        public MyFile2(String path, Info info) {
    
            this.path = path;
    
            this.info = info;
    
        }
    
    
    
        @Override
    
        public boolean equals(Object obj) {
    
            if (this == obj) {
    
                return true;
    
            }
    
            if (obj == null) {
    
                return false;
    
            }
    
            if (obj.getClass()!= getClass()) {
    
                return false;
    
            }
    
            MyFile2 temp = (MyFile2) obj;
    
            if (path == null) {
    
                if (temp.path != null) {
    
                    return false;
    
                }
    
            } else if (!path.equals(temp.path)) {
    
                return false;
    
            }
    
            if (info == null) {
    
                if (temp.info != null) {
    
                    return false;
    
                }
    
            } else if (!info.equals(temp.info)) {
    
                return false;
    
            }
    
            return true;
    
        }
    
    
    
        @Override
    
        protected Object clone() throws CloneNotSupportedException {
    
            MyFile2 file = (MyFile2) super.clone();
    
            file.info = (Info) file.info.clone();
    
    
    
            return file;
    
        }
    
    
    
        public static void main(String[] args) throws CloneNotSupportedException {
    
            Info info = new Info(2, "Hello world.");
    
            MyFile2 file1 = new MyFile2("c:", info);
    
            MyFile2 file2 = (MyFile2) file1.clone();
    
    
    
            System.out.println(file1.getClass() == file2.getClass());//true
    
            System.out.println(file1 == file2);//false
    
            System.out.println(file1.equals(file2));//true
    
            System.out.println(file1.info.getClass() == file2.info.getClass());//true
    
            System.out.println(file1.info == file2.info);//false
    
            System.out.println(file1.info.equals(file2.info));//true
    
        }
    
    }
    
    展开全文
  • 转自:https://blog.csdn.net/xiaomingdetianxia/article/details/74453033Cloneable接口clone:它...Java 一个类要实现clone功能 必须实现 Cloneable接口,否则在调用 clone() 时会报 CloneNotSupportedExcep...

    转自:https://blog.csdn.net/xiaomingdetianxia/article/details/74453033

     

    Cloneable接口

     

    clone:它允许在堆中克隆出一块和原对象一样的对象,并将这个对象的地址赋予新的引用。 
    Java 中 一个类要实现clone功能 必须实现 Cloneable接口,否则在调用 clone() 时会报 CloneNotSupportedException 异常。

    Java中所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone(),这个方法将返回Object对象的一个拷贝。 
    要说明的有两点: 
    一是拷贝对象返回的是一个新对象,而不是一个引用; 
    二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。

    如果一个类重写了 Object 内定义的 clone()方法 ,需要同时实现 Cloneable 接口(虽然这个接口内并没有定义 clone() 方法),否则会抛出异常,也就是说, Cloneable 接口只是个合法调用 clone() 的标识(marker-interface)。

    实例

     

    class CloneClass implements Cloneable{
     public int aInt;
     public Object clone(){
      CloneClass o = null;
      try{
       o = (CloneClass)super.clone();
      }catch(CloneNotSupportedException e){
       e.printStackTrace();
      }
      return o;
     }
    }

    有三个值得注意的地方: 
    一是为了实现clone功能,CloneClass类实现了Cloneable接口,这个接口属于java.lang 包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable; 
    二是重载了clone()方 法; 
    三是在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或 间接调用了java.lang.Object类的clone()方法。

    Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为 什么要用Object中clone()方法而不是先new一个对象,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能,但效率较低。

    Object类中的clone()方法还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在 Java中所有的类是缺省继承Object类的,也就不用关心这点了。

    然后重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone 类的clone()方法,重载之后要把clone()方法的属性设置为public。 

    Serializable接口

    Serializable接口中一个成员函数或者成员变量也没有,这个接口的作用就是实现序列化,那什么是序列化?

    序列化

    序列化:对象的寿命通常随着生成该对象的程序的终止而终止,而有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。 Java提供了一种保存对象状态的机制,那就是序列化。

    Java 序列化技术可以将一个对象的状态写入一个Byte 流里(序列化),并且可以从其它地方把该Byte 流里的数据读出来(反序列化)。

    什么时候需要序列化

    想把内存中的对象状态保存到一个文件中或者数据库中时候; 
    想把对象通过网络进行传播的时候

    如何序列化

    只要一个类实现Serializable接口,那么这个类就可以序列化了。

    实例

    class Person implements Serializable{   
        //一会就说这个是做什么的
        private static final long serialVersionUID = 1L; 
        String name;
        int age;
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    通过ObjectOutputStream 的writeObject()方法把这个类的对象写到一个地方(文件),再通过ObjectInputStream 的readObject()方法把这个对象读出来。

    File file = new File("file"+File.separator+"out.txt");
    
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            ObjectOutputStream oos = null;
            try {
                oos = new ObjectOutputStream(fos);
                Person person = new Person("tom", 22);
                // 调用 person的 tostring() 方法
                System.out.println(person);
                //写入对象
                oos.writeObject(person);            
                oos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    oos.close();
                } catch (IOException e) {
                    System.out.println("oos关闭失败:"+e.getMessage());
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("找不到文件:"+e.getMessage());
        } finally{
            try {
                fos.close();
            } catch (IOException e) {
                System.out.println("fos关闭失败:"+e.getMessage());
            }
        }
    
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            ObjectInputStream ois = null;
            try {
                ois = new ObjectInputStream(fis);
                try {
                    Person person = (Person)ois.readObject();   //读出对象
                    System.out.println(person);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    ois.close();
                } catch (IOException e) {
                    System.out.println("ois关闭失败:"+e.getMessage());
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("找不到文件:"+e.getMessage());
        } finally{
            try {
                fis.close();
            } catch (IOException e) {
                System.out.println("fis关闭失败:"+e.getMessage());
            }
        }

    输出:

    name:tom    age:22
    name:tom    age:22

    结果完全一样;如果把Person类中的implements Serializable 去掉,Person类就不能序列化了,此时再运行上述程序,就会报java.io.NotSerializableException异常。

    serialVersionUID

    注意到上面程序中有一个 serialVersionUID ,实现了Serializable接口之后,Eclipse就会提示你增加一个 serialVersionUID,虽然不加的话上述程序依然能够正常运行。

    序列化 ID 在 Eclipse 下提供了两种生成策略

    一个是固定的 1L 
    一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具,根据类名、接口名、成员方法及属性等来生成) 
    上面程序中,输出对象和读入对象使用的是同一个Person类。

    如果是通过网络传输的话,如果Person类的serialVersionUID不一致,那么反序列化就不能正常进行。例如在客户端A中Person类的serialVersionUID=1L,而在客户端B中Person类的serialVersionUID=2L 那么就不能重构这个Person对象。

    客户端A中的Person类:

    class Person implements Serializable{   
    
        private static final long serialVersionUID = 1L;
    
        String name;
        int age;
    
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    客户端B中的Person类:

    class Person implements Serializable{   
    
        private static final long serialVersionUID = 2L;
    
        String name;
        int age;
    
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }试图重构就会报java.io.InvalidClassException异常,因为这两个类的版本不一致,local class incompatible,重构就会出现错误。

    如果没有特殊需求的话,使用用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。

    静态变量序列化 
    序列化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且序列化保存的只是变量的值,对于变量的任何修饰符都不能保存。

    如果把Person类中的name定义为static类型的话,试图重构,就不能得到原来的值,只能得到null。说明对静态成员变量值是不保存的。这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

    transient关键字

    ( java的serialization提供了一个非常棒的存储对象状态的机制,说白了serialization就是把对象的状态存储到硬盘上去,等需要的时候就可以再把它读出来使用。但是在存储对象状态时,我们有时候会需要特定的对象数据在serialization时不进行存储。这时候transient关键字就派上用场了。要关掉类的特定的数据域,可以使用transient关键字进行定义,这对于底层的java虚拟机来说,这个transient类型的变量不是一个类的永久性的状态。)

    经常在实现了 Serializable接口的类中能看见transient关键字。 transient关键字的作用是:阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。

    当某些变量不想被序列化,同是又不适合使用static关键字声明,那么此时就需要用transient关键字来声明该变量。

    例如用 transient关键字 修饰name变量

    class Person implements Serializable{   
    
        private static final long serialVersionUID = 1L;
    
        transient String name;
        int age;
    
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }
    

    在反序列化视图重构对象的时候,作用与static变量一样: 输出结果为:

    name:null   age:22

    在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

    注:对于某些类型的属性,其状态是瞬时的,这样的属性是无法保存其状态的。例如一个线程属性或需要访问IO、本地资源、网络资源等的属性,对于这些字段,我们必须用transient关键字标明,否则编译器将报措。

    序列化中的继承问题

    当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口; 
    一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,要想将父类对象也序列化,就需要让父类也实现Serializable 接口。 
    第二种情况中:如果父类不实现 Serializable接口的话,就需要有默认的无参的构造函数。这是因为创建java 对象的时候需要先有父对象,才有子对象,反序列化也不例外。在反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。在这种情况下,在序列化时根据需要在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

    实例

    class People{
        int num;
        public People(){}           //默认的无参构造函数,没有进行初始化
        public People(int num){     //有参构造函数
            this.num = num;
        }
        public String toString(){
            return "num:"+num;
        }
    }
    class Person extends People implements Serializable{    
    
        private static final long serialVersionUID = 1L;
    
        String name;
        int age;
    
        public Person(int num,String name,int age){
            super(num);             //调用父类中的构造函数
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return super.toString()+"\tname:"+name+"\tage:"+age;
        }
    }

    将对象写到文件中

    //调用带参数的构造函数num=10,name = "tim",age =22
    Person person = new Person(10,"tom", 22); 
        System.out.println(person);
        oos.writeObject(person);    

    从文件中读出对象

    //反序列化,调用父类中的无参构函数
    Person person = (Person)ois.readObject(); 
        System.out.println(person);

    输出

    num:0   name:tom    age:22

     

     

     

     

     

    展开全文
  • java中cloneable的使用

    千次阅读 2017-09-12 15:33:40
    什么是java中的浅克隆和深克隆? 浅克隆:克隆对象的变量与之前对象的值相同,并且对象的引用类型变量仍然指向原来对象引用类型变量的地址. 深克隆:克隆对象的变量与之前对象的值相同,并且对象的引用类型变量...
    什么是java中的浅克隆和深克隆?
    • 浅克隆:克隆对象中的变量与之前对象的值相同,并且对象中的引用类型变量仍然指向原来对象引用类型变量的地址.
    • 深克隆:克隆对象中的变量与之前对象的值相同,并且对象中的引用类型变量指向了新的对象的引用变量的地址.
    • 要想实现克隆,只需定义的类声明下cloneable这个标记性接口,并且衍生重写Object类中就有的clone()方法即可.

    为什么类要首先声明cloneable标记接口,然后重写clone()方法?因为不声明cloneable调用clone()方法会抛出CloneNotSupportedException异常,源码如下:

    protected Object clone() throws CloneNotSupportedException {
            if (!(this instanceof Cloneable)) {
                throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                     " doesn't implement Cloneable");
            }
    
            return internalClone();
        }
    
        /*
         * Native helper method for cloning.
         */
        private native Object internalClone();
    

    在上一节中讲了java中Serializable与Parcelable的使用序列化与反序列化的问题。事实上利用对象输出流对对象进行序列化,利用对象的输入流对对象进行反序列化也可以实现克隆,如果对象中依赖的其他对象的引用也实现了序列化(即引用类实现了serializable标记接口)就实现了深度克隆,否则实现了浅克隆.

    实现了Serializable接口的Company

    public class Company implements Serializable {//Serializable接口是空的,没有声明的方法及常量
    
        private static final long serialVersionUID = 1L; //序列化标识
    
        private String name;
    
        private String address;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public Company(String name, String address) {
            this.name = name;
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "company name is:"+name+",address is:"+address;
        }
    }
    

    获得实现了Serializable接口的克隆实例调用方法。

    private <T> T getCopyObj(T t) {
    
            ByteArrayOutputStream byteArrayOutputStream = null;
            ObjectOutputStream objectOutputStream = null;
    
            ByteArrayInputStream byteArrayInputStream = null;
            ObjectInputStream objectInputStream = null;
    
            try {
                byteArrayOutputStream = new ByteArrayOutputStream();
    
                objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(t);//序列化对象
                objectOutputStream.flush();
    
                byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                objectInputStream = new ObjectInputStream(byteArrayInputStream);
                T t1 = (T) objectInputStream.readObject();
                return t1;
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (byteArrayOutputStream != null) {
                    try {
                        byteArrayOutputStream.close();
                        byteArrayOutputStream = null;
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                if (objectOutputStream != null) {
                    try {
                        objectOutputStream.close();
                        objectOutputStream = null;
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                if (byteArrayInputStream != null) {
                    try {
                        byteArrayInputStream.close();
                        byteArrayInputStream = null;
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                if (objectInputStream != null) {
                    try {
                        objectInputStream.close();
                        objectInputStream = null;
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
            return null;
    
        }
    }
    

    测试通过的testCase,说明通过Serializable的反序列化创建的是一个新的对象,不再是之前的对象了。

        @Test
        public void test() throws CloneNotSupportedException {
            Company company=new Company("百度","上地十街");
            Company copyCompany=getCopyObj(company);
            copyCompany.setName("腾讯");
            Assert.assertEquals(false,company==copyCompany);
            Assert.assertEquals(true,company.getName().equals("百度"));
            Assert.assertEquals(true,copyCompany.getName().equals("腾讯"));
    
        }
    

    实现了Clonable克隆的例子
    public class People implements Cloneable {
    
        private String name;
    
        private int age;
    
        public People(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        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;
        }
    }
    

    验证通过的case,表明了克隆出来的对象与原来的对象地址不一样,是一个新的对象,所以克隆对象中的name和age是新的.

        @Test
        public void test() throws CloneNotSupportedException {
    
            People people = new People("storm", 30);
            People clonePeople = (People) people.clone();
    
            clonePeople.setName("stormClone");
            clonePeople.setAge(29);
    
            Assert.assertFalse(people == clonePeople);
            System.out.println("people name=" + people.getName());//people name=storm
            System.out.println("people age=" + people.getAge());//people age=30
            System.out.println("clonePeople name=" + clonePeople.getName());//clonePeople name=stormClone
            System.out.println("clonePeople age=" + clonePeople.getAge());//clonePeople age=29
    
        }

    使用cloneable实现浅克隆

    public class Animal {
    
        private String animalName;
    
        public Animal(String animalName) {
            this.animalName = animalName;
        }
    
        public String getAnimalName() {
            return animalName;
        }
    
        public void setAnimalName(String animalName) {
            this.animalName = animalName;
        }
    }
    
    public class People implements Cloneable {
    
        private String name;
    
        private int age;
    
        private Animal animal;//克隆对象中的引用型变量
    
        public People(String name, int age,Animal animal) {
            this.name = name;
            this.age = age;
            this.animal=animal;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        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 Animal getAnimal() {
            return animal;
        }
    
        public void setAnimal(Animal animal) {
            this.animal = animal;
        }
    }
    

    验证通过的case,表明了克隆对象的引用型变量animal并未发生改变,也即使内存中的地址并未发生改变,所以对其name的更改会影响原对象与克隆对象的值.

        @Test
        public void test() throws CloneNotSupportedException {
    
            Animal animal=new Animal("cat");
            People people = new People("storm", 30,animal);
    
            People clonePeople = (People) people.clone();
            animal.setAnimalName("dog");
    
            Assert.assertFalse(people == clonePeople);
            Assert.assertTrue(people.getAnimal()==clonePeople.getAnimal());
            Assert.assertTrue(people.getAnimal().getAnimalName().equals("dog"));
            Assert.assertTrue(clonePeople.getAnimal().getAnimalName().equals("dog"));
    
     }
    

    使用cloneable实现深克隆(实现很简单只需要引用类型变量实现cloneable接口即可),相比浅克隆,只需做如下修改.

    
    public class Animal implements Cloneable{
    
        private String animalName;
    
        public Animal(String animalName) {
            this.animalName = animalName;
        }
    
        public String getAnimalName() {
            return animalName;
        }
    
        public void setAnimalName(String animalName) {
            this.animalName = animalName;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    

    验证通过的case,表明了克隆对象的引用型变量animal发生改变,也即内存中的地址发生改变,所以对其name的更改不会影响克隆对象的值.同时说明了进行深克隆会把所有的引用类型都实现cloneable接口,如果克隆对象中的引用类型变量比较多的话,这牵涉的工作量就会比较大了,这时我们考虑使用上面实现Serializable实现克隆的方式,缺点是反复进行IO操作,内存开销大.

    @Test
        public void test() throws CloneNotSupportedException {
    
            Animal animal=new Animal("cat");
            People people = new People("storm", 30,animal);
    
            People clonePeople = (People) people.clone();
            Animal cloneAnimal=(Animal) animal.clone();
            clonePeople.setAnimal(cloneAnimal);
            animal.setAnimalName("dog");
    
            Assert.assertFalse(people == clonePeople);
            Assert.assertFalse(people.getAnimal()==clonePeople.getAnimal());
            Assert.assertTrue(people.getAnimal().getAnimalName().equals("dog"));
            Assert.assertTrue(clonePeople.getAnimal().getAnimalName().equals("cat"));
    
        }
    展开全文
  • Cloneable只是一个标记接口,只有实现了这个接口,才能在类重写clone方法,然后通过类调用clone完成功能实现,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常 1、浅克隆 首先创建...

    一、Cloneable接口

    Cloneable只是一个标记接口,只有实现了这个接口,才能在类中重写clone方法,然后通过类调用clone完成功能实现,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常

    1、浅克隆

    首先创建Student类实现Cloneable接口,重写clone方法,clone默认返回Object类,所以我们在返回时就强转为Student类

    // 省略构造方法,get,set方法
    class Student implements Cloneable{
        private String name;
        private int age;
        private School school;
    
        @Override
        protected Student clone() throws CloneNotSupportedException {
            return (Student)super.clone();
        }
    

    创建School类

    // 省略构造方法
    class School{
        private String name;
        private String address;
    }
    

    测试类调用

    public class Test {
        public static void main(String[] args) throws CloneNotSupportedException {
            School school = new School("清华大学","北京");
            Student student = new Student("lisa",22,school);
            Student student1 = student.clone();
            System.out.println(student);
            System.out.println(student1);
            System.out.println(student.getSchool());
            System.out.println(student1.getSchool());
            student.getSchool().setName("北京大学");
            System.out.println(student.getSchool().getName());
            System.out.println(student1.getSchool().getName());
        }
    }
    

    结果

    Student@4554617c
    Student@74a14482
    School@1540e19d
    School@1540e19d
    北京大学
    北京大学
    

    可以看出student与student1的hashcode不同,也就是说clone方法并不是把student的引用赋予student1,而是在堆中重新开辟了一块空间,将student复制过去,将新的地址返回给student1
    但是s1中stu的hashcode与s2中stu的hashcode相同,也就是这两个指向了同一个对象,修改s2中的stu会造成s1中stu数据的改变。但是修改s2中的基本数据类型与Stirng类型时,不会造成s1中数据的改变,基本数据类型例如int,在clone的时候会重新开辟一个四个字节的大小的空间,将其赋值。而String则由于String变量的唯一性,如果在s2中改变了String类型的值,则会生成一个新的String对象,对之前的没有影响。

    2、深克隆

    我们让School类实现Cloneable接口,重写其clone方法,然后修改Student类的clone方法

    @Override
    protected Student clone() throws CloneNotSupportedException {
        Student newStudent = (Student)super.clone();
        newStudent.school = newStudent.school.clone();
        return newStudent;
    }
    

    结果

    Student@4554617c
    Student@74a14482
    School@1540e19d
    School@677327b6
    北京大学
    清华大学
    

    这时候School类已经不相同了

    一、Serializable接口

    Serializable 是 java.io 包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。Serializable 序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。实现了 Serializable 接口的类可以被 ObjectOutputStream 转换为字节流,同时也可以通过 ObjectInputStream 再将其解析为对象。我们可以在需要写入文件、写入数据库,进行网络传输时使用这个接口
    创建需要被序列化的实体类

    // 省略构造方法,toString方法
    class Student implements Serializable {
        private String name;
        private int age;
    

    测试类

    public class Test {
        public static void main(String[] args) {
            Student student = new Student("lisa", 22);
            writeObject(student);
            Student student1 = readObject();
            System.out.println(student1);
        }
    	// 将类写入文件
        public static void writeObject(Student student){
            ObjectOutputStream oos;
            try {
                oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
                oos.writeObject(student);
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    	// 从文件中读取类
        public static Student readObject(){
            Student student = null;
            ObjectInputStream ois = null;
            try {
                ois = new ObjectInputStream(new FileInputStream("1.txt"));
                student = (Student) ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException classNotFoundException) {
                classNotFoundException.printStackTrace();
            }
            return student;
        }
    }
    

    结果

    Student{name='lisa', age=22}
    
    展开全文
  • Java中Cloneable接口

    千次阅读 2013-07-18 10:59:46
    原文地址:Java中Cloneable接口作者:我有一个梦 目录 预备知识 为什么要clone Object的clone以及为什么如此实现 如何clone 对clone的态度 其他的选择 和Serializable的比较 性能 预备知识 为了...
  • Java中的深拷贝(深复制)和浅拷贝(浅复制) 深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言,若不弄懂,则会在delete的时候出问题,但是我们在这幸好用的是Java。虽然java自动管理对象的...
  • Java中Cloneable 和 clone()的总结和使用

    万次阅读 2015-08-07 13:10:02
    一.Cloneable 的用途Cloneable和Serializable一样都是标记型接口,它们内部都没有方法和属性,implements Cloneable表示该对象能被克隆,能使用Object.clone()方法。如果没有implements Cloneable的类调用Object....
  • Cloneable接口是一个标记接口,也就是没有任何内容,定义如下:这里分析一下这个接口的用法,clone方法是在Object种定义的,而且是protected型的,只有实现了这个接口,才可以在该类的实例上调用clone方法,否则会抛出...
  • 1. 克隆的用处在日常编码我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象。如果使用单纯的引用赋值,会发生什么效果呢?我们可以观察...
  • 1. Cloneable接口的作用 Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出...
  • java-Cloneable接口

    千次阅读 2018-07-02 17:31:30
    一个类实现了Cloneable接口 指向@link java.lang.Object#clone()} 方法是合法的 使得一个 field-for-field copy的类的实例的拷贝 在不实现Cloneable接口的对象上调用Object的clone方法 会导致...
  • Java中Cloneable接口的用法

    千次阅读 2013-08-22 15:44:18
    Java中Cloneable接口的用法 今天编写一个用到Cloneable接口的程序时才注意到原来clone()方法提供的是浅层拷贝,而不是我先前认为的深层拷贝。 class A implements Cloneable{  int a=1;  ArrayList b=...
  • 1、实现Cloneable接口表明该类的对象是允许克隆的。 2、允许克隆的意思是:可以调用clone()方法。 3、深拷贝还是浅拷贝,取决于如何重写Object的clone()方法。 4、原对象和克隆对象的关系: 深拷贝:阳关道和独...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 71,421
精华内容 28,568
关键字:

java中cloneable

java 订阅