一、假克隆
1、假克隆是 直接给地址
2、上代码
(1)员工类 Employee:有name、age两个属性,并重写toString()
1 2 3 4 5 6 7 8 9 10 11 12 | public class Employee { //员工的name private String name; //员工的age private int age;
//省略get、set方法 @Override public String toString() { return "姓名:" + name + ",年龄:" + age; } } |
(2)测试类 Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class Test { public static void main(String[] args) { System.out.println("克隆前:"); Employee employee = new Employee(); employee.setName("张三"); employee.setAge(20); System.out.println(employee.toString());
System.out.println("克隆后:"); Employee employee2 = employee; employee2.setName("李四"); employee2.setAge(22); System.out.println("employee:"+employee); System.out.println("employee2:"+employee2);
System.out.println("___________________________________"); System.out.println("x.clone() != x --->" + (employee != employee2)); System.out.println("x.clone().getClass() == x.getClass() -->"+(employee.getClass() == employee2.getClass())); System.out.println("x.clone().equals(x) --->" + employee.equals(employee2)); } } |
(3)运行结果:
|
二、浅克隆
1、浅克隆 通过Cloneable 的 clone 方法实现克隆
2、特点:
(1)如果对象成员是基本类型,浅克隆即可完成,如果对象的成员变量包含
引用类型,就会直接复制对象地址
(2)当然如果引用类型是不可变的,如String类型,则可以
3、上代码:
(1)员工类 Employee:有name、age、Address 三个属性,其中
Address 是引用类型,并重写toString(),实现 clone 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class Employee implements Cloneable { //员工的name private String name; //员工的age private int age; //地址 private Address address;
public Employee(String name, int age, Address address) { super(); this.name = name; this.age = age; this.address = address; }
//省略get、set方法
@Override public String toString() { return "姓名:" + name + ",年龄:" + age + ",地址:" + address; }
//clone实现克隆 @Override protected Employee clone() throws CloneNotSupportedException { return (Employee) super.clone(); } } |
(2)地址类 Address ,有国家、省、城市 三个属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Address { private String state; private String province; private String city;
public Address(String state, String province, String city) { super(); this.state = state; this.province = province; this.city = city; } //省略get、set @Override public String toString() { return "Address [state=" + state + ", province=" + province + ", city=" + city + "]"; }
} |
(3)测试类 Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Test { public static void main(String[] args) throws CloneNotSupportedException { System.out.println("克隆前:"); Address address = new Address("中国", "吉林", "长春"); Employee employee = new Employee("张三", 30, address); System.out.println(employee.toString());
System.out.println("克隆后:"); Employee employee2 = employee.clone(); employee2.setName("李四"); employee2.setAge(22); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); System.out.println("employee:" + employee); System.out.println("employee2:" + employee2);
System.out.println("___________________________________"); System.out.println("x.clone() != x --->" + (employee != employee2)); System.out.println("x.clone().getClass() == x.getClass() -->"+(employee.getClass() == employee2.getClass())); System.out.println("x.clone().equals(x) --->" + employee.equals(employee2)); } } |
(4)运行结果:
|
(5)关于 Cloneable 的 clone(),Cloneable接口是一个空的标志接口,其
实现是调用 Object 的 clone(),因为这是一个 native 方法,怎么实现不清楚,源码如下:
1 2 3 4 5 6 7 | * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The general * intent is that, for any object {@code x}, the expression: * x.clone() != x will be true, * x.clone().getClass() == x.getClass() will be {@code true}, * x.clone().equals(x) will be {@code true}, protected native Object clone() throws CloneNotSupportedException; |
三、深克隆
1、通过对可变引用类型实现 Cloneable 的 clone()方法
2、如果引用类型中还有可变的引用类型成员变量,则它也要进行克隆
3、如果类的成员变量比较复杂,例如使用了多个可变引用类型,使用 clone() 方
法来克隆是非常麻烦的。
4、代码实现:
(1)员工类 Employee:有name、age、Address 三个属性,其中
Address 是引用类型,并重写toString(),实现 clone 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class Employee implements Cloneable { //员工的name private String name; //员工的age private int age; //地址 private Address address;
public Employee(String name, int age, Address address) { super(); this.name = name; this.age = age; this.address = address; } //省略,,,, @Override public String toString() { return "姓名:" + name + ",年龄:" + age + ",地址:" + address; }
//实现浅克隆 @Override protected Employee clone() throws CloneNotSupportedException { Employee employee = (Employee) super.clone(); employee.address = address.clone(); return employee; } } |
(2)地址类 Address ,有国家、省、城市 三个属性,并实现 clone()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Address implements Cloneable { private String state; private String province; private String city;
public Address(String state, String province, String city) { super(); this.state = state; this.province = province; this.city = city; } //。。。。。 @Override protected Address clone() throws CloneNotSupportedException { return (Address) super.clone(); }
@Override public String toString() { return "Address [state=" + state + ", province=" + province + ", city=" + city + "]"; } } |
(3)测试类 Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Test { public static void main(String[] args) throws CloneNotSupportedException { System.out.println("克隆前:"); Address address = new Address("中国", "吉林", "长春"); Employee employee = new Employee("张三", 30, address); System.out.println(employee.toString());
System.out.println("克隆后:"); Employee employee2 = employee.clone(); employee2.setName("李四"); employee2.setAge(22); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); System.out.println("employee:" + employee); System.out.println("employee2:" + employee2);
System.out.println("___________________________________"); System.out.println("x.clone() != x --->" + (employee != employee2)); System.out.println("x.clone().getClass() == x.getClass() -->"+(employee.getClass() == employee2.getClass())); System.out.println("x.clone().equals(x) --->" + employee.equals(employee2)); } } |
(4)测试结果
四、序列化
1、通过实现Serializable接口,该接口没有定义任何方法,是一个标志接口
2、如果类中有引用类型成员变量,则该变量也需要实现 Serializable 接口,依次类
推,当然序列化到本地和内存都可以
3、代码实现:
(1)Employee 还是员工类,实现Serializable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class Employee implements Serializable { private static final long serialVersionUID = 1L; //员工的name private String name; //员工的age private int age; //地址 private Address address;
public Employee(String name, int age, Address address) { super(); this.name = name; this.age = age; this.address = address; } //省略,,,, @Override public String toString() { return "姓名:" + name + ",年龄:" + age + ",地址:" + address; } } |
(2)Address 还是地址类,实现Serializable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Address implements Serializable { private static final long serialVersionUID = 1L; private String state; private String province; private String city;
public Address(String state, String province, String city) { super(); this.state = state; this.province = province; this.city = city; } //省略。。。。 @Override public String toString() { return "Address [state=" + state + ", province=" + province + ", city=" + city + "]"; } } |
(3)测试类 Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class Test { public static void main(String[] args) throws CloneNotSupportedException { System.out.println("序列化之前:"); Address address = new Address("中国", "吉林", "长春"); Employee employee = new Employee("张三", 30, address); System.out.println(employee.toString());
System.out.println("序列化之后:"); Employee employee2 = null; //序列化到本地 try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:/test/employee.txt"))); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/test/employee.txt")))) { oos.writeObject(employee); employee2 = (Employee) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } if(employee2 == null) return; employee2.setName("李四"); employee2.setAge(22); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); System.out.println("employee:" + employee); System.out.println("employee2:" + employee2);
System.out.println("___________________________________"); System.out.println("x.clone() != x --->" + (employee != employee2)); System.out.println("x.clone().getClass() == x.getClass() -->"+(employee.getClass() == employee2.getClass())); System.out.println("x.clone().equals(x) --->" + employee.equals(employee2)); } } |
(4)运行结果
|
五、序列化和深克隆效率比较:
1、本次实例对 深克隆 和 序列化克隆 方式进行比较,分别使用这两种方式创建 100000 个对象
2、通过比较发现深克隆所花费的时间远远小于序列化,与序列化每次开销很大有关系
3、代码实现:
(1)Employee 还是员工类,实现Serializable、Cloneable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Employee implements Serializable, Cloneable { //员工的name private String name; //员工的age private int age;
public Employee(String name, int age) { super(); this.name = name; this.age = age; }
//省略,,, @Override public String toString() { return "姓名:" + name + ",年龄:" + age; } @Override protected Employee clone() throws CloneNotSupportedException { return (Employee)super.clone(); } } |
(2)测试类 Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class Test { public static void main(String[] args) throws CloneNotSupportedException { //深克隆测试 List<Employee> employees = new ArrayList<>(); Employee employee = new Employee("王五", 25); long startTime = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { employees.add(employee.clone()); } System.out.println("深克隆花费的时间:" + (System.currentTimeMillis() - startTime));
//序列化测试 List<Employee> employees2 = new ArrayList<>(); startTime = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { //保存于内存中 try(ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); ) { oos.writeObject(employee); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); employees2.add((Employee)ois.readObject()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } System.out.println("序列化花费的时间:"+ (System.currentTimeMillis() - startTime)); } } |
(3)运行结果
观看《Java经典编程300例》所写