那就先看是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; };
}
字数超出最大允许值,服务器可能拒绝保存---这个字数超标--方法就是这样,经测试正确