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

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

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

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

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

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

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

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

可是,有些状况下这样作是能够接受的,甚至是必须的,好比,使用一些旧的库,与硬件、操做系统进app

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

,必须继承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的对象写出(即保存其状态),要恢复的话则用输入流。
二、串行化的特色:
    (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; }; 
}

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

相关文章
相关标签/搜索