Java实例 Part6:Java中的克隆
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中的克隆的更多相关文章
- Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素
ylbtech-Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素 1.返回顶部 1. Java 实例 - 在数组中查找指定元素 Java 实例 以下实例 ...
- Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素-un
ylbtech-Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素 1.返回顶部 1. Java 实例 - 查找数组中的重复元素 Java 实例 以下实例 ...
- Java-Runoob-高级教程-实例-字符串:03. Java 实例 - 删除字符串中的一个字符
ylbtech-Java-Runoob-高级教程-实例-字符串:03. Java 实例 - 删除字符串中的一个字符 1.返回顶部 1. Java 实例 - 删除字符串中的一个字符 Java 实例 以 ...
- Java实例练习——java实现自动生成长度为10以内的随机字符串(可用于生成随机密码)
package sorttest; import java.util.ArrayList; import java.util.Collections; import java.util.List; i ...
- Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖
ylbtech-Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖 1.返回顶部 1. Java 实例 - 方法覆盖 Java 实例 前面章节中我们已经学习了 Jav ...
- Java - 35 Java 实例
Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 Java 实例 – 如何编译一个Java 文件? Java 实 ...
- Java-Runoob-高级教程:Java 实例
ylbtech-Java-Runoob-高级教程:Java 实例 1.返回顶部 1. Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. ...
- Java 实例
Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 Java 实例 – 如何编译一个Java 文件? Java 实 ...
- Java 学习(21):Java 实例
Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 //HelloWorld.java 文件 public cla ...
随机推荐
- CentOS7 安装 JIRA 7.2.x 教程:下载、安装、汉化、破解
1.先看视频,参考着能装出个试用版来,不同的地方后面再做说明.JIRA 安装视频(Linux) http://www.confluence.cn/pages/viewpage.action?pageI ...
- Java笔记-IO流的运用
--如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3877386.html "谢谢-- 1.InputStream和System ...
- centos 安装php扩展的两种方法
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 查看PHP版本: php -v 1 1 查看指定PHP版本: /usr/local/php/bin/php -v 1 1 ...
- Mysql学习---使用Python执行存储过程
使用Python执行存储过程 使用Python执行存储过程[2部分]: 1.执行存储过程,获取存储过程的结果集 2.将返回值设置给了 @_存储过程名_序号 = #!/usr/bin/env pyt ...
- 最优化 KKT条件
对于约束优化问题: 拉格朗日公式: 其KKT条件为: 求解 x.α.β 其中β*g(x)为互补松弛条件 KKT条件是使一组解成为最优解的必要条件,当原问题是凸问题的时候,KKT条件也是充分条件.
- Attempt to load Oracle client libraries threw BadImageFormatException. This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed.
System.Data.OracleClient 已经过时了.微软不再支持它. 因此,我建议你为. NET 使用Oracle数据提供程序:ODP.Net. 你可以从以下位置下载: 版本:Release ...
- myeclipse 2013破解注册图文教程
以下这个试过有效 http://www.33lc.com/article/10792.html
- 关于UITableView 不能回调 tableView: cellForRowAtIndexPath的问题
做项目时始终遇到一个问题,tableview不能回调cellForRowAtIndexPath方法,导致cell不能显示. 在网上没找到合理的解决方案. 自己弄了一下,按照自己的推测解决了这个问题 首 ...
- POJ1375 Intervals
嘟嘟嘟 题意简述:给出一个光源\((x_0, y_0)\),和一些圆,求投影区间. 这道题其实就是求经过\((x_0, y_0)\))的圆的切线. 刚开始我想到了一个用向量旋转的方法,但是写起来特别麻 ...
- Monkeyrunner测试小实践
环境搭建完成后,我们通过命令打开模拟器,前提是在Eclipse中创建了一个模拟器 (1)cmd命令:emulator -avd 模拟器名称 启动了模拟器,此时你就会看到一个安卓模拟器的弹出 (2)cm ...