码迷,mamicode.com
首页 > 其他好文 > 详细

对象的复制(clone、序列化)

时间:2015-02-13 18:45:21      阅读:163      评论:0      收藏:0      [点我收藏+]

标签:

那就先看是clone方法复制对象  摘自 java.lang.Object.clone()分析

首先,看一下源码:
public class Object  {
    protected native Object clone() throws CloneNotSupportedException;
}
  由源代码我们会发现:
  第一:Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于Java中的

非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的

信息复制到新对象中,虽然这也实现了clone功能。(JNI是Java Native Interface的 缩写。从Java

1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的

代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他

语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。

但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进

行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。)
  第二:Object类中的 clone()方法被protected修饰符修饰。这也意味着如果要应用 clone()方 法

,必须继承Object类,在 Java中所有的类是缺省继承 Object类的,也就不用关心这点了。然后重载

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

clone()方法的属性设置为 public。
  第三:Object.clone()方法返回一个Object对象。我们必须进行强制类型转换才能得到我们需要的

类型。
  浅层复制与深层复制概念:
  浅层复制: 被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用

仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象(对象属性、数据),而不复制它所引用

的对象(对象地址)。
  深层复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。

那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复

制要复制的对象引用的对象(对象属性、数据)都复制一遍。
  Java中对象的克隆
  1)在派生类中实现Cloneable借口。
  2)为了获取对象的一份拷贝,我们可以利用Object类的clone方法。
  3)在派生类中覆盖积累的clone方法,声明为public。
  4)在派生类的clone方法中,调用super.clone()。
实现Cloneable接口
  首先,看一下源码:  
1 public interface Cloneable {
2 }
  我们奇怪的发现Cloneable竟然是空的,那么我们为什么要实现Cloneable接口呢?其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。

public class Person {
  private String name;
  private int age;
  public Person(){}
  public Person(String name,int age){
   this.name=name;
   this.age=age;
  }
  public Object clone(){
   Object o=null;
   try {
    o=super.clone();
   } catch (CloneNotSupportedException e) {
    e.printStackTrace();
   }
   return o;
  }
  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 static void main(String[] args) {
   Person p1=new Person("zhangsan",18);
   Person p2=(Person)p1.clone();
   p2.setName("lis");
   p2.setAge(20);
   System.out.println("name="+p1.getName()+",age="+p1.getAge());
   System.out.println("name="+p2.getName()+",age="+p2.getAge());
   //修改p2后,没有对p1产生影响。上面输出为:
   //name=zhangsan,age=18
   //name=lis,age=20
  }
 }

上面的代码就可以看出来这个简单的clone是仅仅复制了对象的属性及数据,不是存储对象的索引。所以clone之后的对象与之前的没有联系了,不属于同一个对象。

说明:
  1)为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
 可能会有一个情况,你的这个类没有实现Cloneable接口,却也可以写这个方法。那是以为Object是所有对象的父类(包括新建的),所以可以覆盖它的clone方法,但是由于少了Cloneable这个标识,它就会报错,进入CloneNotSupportedException 异常里面
  2)继承自java.lang.Object.clone()方法是浅层复制。以下代码可以证明之:

public class Student implements Cloneable {
 private String name;
 private int age;
 private Professor pro;
 public Student(){}
 public Student(String name,int age,Professor pro){
  this.name=name;
  this.age=age;
  this.pro=pro;
 }
 public Object clone(){
  Object o=null;
  try {
   //Object中的clone()识别出你要复制的是哪一个对象。
   o=super.clone();
  } catch (CloneNotSupportedException e) {
   System.out.println(e.toString());
  }
   return o;
 }
 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 Professor getPro() {
  return pro;
 }
 public void setPro(Professor pro) {
  this.pro = pro;
 }
 public static void main(String[] args) {
     Professor p=new Professor("wangwu",50);
     Student s1=new Student("zhangsan",18,p);
     Student s2=(Student)s1.clone();
     s2.getPro().setName("maer");
     s2.getPro().setAge(40);
     System.out.println("name="+s1.getPro().getName()+",age="+s1.getPro().getAge());
     //name=maer,age=40
     System.out.println("name="+s2.getPro().getName()+",age="+s2.getPro().getAge());
     //name=maer,age=40
 }
}
class Professor{
 private String name;
 private int age;
 public Professor(){}
 public Professor(String name,int age){
  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;
 }
}

我们会发现,在外层的对象是发生了复制,但是里层的没有。这时就要进行深层clone啦

public class Student implements Cloneable {
private String name;
private int age;
private Professor pro;
public Student(){}
public Student(String name,int age,Professor pro){
  this.name=name;
  this.age=age;
  this.pro=pro;
}
public Object clone(){
  Object o=null;
  try {
   //Object中的clone()识别出你要复制的是哪一个对象。
   o=super.clone();
  } catch (CloneNotSupportedException e) {
   System.out.println(e.toString());
  }
   o.pro=(Professor)pro.clone();
   return o;
}
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 Professor getPro() {
  return pro;
}
public void setPro(Professor pro) {
  this.pro = pro;
}
public static void main(String[] args) {
     Professor p=new Professor("wangwu",50);
     Student s1=new Student("zhangsan",18,p);
     Student s2=(Student)s1.clone();
     s2.getPro().setName("maer");
     s2.getPro().setAge(40);
     System.out.println("name="+s1.getPro().getName()+",age="+s1.getPro().getAge());
     //name=wang,age=50
    System.out.println("name="+s2.getPro().getName()+",age="+s2.getPro().getAge());
     //name=maer,age=40
}
}
class Professor implements Cloneable{
private String name;
private int age;
public Professor(){}
public Professor(String name,int age){
  this.name=name;
  this.age=age;
}
public Object clone(){
  Object o=null;
  try {
    o=super.clone();
  } catch (CloneNotSupportedException e) {
    e.printStackTrace();
  }
  return o;
}
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;
}
}

