Java深拷贝浅拷贝
首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:
实现 Cloneable接口,因为 Object的 clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。
改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作, Object的 clone方法是 protected权限。
通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
现在,来看一下不同的类型在拷贝过程中的表现:
Operator = |
拷贝构造函数 |
clone方法 |
||
预定义非集合类型 |
深拷贝 |
如果支持拷贝构造函数的类型,则是深拷贝 |
不支持 |
|
自定义类型 |
浅拷贝 |
取决于实现 |
取决于实现 |
|
预定义集合类型 |
浅拷贝 |
会逐个调用每个元素的operator=方法 |
|
下面是测试代码,首先测试的是预定义非集合类型的operator =操作:
int x=1;
int y=x;
y=2;
if(x!=y){
System.out.println("deep copy");
}
Integer a=1;
Integer b=a;
b=2;
if(!a.equals(b)){
System.out.println("deep copy");
}
String m="ok";
String n=m;
n="no";
if(!m.equals(n)){
System.out.println("deep copy");
}
程序运行后,输出三行deep copy,测试结果表明,这三种类型的operator =操作都是深拷贝。由于我没有测试完所有的预定义非集合类型,我这里推测它们的operator =都是深拷贝。
下面测试预定义非集合类型的拷贝构造函数:
Integer a=1;
Integer b=new Integer(a);
b=2;
if(!a.equals(b)){
System.out.println("deep copy");
}
String m="ok";
String n=new String(m);
n="no";
if(!m.equals(n)){
System.out.println("deep copy");
}
程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。
现在我们来测试自定义类型的operator=操作,假设我有一个类Person,代码如下:
public class Person implements Cloneable{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
}
测试代码:
Person p=new Person();
p.setAge(32);
p.setName("陈抒");
Person p2=p;
p.setAge(33);
p.setName("老陈");
if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){
System.out.println("deep copy");
}
运行后,没有输出deep copy,说明这是浅拷贝。这里就是我们常说的两个引用之间的赋值,仅仅是让两个引用指向同一个对象。
现在,我们来测试预定义集合类型的operator=操作:
ArrayList list1=new ArrayList();
list1.add("yangzhou");
ArrayList list2=list1;
list1.clear();
if(list2.isEmpty()){
System.out.println("shallow copy");
}
结果输出为shallow copy。
现在我来测试拷贝构造函数:
ArrayList list1=new ArrayList();
list1.add("yangzhou");
ArrayList list2=new ArrayList(list1);
list1.clear();
if(list2.isEmpty()){
System.out.println("shallow copy");
}else{
System.out.println("deep copy");
}
输出结果是deep copy;
clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 。
预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:
ArrayList list1=new ArrayList();
Person p1=new Person();
p1.setAge(32);
p1.setName("陈抒");
list1.add(p1);
ArrayList list2=(ArrayList) list1.clone();
list2.get(0).setName("chenshu");
if(list2.get(0).getName().equals(list1.get(0).getName())){
System.out.println("shallow copy");
}else{
System.out.println("deep copy");
}
输出为shallow copy,Person是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。
现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。
假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;
假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:
public class Company {
public Company(){
}
public Company(Company c){
name=c.name;
person=new Person(c.person);
}
private String name;
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException{
Company c=new Company();
c.setName(name);
c.setPerson((Person) person.clone());
return c;
}
}
public class Person implements Cloneable{
public Person(){
}
public Person(Person p){
age=p.age;
name=p.name;
}
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Person的clone方法。
Java深拷贝浅拷贝的更多相关文章
- Java 深拷贝浅拷贝 与 序列化
一.浅拷贝.深拷贝 浅拷贝会对对象中的成员变量进行拷贝:如果是基本类型,拷贝的就是基本类型的值:如果属性是内存地址(引用类型),拷贝的就是内存地址 : 深拷贝,除了基本类型外,引用类型所引用的对象也会 ...
- Java基础 深拷贝浅拷贝
Java基础 深拷贝浅拷贝 非基本数据类型 需要new新空间 class Student implements Cloneable{ private int id; private String na ...
- Java之浅拷贝与深拷贝
----?浅拷贝 --- 概念 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.简单说,浅拷贝就是只复制所考虑的对象,而不复制它所引用的对象 --- 实现方 ...
- Java 深拷贝和浅拷贝 利用序列化实现深拷贝
Java 深拷贝和浅拷贝 转自:http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html 深拷贝(deep clone)与浅拷贝(sh ...
- Java的浅拷贝与深拷贝
Java的浅拷贝与深拷贝 Java中,所有的类都继承Object,Object中有clone方法,它被声明为了 protected ,所以我们但是如果要使用该方法就得重写且声明为public,必须在要 ...
- java 深拷贝与浅拷贝机制详解
概要: 在Java中,拷贝分为深拷贝和浅拷贝两种.java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝. (一 ...
- 一种c#深拷贝方式完胜java深拷贝(实现上的对比)
楼主是一名asp.net攻城狮,最近经常跑java组客串帮忙开发,所以最近对java的一些基础知识特别上心.却遇到需要将一个对象深拷贝出来做其他事情,而原对象保持原有状态的情况.(实在是不想自己new ...
- c# 内存的具体表现- 通用类型系统 深拷贝 浅拷贝 函数传参
c# 通用类型系统 及变量在 深拷贝 浅拷贝 函数传参 中的深层次的表现 在编程中遇到了一些想不到的异常,跟踪发现,自己对于c#变量在内存上的表现理解有偏差,系统的学习并通过代码实验梳理了各种情况下, ...
- python集合增删改查,深拷贝浅拷贝
集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. 关系 ...
随机推荐
- CentOS 7 下使用虚拟环境Virtualenv安装Tensorflow cpu版记录
1.首先安装pip-install 在使用centos7的软件包管理程序yum安装python-pip的时候会报一下错误: No package python-pip available. Error ...
- Android简易实战教程--第三十五话《音乐播放》
已经好几天不更新博客了,今天轻松一点模拟个简单的"音乐播放器".1分钟看完~ 整个简单布局,加几个控制按钮: <LinearLayout xmlns:android=&quo ...
- chromium出现输入密码解锁登录密钥环
chromium出现输入密码解锁登录密钥环 在ubuntu 16.04上安装了Chromium出现对话框,如下所示: 因为密码框截图困难,这个是网上图片. 点取消就可以使用,但是每次都这样很烦,百度后 ...
- Java并发框架——AQS之如何使用AQS构建同步器
AQS的设计思想是通过继承的方式提供一个模板让大家可以很容易根据不同场景实现一个富有个性化的同步器.同步器的核心是要管理一个共享状态,通过对状态的控制即可以实现不同的锁机制.AQS的设计必须考虑把复杂 ...
- iOS中NSTimer的invalidate调用之后
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...
- 非负矩阵分解NMF
http://blog.csdn.net/pipisorry/article/details/52098864 非负矩阵分解(NMF,Non-negative matrix factorization ...
- Java并发框架——AQS之阻塞与唤醒
根据前面的线程阻塞与唤醒小节知道,目前在Java语言层面能实现阻塞唤醒的方式一共有三种:suspend与resume组合.wait与notify组合.park与unpark组合.其中suspend与r ...
- Django使用forms来实现评论功能
貌似Django从版本1.6开始就放弃了对自带的comments的使用,具体原因未查,但是现在使用Django的内部的模块也可以实现评论功能,那就是借助于forms模块,下面是我的一个小例子. 环境准 ...
- 如何在mac OS X中查看Emoji表情的含义
使用ctrl+空格,在搜索框中搜索 TextEdit程序,其实中文是 文本编辑 程序, 运行,在菜单中选择 编辑->特殊字符 然后可以看到每个图标的说明啦
- scala学习笔记5 (隐式转化/参数/类)
隐式转化: 隐式参数: 隐式类: