创建对象的方式
1、调用new语句创建对象,最常见的一种 2、运用反射手段创建对象,调用java.lang.Class 或者 java.lang.reflect.Constructor 类的newInstance()实例方 3、调用对象的clone()方法 4、使用反序列化,调用java.io.ObjectInputStream 对象的 readObject()方法.
将一个对象的引用复制给另外一个对象的方法
1、直接赋值 2、浅拷贝 3、深拷贝
直接赋值
实体类Person.java
public class Person { //姓名 private String name; // 年龄 private int age; // 编号 private Integer code;
public Person(){
}
public Person(String name, int age, Integer code) { this.name = name; this.age = age; this.code = code; }
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 Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
}测试类cloneDemo.java 本篇所有测试均会在该类下完成
public class cloneDemo { public static void main(String[] args) { Person p = new Person("小明",11,123); Person p1 = p; System.out.println(p); System.out.println(p1); }}打印结果:
Others.base.cloneDemo.Person@4554617cOthers.base.cloneDemo.Person@4554617c两者打印出来的地址相同,可见二者的引用是同一个对象,并没有创建出一个新的对象。可以把这种现象叫做引用的复制(或引用拷贝)。 对Person.java追加toString方法,
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", code=" + code + '}'; }cloneDemo.java中追加
p1.setCode(1234); System.out.println("--------windcoder.com----------"); System.out.println(p); System.out.println(p1);从打印结果中更能体现出这一点,因为是同一个对象的引用,所以两者改一个,另一个对象的值也随之改变:
Person{name='小明', age=11, code=123}Person{name='小明', age=11, code=123}--------windcoder.com----------Person{name='小明', age=11, code=1234}Person{name='小明', age=11, code=1234}浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的就是基本类型的值; 如果属性是内存地址(引用类型),拷贝的就是内存地址(即复制引用但不复制引用的对象) ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。 实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。 下面改造Person.java
/** * 实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法 */public class Person implements Cloneable{ //姓名 private String name; // 年龄 private int age; // 编号 private Integer code;
public Person(){
}
public Person(String name, int age, Integer code) { this.name = name; this.age = age; this.code = code; }
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 Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", code=" + code + '}'; }
/** * 覆写clone()方法 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}cloneDemo.java中追加
Person p2 = p; p2.setName("小明克隆人"); p2.setAge(12); p2.setCode(12345); System.out.println(p); System.out.println(p2); System.out.println("--------windcoder.com----------");得到结果:
Person{name='小明', age=11, code=1234}Person{name='小明克隆人', age=12, code=12345}--------windcoder.com----------此时p2完成了一次拷贝。现在继续改造Person,添加一个Address对象。 Address.java
public class Address { private String name;
public Address( ) {
}
public Address(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; }}Person.java中添加对应属性
private Address address;public Address getAddress() { return address;}
public void setAddress(Address address) { this.address = address;}// 修改toString,追加addresspublic String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", code=" + code + ", address=" + address + '}';}cloneDemo.java追加如下:
p.setAddress(new Address("我是一个小学生"));Person p2 = (Person)p.clone();System.out.println(p);System.out.println(p2);System.out.println("--------windcoder.com----------");p2.setName("小明克隆人");p2.setAge(12);p2.setCode(12345);p2.getAddress().setName("我是一个小学生克隆人");System.out.println(p);System.out.println(p2);System.out.println("--------windcoder.com----------");我们预期是能够获得p还是原来的address,p2的address发生改变,但结果如何呢,打印发现两者都发生了改变:
--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生克隆人'}}Person{name='小明克隆人', age=12, code=12345, address=Student{name='我是一个小学生克隆人'}}--------windcoder.com----------这是由于,此时发生的仅是浅拷贝,即被拷贝对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。针对基本类型及其封装类都是将对应的基本类型值拷贝,对于其余对象,则仅是拷贝其引用,这导致最终里面的对象是同一个,更改一个,另一个的拷贝/原对象的对应值也随之更改。
深拷贝
如果想将上述的address也单独复制一份,这就用到了深拷贝。 被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。 通俗的说,如果说浅拷贝,开始的时候是两条线,如果在最后有一个其他类的变量,那么这两条线最后会合二为一,共同指向这变量,都能对他进行操作。深拷贝则是完完全全的两条线,互不干涉,因为他已经把所有的内部中的变量的对象全都复制一遍了。 深拷贝在代码中,需要在clone方法中多书写调用这个类中其他类的变量的clone函数。
改造方案
Address.java 实现Cloneable接口并重写clone
public class Address implements Cloneable{ private String name;
public Address( ) {
}
public Address(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; }
/** * 覆写clone()方法 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }}Person.java修改clone方法:
protected Object clone() { try { // super.clone(); // return super.clone();
// 改为深复制: Person newPerson = (Person)super.clone(); // 拷贝一份新的address newPerson.address = (Address) address.clone(); return newPerson; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }此时再运行之前的cloneDemo.java,发现结果已经是预期的效果:
--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}Person{name='小明克隆人', age=12, code=12345, address=Student{name='我是一个小学生克隆人'}}--------windcoder.com----------拓展:利用串行化来做深复制
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。
public Object deepClone() { //写入对象 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); //读取对象 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject());}改造示例
Address追加Serializable接口实现
public class Address implements Serializable, Cloneable{ .....}Person追加Serializable接口实现
public class Person implements Serializable, Cloneable{ ...... /** * 利用串行化来做深复制 * by: windCoder.com * @return */ public Object deepClone() {
try { //写入对象 ByteArrayOutputStream bo= new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bo); os.writeObject(this); //读取对象 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }}cloneDemo.java追加:
Person p3 = (Person)p.deepClone();System.out.println(p);System.out.println(p3);System.out.println("--------windcoder.com----------");p3.setName("小明克隆人");p3.setAge(12);p3.setCode(12345);p3.getAddress().setName("我是一个小学生克隆人233333");System.out.println(p);System.out.println(p3);System.out.println("--------windcoder.com----------");运行结果:
--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}--------windcoder.com----------Person{name='小明', age=11, code=1234, address=Student{name='我是一个小学生'}}Person{name='小明克隆人', age=12, code=12345, address=Student{name='我是一个小学生克隆人233333'}}--------windcoder.com----------注:可以使用transient关键字,修饰不需要序列化的属性。当序列化对象的时候,这个属性就不会序列化到指定的目的地中。

评论