经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Java实例 Part6:Java中的克隆
来源:cnblogs  作者:落落free  时间:2018/11/5 11:13:28  对本文有异议

目录

Part6:Java中的克隆


@
***

Example01:Java对象的假克隆

  • 对象的克隆是Java中的一项高级技术,获得与其相同的对象。

??基本数据类型可以使用“=”来进行克隆,此时两个变量除了相等是没有任何关系的。而对于引用类型数据不能简单地使用“=”进行克隆,这与Java的内存空间使用有关。 ??
??Java将内存空间分成两块,即栈和堆。在栈中保存基本类型和引用变量;在堆中保存对象。对于引用变量而言,使用“=”将修改引用,而不是复制堆中的对象。此时两个引用变量将指向同一个对象。因此,如果一个变量对其修改则会改变另一个变量。

运行结果:
在这里插入图片描述
代码实现:

  1. public class Employee {
  2. private String name;
  3. private int age;
  4. //省略set()和get()方法
  5. @Override
  6. public String toString() {
  7. return "Employee{" +
  8. "name='" + name + '\'' +
  9. ", age=" + age +
  10. '}';
  11. }
  12. public static void main(String[] args) {
  13. System.out.println("-----克隆之前:--------");
  14. Employee employee1 = new Employee();
  15. employee1.setName("hyn");
  16. employee1.setAge(20);
  17. System.out.println("员工1的信息:\n"+employee1);
  18. System.out.println("-----克隆之后:--------");
  19. Employee employee2 = employee1; //将employee1赋值给employee2
  20. employee2.setName("azw");
  21. employee2.setAge(21);
  22. System.out.println("员工1的信息:\n"+employee1);
  23. System.out.println("员工2的信息:\n"+employee2);
  24. }
  25. }

Example02:Java对象的浅克隆

??在克隆对象时,如果对象的成员变量是基本数据类型,则使用浅克隆即可完成。如果对象的成员变量包括可变引用类型,则需要深克隆。

运行结果:
在这里插入图片描述
代码实现:

  1. //Address.java
  2. public class Address {
  3. private String state; //所在国家
  4. private String province; //所在省
  5. private String city; //所在城市
  6. public Address(String state, String province, String city) {
  7. this.state = state;
  8. this.province = province;
  9. this.city = city;
  10. }
  11. //省略set()和get()方法
  12. @Override
  13. public String toString() {
  14. StringBuilder sb = new StringBuilder();
  15. sb.append("国家:"+state+",");
  16. sb.append("省:"+province+",");
  17. sb.append("市:"+city);
  18. return sb.toString();
  19. }
  20. }
  21. //Employee.java
  22. public class Employee implements Cloneable{
  23. private String name;
  24. private int age;
  25. private Address address;
  26. public Employee(String name, int age, Address address) {
  27. this.name = name;
  28. this.age = age;
  29. this.address = address;
  30. }
  31. //省略set()和get()方法
  32. @Override
  33. public String toString() {
  34. StringBuilder sb = new StringBuilder();
  35. sb.append("姓名:"+name+",");
  36. sb.append("年龄:"+age+",");
  37. sb.append("\n地址:"+address);
  38. return sb.toString();
  39. }
  40. @Override
  41. public Employee clone() throws CloneNotSupportedException { //实现浅克隆
  42. Employee employee = (Employee) super.clone();
  43. return employee;
  44. }
  45. }

测试代码:

  1. class Test {
  2. public static void main(String[] args) throws CloneNotSupportedException {
  3. System.out.println("*****克隆之前:******");
  4. Address address = new Address("中国", "湖北", "武汉");
  5. Employee employee1 = new Employee("azw", 20, address);
  6. System.out.println("员工1的信息:\n" + employee1); //employee1的信息
  7. System.out.println("*****克隆之后:******");
  8. Employee employee2 = employee1.clone(); //使用克隆创建Employee2
  9. employee2.getAddress().setState("中国"); //修改地址
  10. employee2.getAddress().setProvince("黑龙江");
  11. employee2.getAddress().setCity("哈尔滨");
  12. employee2.setName("hyn");
  13. employee2.setAge(21);
  14. System.out.println("员工1的信息:\n" + employee1);
  15. System.out.println("员工2的信息:\n" + employee2);
  16. }
  17. }
  • 如果引用类型是不可变的,如String类对象,则不必进行深克隆。
    ***

Example03:Java对象的深克隆

  • 如果类的成员变量中包括可变引用类型,则需进行深克隆。

