Part6:Java中的克隆


@


Example01:Java对象的假克隆

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

  基本数据类型可以使用“=”来进行克隆,此时两个变量除了相等是没有任何关系的。而对于引用类型数据不能简单地使用“=”进行克隆,这与Java的内存空间使用有关。   

  Java将内存空间分成两块,即栈和堆。在栈中保存基本类型和引用变量;在堆中保存对象。对于引用变量而言,使用“=”将修改引用,而不是复制堆中的对象。此时两个引用变量将指向同一个对象。因此,如果一个变量对其修改则会改变另一个变量。

运行结果:



代码实现:

public class Employee {
private String name;
private int age; //省略set()和get()方法 @Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public static void main(String[] args) {
System.out.println("-----克隆之前:--------");
Employee employee1 = new Employee();
employee1.setName("hyn");
employee1.setAge(20);
System.out.println("员工1的信息:\n"+employee1); System.out.println("-----克隆之后:--------");
Employee employee2 = employee1; //将employee1赋值给employee2
employee2.setName("azw");
employee2.setAge(21);
System.out.println("员工1的信息:\n"+employee1);
System.out.println("员工2的信息:\n"+employee2);
}
}

Example02:Java对象的浅克隆

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

运行结果:



代码实现:

//Address.java
public class Address {
private String state; //所在国家
private String province; //所在省
private String city; //所在城市 public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
} //省略set()和get()方法 @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("国家:"+state+",");
sb.append("省:"+province+",");
sb.append("市:"+city);
return sb.toString();
}
}
//Employee.java
public class Employee implements Cloneable{
private String name;
private int age;
private Address address; public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
//省略set()和get()方法 @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年龄:"+age+",");
sb.append("\n地址:"+address);
return sb.toString();
} @Override
public Employee clone() throws CloneNotSupportedException { //实现浅克隆
Employee employee = (Employee) super.clone();
return employee;
}
}

测试代码:

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

Example03:Java对象的深克隆

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

运行结果:



代码实现:

//Address.java
public class Address implements Cloneable{
private String state; //所在国家
private String province; //所在省
private String city; //所在城市 public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
} //省略set()和get()方法 @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("国家:"+state+",");
sb.append("省:"+province+",");
sb.append("市:"+city);
return sb.toString();
}
//---------------------------
@Override
public Address clone() throws CloneNotSupportedException {
//Address类中的域不是基本类型就是不可变类型,所以可以直接使用浅克隆
Address address = (Address) super.clone();
return address;
}
//---------------------------
} //Employee.java
public class Employee implements Cloneable{
private String name;
private int age;
private Address address; public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
} //省略set()和get()方法 @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年龄:"+age+",");
sb.append("\n地址:"+address);
return sb.toString();
} @Override
public Employee clone() throws CloneNotSupportedException { //实现深克隆
Employee employee = (Employee) super.clone();
//---------------------------------
employee.address = address.clone();
//---------------------------------
return employee;
}
} //测试代码同Example02测试代码.
  • 要点:通常情况下,需要用到克隆对象时都需要使用深克隆。

Example04:序列化与对象克隆

  如果类的成员变量比较复杂,例如使用了多个可变的引用类型,使用clone()方法是非常麻烦的,所以可以考虑序列化的方式完成克隆。

运行结果:



代码实现:

import java.io.Serializable;

public class Employee implements Serializable {
//同Example04中Employee.java的代码
} public class Address implements Serializable {
//同Example04中Assress.java的代码
}

测试代码:

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

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


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

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

运行结果:



代码实现:

import java.io.Serializable;

public class Employee implements Cloneable,Serializable {
private String name;
private int age; public Employee(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:"+name+",");
sb.append("年龄:"+age+",");
return sb.toString();
} @Override
public Employee clone() throws CloneNotSupportedException { //使用父类的clone()方法实现深克隆
Employee employee = (Employee) super.clone();
return employee;
}
}
测试代码:
import java.io.*;
import java.util.ArrayList;
import java.util.List; class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
List<Employee> employees = new ArrayList<Employee>(); //创建列表保存对象
Employee employee = new Employee("azw", 20); //创建对象
long currentTime = System.currentTimeMillis(); //获得当前系统时间
//使用克隆方式获得对象
for (int i = 0;i<100000;i++){
employees.add(employee.clone());
}
System.out.println("克隆花费的时间:"+(System.currentTimeMillis()-currentTime)+"毫秒"); currentTime = System.currentTimeMillis(); //获得当前系统时间
for (int i = 0;i<100000;i++){
ByteArrayOutputStream bout = new ByteArrayOutputStream(); //创建字节数组输出流
ObjectOutputStream out = new ObjectOutputStream(bout); //创建对象输出流
out.writeObject(employee); //将对象写入到输出流中
//获得字节输出流内容
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin); //创建对象输入流
employees.add((Employee) in.readObject()); //读取对象
}
System.out.println("序列化花费的时间:"+(System.currentTimeMillis()-currentTime)+"毫秒");
}
}

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


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

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

