1. 拷贝
在 java 拷贝一个对象 时,引用为同一内存地址,当改变一个对象时会对原来对象也会产生影响
/**
* 对象拷贝
*/
private static void test1() {
Car car =new Car("奔驰", 3.16, "黑色",45.6);
Car copyCar=car; //简单的对象复制
System.out.println("复制完成后内容----------------------------------");
System.out.println(car.getBrand()+"=="+car.getCarLength()+"=="
+car.getColor()+"=="+car.getPrice());
System.out.println(copyCar.getBrand()+"=="+copyCar.getCarLength()+"=="
+copyCar.getColor()+"=="+copyCar.getPrice());
//改变复制对象的内容
copyCar.setBrand("奔驰c200");
copyCar.setPrice(30.5);
System.out.println("修改后内容--------------------------------------");
System.out.println(car.getBrand()+"=="+car.getCarLength()+"=="
+car.getColor()+"=="+car.getPrice());
System.out.println(copyCar.getBrand()+"=="+copyCar.getCarLength()+"=="
+copyCar.getColor()+"=="+copyCar.getPrice());
}
测验结果:
复制完成后内容----------------------------------
奔驰==3.16==黑色==45.6
奔驰==3.16==黑色==45.6
修改后内容--------------------------------------
奔驰c200==3.16==黑色==30.5
奔驰c200==3.16==黑色==30.5
经上测试后发现拷贝发现两个对象还互相干扰,那怎么可以让两个对象拥有自己独立的状态呢?
2. 浅克隆
对象克隆后,双方拥有自己独立的状态,修改后不会互相干扰,这种方式叫做克隆,Object中有一个proteced 的Clone方法,通过重写此方法达到浅克隆。
浅克隆:如果对象的所有属性都属于基本类型或者不可变类,通过浅克隆可以达到双方对象独立。
实现方式如下代码:
1.在Car实体类中实现cloneable接口(标记接口),重写clone方法,修改返回类型,调用super.clone(),修改方法的访问修饰符为public(以便在其他类中可以访问);
public class Car implements Cloneable{
private String brand; //汽车品牌
private double carLength; //汽车车长
private String color; //汽车颜色
private double price; //汽车价格
/*省略getting/setting 和构造方法*/
/**
* 重写Object类的克隆方法,实现浅克隆
*/
@Override
public Car clone() throws CloneNotSupportedException {
return (Car)super.clone();
}
}
2.测试代码:
/**
* 浅克隆
*/
private static void test2() {
Car car =new Car("奔驰", 3.16, "黑色",45.6);
try {
Car cloneCar=car.clone(); //通过clone实现克隆
System.out.println("克隆后内容--------------------------------");
System.out.println(car.getBrand()+"=="+car.getCarLength()+"=="
+car.getColor()+"=="+car.getPrice());
System.out.println(cloneCar.getBrand()+"=="+cloneCar.getCarLength()+"=="
+cloneCar.getColor()+"=="+cloneCar.getPrice());
//修改克隆数据内容
cloneCar.setBrand("奔驰c200");
cloneCar.setCarLength(2.89);
System.out.println("修改后结果---------------------------------");
System.out.println(car.getBrand()+"=="+car.getCarLength()+"=="
+car.getColor()+"=="+car.getPrice());
System.out.println(cloneCar.getBrand()+"=="+cloneCar.getCarLength()+"=="
+cloneCar.getColor()+"=="+cloneCar.getPrice());
} catch (CloneNotSupportedException e) {
System.out.print("对象克隆出现异常,异常信息:");
e.printStackTrace();
}
}
测试结果:
克隆后内容--------------------------------
奔驰 == 3.16 == 黑色 == 45.6
奔驰 == 3.16 == 黑色 == 45.6
修改后结果---------------------------------
奔驰 == 3.16 == 黑色 == 45.6
奔驰c200 == 2.89 == 黑色 == 45.6
3.浅克隆图例:
从上图可以看出浅克隆会复制所有数据一份,从新生成一个对象,双方拥有独立的空间,修改不会干扰,但是引用类型String ,复制的却是地址,改变会修改原数据吗?答案是不会,因为String 类型属于不可变类型(用final 修饰),内容改变后会重新开辟内存空间,指向新的引用。但是自己创建的类呢???
3.深克隆
给Car 类增加 private Date date; //出厂日期 属性,在Test 类中 调用 date.setTime(),修改内容,发现两个数据对象都修改啦,这是因为复制的只是地址,我们现在通过深克隆,达到互不干扰的效果。
1.添加对Date 的克隆
/**
* 重写Object类的克隆方法,实现浅克隆
*/
@Override
public Car clone() throws CloneNotSupportedException {
Car car = (Car)super.clone();
//增加Date 类型的深克隆
car.date =(Date)date.clone(); //Date 类中默认重写了clone方法
return car;
}
2.调试Debug后发现克隆后Date 的引入地址不一样啦
3.对自定义类的克隆
对自定义类实现Cloneable,重写clone方法,重复以上步骤
如果类引用多层的话,每次都重写类的clone方法,会很累,下来我们就用 java 序列化实现深克隆
4. JAVA 序列化 -- 深克隆
1. 克隆的类要实现 Serializable 接口(标记接口),类有引用的自定义类也要实现此接口。重写clone方法、
/**
* 员工信息类
* @author john
*
*/
public class Employee implements Serializable {
/**
* 序列化id
*/
private static final long serialVersionUID = 1L;
private String name; //员工姓名
private int age; //员工年龄
private Dept dept; //员工部门
private Date date; //入职时间
/*省略getting/setting 和构造方法*/
/**
* java 序列化实现深克隆
*/
@Override
public Employee clone(){
Employee emp=null;
//对象序列化
ByteArrayOutputStream out=new ByteArrayOutputStream();
try {
ObjectOutputStream outStream=new ObjectOutputStream(out);
outStream.writeObject(this);
//反序列化
ByteArrayInputStream inp=new ByteArrayInputStream(out.toByteArray());
ObjectInputStream inputStream=new ObjectInputStream(inp);
emp=(Employee)inputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return emp;
}
}
2.测试代码
/**
* Java 序列化实现深克隆
*/
private static void test4() {
Dept dept=new Dept("001", "营销部", new Date());
Employee emp=new Employee("张三", 20, dept, new Date());
Employee cloneEmp=emp.clone();
System.out.println("克隆后内容---------------------------------");
System.out.println(emp.getName()+"=="+emp.getAge()+"=="+emp.getDate()
+"=="+emp.getDept().getId()+"=="+emp.getDept().getDeptName()+"=="+emp.getDept().getDate());
System.out.println(cloneEmp.getName()+"=="+cloneEmp.getAge()+"=="+cloneEmp.getDate()
+"=="+cloneEmp.getDept().getId()+"=="+cloneEmp.getDept().getDeptName()+"=="+cloneEmp.getDept().getDate());
//修改克隆对象内容
cloneEmp.setAge(30);
cloneEmp.setName("李四");
cloneEmp.getDate().setTime(new GregorianCalendar(2016,8,9).getTimeInMillis());
cloneEmp.getDept().setDeptName("开发部");
cloneEmp.getDept().getDate().setTime(new GregorianCalendar(2015,8,9).getTimeInMillis());
System.out.println("修改后内容---------------------------------");
System.out.println(emp.getName()+"=="+emp.getAge()+"=="+emp.getDate()
+"=="+emp.getDept().getId()+"=="+emp.getDept().getDeptName()+"=="+emp.getDept().getDate());
System.out.println(cloneEmp.getName()+"=="+cloneEmp.getAge()+"=="+cloneEmp.getDate()
+"=="+cloneEmp.getDept().getId()+"=="+cloneEmp.getDept().getDeptName()+"=="+cloneEmp.getDept().getDate());
}
3.测试结果
克隆后内容---------------------------------
张三==20==Wed Apr 26 15:13:23 CST 2017==001==营销部==Wed Apr 26 15:13:19 CST 2017
张三==20==Wed Apr 26 15:13:23 CST 2017==001==营销部==Wed Apr 26 15:13:19 CST 2017
修改后内容---------------------------------
张三==20==Wed Apr 26 15:13:23 CST 2017==001==营销部==Wed Apr 26 15:13:19 CST 2017
李四==30==Fri Sep 09 00:00:00 CST 2016==001==开发部==Wed Sep 09 00:00:00 CST 2015
4.debug 下
至此,Java克隆完成!
github代码地址:https://github.com/kedouC/exampleClone.git