运行结果:
在这里插入图片描述
代码实现:

  1. //Address.java
  2. public class Address implements Cloneable{
  3. private String state; //所在国家
  4. private String province; //所在省
  5. private String city; //所在城市
  6. public Address(String state, String province, String city) {
  7. this.state = state;
  8. this.province = province;
  9. this.city = city;
  10. }
  11. //省略set()和get()方法
  12. @Override
  13. public String toString() {
  14. StringBuilder sb = new StringBuilder();
  15. sb.append("国家:"+state+",");
  16. sb.append("省:"+province+",");
  17. sb.append("市:"+city);
  18. return sb.toString();
  19. }
  20. //---------------------------
  21. @Override
  22. public Address clone() throws CloneNotSupportedException {
  23. //Address类中的域不是基本类型就是不可变类型,所以可以直接使用浅克隆
  24. Address address = (Address) super.clone();
  25. return address;
  26. }
  27. //---------------------------
  28. }
  29. //Employee.java
  30. public class Employee implements Cloneable{
  31. private String name;
  32. private int age;
  33. private Address address;
  34. public Employee(String name, int age, Address address) {
  35. this.name = name;
  36. this.age = age;
  37. this.address = address;
  38. }
  39. //省略set()和get()方法
  40. @Override
  41. public String toString() {
  42. StringBuilder sb = new StringBuilder();
  43. sb.append("姓名:"+name+",");
  44. sb.append("年龄:"+age+",");
  45. sb.append("\n地址:"+address);
  46. return sb.toString();
  47. }
  48. @Override
  49. public Employee clone() throws CloneNotSupportedException { //实现深克隆
  50. Employee employee = (Employee) super.clone();
  51. //---------------------------------
  52. employee.address = address.clone();
  53. //---------------------------------
  54. return employee;
  55. }
  56. }
  57. //测试代码同Example02测试代码.
  • 要点:通常情况下,需要用到克隆对象时都需要使用深克隆。
    ***

Example04:序列化与对象克隆

??如果类的成员变量比较复杂,例如使用了多个可变的引用类型,使用clone()方法是非常麻烦的,所以可以考虑序列化的方式完成克隆。
运行结果:
在这里插入图片描述
代码实现:

  1. import java.io.Serializable;
  2. public class Employee implements Serializable {
  3. //同Example04中Employee.java的代码
  4. }
  5. public class Address implements Serializable {
  6. //同Example04中Assress.java的代码
  7. }

测试代码:

  1. class Test {
  2. public static void main(String[] args) throws IOException, ClassNotFoundException {
  3. System.out.println("*****序列化之前:******");
  4. Address address = new Address("中国", "湖北", "武汉");
  5. Employee employee1 = new Employee("azw", 20, address);
  6. System.out.println("员工1的信息:\n" + employee1); //employee1的信息
  7. System.out.println("*****序列化之后:******");
  8. Employee employee2 = null;
  9. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\employee.txt"));
  10. out.writeObject(employee1); //将对象写入到本地文件中
  11. ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:\\employee.txt"));
  12. employee2 = (Employee)in.readObject(); //从本地文件中读取对象
  13. if (employee2 != null) {
  14. employee2.getAddress().setState("中国"); //修改地址
  15. employee2.getAddress().setProvince("黑龙江");
  16. employee2.getAddress().setCity("哈尔滨");
  17. employee2.setName("hyn");
  18. employee2.setAge(21);
  19. System.out.println("员工1的信息:\n" + employee1);
  20. System.out.println("员工2的信息:\n" + employee2);
  21. }
  22. }
  23. }

要点:进行序列化的类需要实现Serializable接口,该接口中并没有定义任何方法,是一个标识接口。如果类中有可变的引用类型成员变量,则该变量需要实现Serializable接口。本实例采用将对象写入本地文件的方式完成序列化。
***

Example05:深克隆和序列化的效率比较

  • 通过使用这两种方式克隆100000个对象,并输出花费的时间来比较这两种方法的效率。

运行结果:
在这里插入图片描述
代码实现:

  1. import java.io.Serializable;
  2. public class Employee implements Cloneable,Serializable {
  3. private String name;
  4. private int age;
  5. public Employee(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. @Override
  10. public String toString() {
  11. StringBuilder sb = new StringBuilder();
  12. sb.append("姓名:"+name+",");
  13. sb.append("年龄:"+age+",");
  14. return sb.toString();
  15. }
  16. @Override
  17. public Employee clone() throws CloneNotSupportedException { //使用父类的clone()方法实现深克隆
  18. Employee employee = (Employee) super.clone();
  19. return employee;
  20. }
  21. }
  22. 测试代码:
  23. import java.io.*;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. class Test {
  27. public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
  28. List<Employee> employees = new ArrayList<Employee>(); //创建列表保存对象
  29. Employee employee = new Employee("azw", 20); //创建对象
  30. long currentTime = System.currentTimeMillis(); //获得当前系统时间
  31. //使用克隆方式获得对象
  32. for (int i = 0;i<100000;i++){
  33. employees.add(employee.clone());
  34. }
  35. System.out.println("克隆花费的时间:"+(System.currentTimeMillis()-currentTime)+"毫秒");
  36. currentTime = System.currentTimeMillis(); //获得当前系统时间
  37. for (int i = 0;i<100000;i++){
  38. ByteArrayOutputStream bout = new ByteArrayOutputStream(); //创建字节数组输出流
  39. ObjectOutputStream out = new ObjectOutputStream(bout); //创建对象输出流
  40. out.writeObject(employee); //将对象写入到输出流中
  41. //获得字节输出流内容
  42. ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
  43. ObjectInputStream in = new ObjectInputStream(bin); //创建对象输入流
  44. employees.add((Employee) in.readObject()); //读取对象
  45. }
  46. System.out.println("序列化花费的时间:"+(System.currentTimeMillis()-currentTime)+"毫秒");
  47. }
  48. }

要点:使用ByteArrayOutputStream和ByteArrayInputStream可以将对象保存在内存中,这样就不必产生一个本地文件来完成序列化的功能。
***

假克隆、浅克隆和深克隆的应用范围

假克隆 基本数据类型
浅克隆 基本数据类型、不可变引用类型
深克隆 可变引用类型

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号