这样就达到效果了,不过可以看仔细哦。1:作为对象属性的对象(上面的Professor)需要实现Cloneable接口,并且实现Clone方法;2:含有对象属性的对象(上面的Student)的clone方法里面在返回之前记得对对象属性进行clone赋值给该对象属性(o.pro=(Professor)pro.clone();)

并且由此可以推出一个结论:一个对象里面含有对象属性的话,对象属性也需要实现Cloneable接口及clone方法,并且在对象里面赋值与对象的对象属性。当对象属性也含有对象属性2的时候,对象属性2也需要实现Cloneable接口及clone方法,并且在对象属性(第一个)里面赋值对象属性(第一个)的对象属性2(第二个)--这个推论已经验证过,这儿时间紧迫就不写了

下面开始序列化复制对象

先讲讲什么是序列化吧(这个之前不太清楚,所以就看看,明白了自动略过,看下面)--转自 什么是java序列化,如何实现java序列化?  要对序列化更加深入可以看 Java序列化的机制和原理 Java序列化与反序列化 深入理解Java对象序列化

Java 串行化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里。Java的串行化机制是RMI、EJB等技术的技术基础。用途:利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
2、串行化的特点:
    (1)如果某个类能够被串行化,其子类也可以被串行化。如果该类有父类,则分两种情况来考虑,如果该父类已经实现了可串行化接口。则其父类的相应字段及属性的处理和该类相同;如果该类的父类没有实现可串行化接口,则该类的父类所有的字段属性将不会串行化。
  (2)声明为static和transient类型的成员数据不能被串行化。因为static代表类的状态, transient代表对象的临时数据;
  (3)相关的类和接口:在java.io包中提供的涉及对象的串行化的类与接口有ObjectOutput接口、ObjectOutputStream类、ObjectInput接口、ObjectInputStream类。
    (1)ObjectOutput接口:它继承DataOutput接口并且支持对象的串行化,其内的writeObject()方法实现存储一个对象。ObjectInput接口:它继承DataInput接口并且支持对象的串行化,其内的readObject()方法实现读取一个对象。
    (2)ObjectOutputStream类:它继承OutputStream类并且实现ObjectOutput接口。利用该类来实现将对象存储(调用ObjectOutput接口中的writeObject()方法)。ObjectInputStream类:它继承InputStream类并且实现ObjectInput接口。利用该类来实现读取一个对象(调用ObjectInput接口中的readObject()方法)。
  对于父类的处理,如果父类没有实现串行化接口,则其必须有默认的构造函数(即没有参数的构造函数)。否则编译的时候就会报错。在反串行化的时候,默认构造函数会被调用。但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。这是为什么呢?这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。
import java.io.*;
public class Cat implements Serializable {
        private String name;
        public Cat () {
                this.name = "new cat";
        }
        public String getName() {
                return this.name;
        }
        public void setName(String name) {
                this.name = name;
        }
        public static void main(String[] args) {        
                Cat cat = new Cat();
                try {
                        FileOutputStream fos = new FileOutputStream("catDemo.out");
                        ObjectOutputStream oos = new ObjectOutputStream(fos);
                        System.out.println(" 1> " + cat.getName());
                        cat.setName("My Cat");                      
                        oos.writeObject(cat);
                        oos.close();                      
                } catch (Exception ex) {  ex.printStackTrace();   }
                try {
                        FileInputStream fis = new FileInputStream("catDemo.out");
                        ObjectInputStream ois = new ObjectInputStream(fis);
                        cat = (Cat) ois.readObject();
                        System.out.println(" 2> " + cat.getName());
                        ois.close();
                } catch (Exception ex) {
                        ex.printStackTrace();
                }
        }
}//writeObject和readObject本身就是线程安全的,传输过程中是不允许被并发访问的。所以对象能一个一个接连不断的传过来

 ================开始复制相关序列化========================

这个地方的序列化需要用用到 ByteArrayOutputStream、ObjectOutputStream 、ByteArrayInputStream、ObjectInputStream

class TestCC implements Serializable{ 
 private int id; 
 private String name; 
 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 TestCC deepClone() throws IOException{
  ByteArrayOutputStream bos=new ByteArrayOutputStream();
  ObjectOutputStream oot=new ObjectOutputStream(bos);
  oot.writeObject(this);
  oot.flush();
  oot.close();
  ByteArrayInputStream bas=new ByteArrayInputStream(bos.toByteArray());
  ObjectInputStream ois=new ObjectInputStream(bas);
  TestCC readObject = null;
  try {
   readObject = (TestCC)ois.readObject();
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return readObject;
 }
 @Override 
 public String toString() {  
  return "TestCC [id=" + id + ", name=" + name + "]"; 
 } 
 public TestCC(){}; 
 public TestCC(int id,String name){  this.id=id;  this.name=name; }; 
}

字数超出最大允许值,服务器可能拒绝保存---这个字数超标--方法就是这样,经测试正确

对象的复制(clone、序列化)

标签:

原文地址:http://my.oschina.net/u/2297250/blog/378513

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!