Java实例 Part6:Java中的克隆的更多相关文章

  1. Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素

    ylbtech-Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素 1.返回顶部 1. Java 实例 - 在数组中查找指定元素  Java 实例 以下实例 ...

  2. Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素-un

    ylbtech-Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素 1.返回顶部 1. Java 实例 - 查找数组中的重复元素  Java 实例 以下实例 ...

  3. Java-Runoob-高级教程-实例-字符串:03. Java 实例 - 删除字符串中的一个字符

    ylbtech-Java-Runoob-高级教程-实例-字符串:03. Java 实例 - 删除字符串中的一个字符 1.返回顶部 1. Java 实例 - 删除字符串中的一个字符  Java 实例 以 ...

  4. Java实例练习——java实现自动生成长度为10以内的随机字符串(可用于生成随机密码)

    package sorttest; import java.util.ArrayList; import java.util.Collections; import java.util.List; i ...

  5. Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖

    ylbtech-Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖 1.返回顶部 1. Java 实例 - 方法覆盖  Java 实例 前面章节中我们已经学习了 Jav ...

  6. Java - 35 Java 实例

    Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 Java 实例 – 如何编译一个Java 文件? Java 实 ...

  7. Java-Runoob-高级教程:Java 实例

    ylbtech-Java-Runoob-高级教程:Java 实例 1.返回顶部 1. Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. ...

  8. Java 实例

    Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 Java 实例 – 如何编译一个Java 文件? Java 实 ...

  9. Java 学习(21):Java 实例

    Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 //HelloWorld.java 文件 public cla ...

随机推荐

  1. visual box 安装 centos7后,无法上网

    ifconfig  命令后,只看到个回环网卡: 进入 /etc/sysconfig/network-scripts后,发现有设备 visual box 中设置为“桥接网卡”,也没问题,最后把控制芯片改 ...

  2. java线程操作

    目录 前言 创建多线程的方式 1继承thread抽象类 2实现Runnable接口 3实现Callable接口 匿名内部类 线程池 线程安全 同步代码块 同步方法 锁机制 线程状态 前言 进程:内存运 ...

  3. java笔记--代码实现汉诺塔移动过程和移动次数

    汉诺塔 有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方. --如果朋友 ...

  4. 剑指offer相关问题

    1. 变态跳台阶 Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+..........+Fib(n-n)         =Fib(0)+Fib(1)+Fib(2)+..... ...

  5. 使用dsoframer控件出现"Unable to display the inactive document. Click here to reactivate the document."的问题 .

    使用如下属性设置: axFramerControl.ActivationPolicy = DSOFramer.dsoActivationPolicy.dsoKeepUIActiveOnAppDeact ...

  6. Effective C++(6) 如何拒绝编译器的自动生成函数

    问题聚焦: 如果不希望class支持某一成员函数,那么不声明和定义它就可以了,但是这一策略对与拷贝构造函数和重载赋值操作符并不起作用. 因为如果不声明它们,那么当尝试调用它们的时候,编译器会为你声明和 ...

  7. Java学习---连接数据库操作

    Java连接Oracle数据库 package com.ftl.mysql; import java.sql.Connection; import java.sql.DriverManager; im ...

  8. java中形参的可变参数的定义(如String... args) .

    如果有下面的一个笔试题: 已知我们有如下的调用关系 logIt(”log message 1 “); logIt(”log message2”, " log message3”); logI ...

  9. 剖析php脚本的超时机制

    在做php开发的时候,经常会设置max_input_time.max_execution_time,用来控制脚本的超时时间.但却从来没有思考过背后的原理. 趁着这两天有空,研究一下这个问题.文中源码取 ...

  10. PhoneGap获取设备信息

    一. 获取设备信息的方法列表(如果没有或者检测不出来就显示undefined) 1.device.name              设备名称(一些国产机检测不出来) 2.device.model   ...