- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入理解Java中的克隆由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
Java克隆(Clone)是Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率.
对于克隆(Clone),Java有一些限制:
1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法.
2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功.
3、在Java.lang.Object类中克隆方法是这么定义的:
1
2
|
protected
Object clone()
throws
CloneNotSupportedException
|
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见.
按照惯例,返回的对象应该通过调用 super.clone 获得.
Java中的赋值 。
在Java中,赋值是很常用的,一个简单的赋值如下 。
1
2
3
4
5
6
7
|
//原始类型
int
a =
1
;
int
b = a;
//引用类型
String[] weekdays =
new
String[
5
];
String[] gongzuori = weekdays;
//仅拷贝引用
|
在上述代码中.
1、如果是原始数据类型,赋值传递的为真实的值 。
2、如果是引用数据类型,赋值传递的为对象的引用,而不是对象.
了解了数据类型和引用类型的这个区别,便于我们了解clone.
Clone 。
在Java中,clone是将已有对象在内存中复制出另一个与之相同的对象的过程。java中的克隆为逐域复制.
在Java中想要支持clone方法,需要首先实现Cloneable接口 。
Cloneable其实是有点奇怪的,它不同与我们常用到的接口,它内部不包含任何方法,它仅仅是一个标记接口.
其源码如下 。
1
2
|
public
interface
Cloneable {
}
|
关于cloneable,需要注意的 。
1、如果想要支持clone,就需要实现Cloneable 接口 。
2、如果没有实现Cloneable接口的调用clone方法,会抛出CloneNotSupportedException异常.
然后是重写clone方法,并修改成public访问级别 。
1
2
3
4
5
6
7
8
9
10
|
static
class
CloneableImp
implements
Cloneable {
public
int
count;
public
Child child;
@Override
public
Object clone()
throws
CloneNotSupportedException {
return
super
.clone();
}
}
|
调用clone方法复制对象 。
1
2
3
4
5
6
7
8
9
|
CloneableImp imp1 =
new
CloneableImp();
imp1.child =
new
Child(
"Andy"
);
try
{
Object obj = imp1.clone();
CloneableImp imp2 = (CloneableImp)obj;
System.out.println(
"main imp2.child.name="
+ imp2.child.name);
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
|
浅拷贝 。
上面的代码实现的clone实际上是属于浅拷贝(Shallow Copy).
关于浅拷贝,你该了解的 。
1、使用默认的clone方法 。
2、对于原始数据域进行值拷贝 。
3、对于引用类型仅拷贝引用 。
4、执行快,效率高 。
5、不能做到数据的100%分离.
6、如果一个对象只包含原始数据域或者不可变对象域,推荐使用浅拷贝.
关于无法做到数据分离,我们可以使用这段代码验证 。
1
2
3
4
5
6
7
8
9
10
11
|
CloneableImp imp1 =
new
CloneableImp();
imp1.child =
new
Child(
"Andy"
);
try
{
Object obj = imp1.clone();
CloneableImp imp2 = (CloneableImp)obj;
imp2.child.name =
"Bob"
;
System.out.println(
"main imp1.child.name="
+ imp1.child.name);
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
|
上述代码我们使用了imp1的clone方法克隆出imp2,然后修改 imp2.child.name 为 Bob,然后打印imp1.child.name 得到的结果是 。
1
|
main imp1.child.name=Bob
|
原因是浅拷贝并没有做到数据的100%分离,imp1和imp2共享同一个Child对象,所以一个修改会影响到另一个.
深拷贝 。
深拷贝可以解决数据100%分离的问题。只需要对上面代码进行一些修改即可.
1、Child实现Cloneable接口.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
Child
implements
Cloneable{
public
String name;
public
Child(String name) {
this
.name = name;
}
@Override
public
String toString() {
return
"Child [name="
+ name +
"]"
;
}
@Override
protected
Object clone()
throws
CloneNotSupportedException {
return
super
.clone();
}
}
|
2.重写clone方法,调用数据域的clone方法.
1
2
3
4
5
6
7
8
9
10
11
12
|
static
class
CloneableImp
implements
Cloneable {
public
int
count;
public
Child child;
@Override
public
Object clone()
throws
CloneNotSupportedException {
CloneableImp obj = (CloneableImp)
super
.clone();
obj.child = (Child) child.clone();
return
obj;
}
}
|
当我们再次修改imp2.child.name就不会影响到imp1.child.name的值了,因为imp1和imp2各自拥有自己的child对象,因为做到了数据的100%隔离.
关于深拷贝的一些特点 。
1、需要重写clone方法,不仅仅只调用父类的方法,还需调用属性的clone方法 。
2、做到了原对象与克隆对象之间100%数据分离 。
3、如果是对象存在引用类型的属性,建议使用深拷贝 。
4、深拷贝比浅拷贝要更加耗时,效率更低 。
为什么使用克隆 。
很重要并且常见的常见就是:某个API需要提供一个List集合,但是又不希望调用者的修改影响到自身的变化,因此需要克隆一份对象,以此达到数据隔离的目的.
应尽量避免clone 。
1.通常情况下,实现接口是为了表明类可以为它的客户做些什么,而Cloneable仅仅是一个标记接口,而且还改变了超类中的手保护的方法的行为,是接口的一种极端非典型的用法,不值得效仿.
2.Clone方法约定及其脆弱 clone方法的Javadoc描述有点暧昧模糊,如下为 Java SE8的约定 。
clone方法创建并返回该对象的一个拷贝。而拷贝的精确含义取决于该对象的类。一般的含义是,对于任何对象x,表达式 。
x.clone() != x 为 true x.clone().getClass() == x.getClass() 也返回true,但非必须 x.clone().equals(x) 也返回true,但也不是必须的 。
上面的第二个和第三个表达式很容易就返回false。因而唯一能保证永久为true的就是表达式一,即两个对象为独立的对象.
3.可变对象final域 在克隆方法中,如果我们需要对可变对象的final域也进行拷贝,由于final的限制,所以实际上是无法编译通过的。因此为了实现克隆,我们需要考虑舍去该可变对象域的final关键字.
4.线程安全 如果你决定用线程安全的类实现Cloneable接口,需要保证它的clone方法做好同步工作。默认的Object.clone方法是没有做同步的.
总的来说,java中的clone方法实际上并不是完善的,建议尽量避免使用。如下是一些替代方案.
Copy constructors 。
使用复制构造器也可以实现对象的拷贝.
1、复制构造器也是构造器的一种 。
2、只接受一个参数,参数类型为当前的类 。
3、目的是生成一个与参数相同的新对象 。
4、复制构造器相比clone方法的优势是简单,易于实现.
一段使用了复制构造器的代码示例 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
Car {
Wheel wheel;
String manufacturer;
public
Car(Wheel wheel, String manufacturer) {
this
.wheel = wheel;
this
.manufacturer = manufacturer;
}
//copy constructor
public
Car(Car car) {
this
(car.wheel, car.manufacturer);
}
public
static
class
Wheel {
String brand;
}
}
|
注意,上面的代码实现为浅拷贝,如果想要实现深拷贝,参考如下代码 。
1
2
3
4
5
6
7
8
|
//copy constructor
public
Car(Car car) {
Wheel wheel =
new
Wheel();
wheel.brand = car.wheel.brand;
this
.wheel = wheel;
this
.manufacturer = car.manufacturer;
}
|
为了更加便捷,我们还可以为上述类增加一个静态的方法 。
1
2
3
|
public
static
Car newInstance(Car car) {
return
new
Car(car);
}
|
使用Serializable实现深拷贝 。
其实,使用序列化也可以实现对象的深拷贝。简略代码如下 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public
class
DeepCopyExample
implements
Serializable{
private
static
final
long
serialVersionUID = 6098694917984051357L;
public
Child child;
public
DeepCopyExample copy() {
DeepCopyExample copy =
null
;
try
{
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
ObjectOutputStream oos =
new
ObjectOutputStream(baos);
oos.writeObject(
this
);
ByteArrayInputStream bais =
new
ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois =
new
ObjectInputStream(bais);
copy = (DeepCopyExample) ois.readObject();
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
return
copy;
}
}
|
其中,Child必须实现Serializable接口 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
Child
implements
Serializable{
private
static
final
long
serialVersionUID = 6832122780722711261L;
public
String name =
""
;
public
Child(String name) {
this
.name = name;
}
@Override
public
String toString() {
return
"Child [name="
+ name +
"]"
;
}
}
|
使用示例兼测试代码 。
1
2
3
4
5
6
7
8
9
|
DeepCopyExample example =
new
DeepCopyExample();
example.child =
new
Child(
"Example"
);
DeepCopyExample copy = example.copy();
if
(copy !=
null
) {
copy.child.name =
"Copied"
;
System.out.println(
"example.child="
+ example.child +
";copy.child="
+ copy.child);
}
//输出结果:example.child=Child [name=Example];copy.child=Child [name=Copied]
|
由输出结果来看,copy对象的child值修改不影响example对象的child值,即使用序列化可以实现对象的深拷贝.
总结 。
以上就是Java中克隆的全部内容,希望本文对大家学习Java能有所帮助.
最后此篇关于深入理解Java中的克隆的文章就讲到这里了,如果你想了解更多关于深入理解Java中的克隆的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有 jquery Draggable/droppable 来处理包含和帮助器选项集。我想做的是将放置的项目的顶部和左侧参数存储在两个变量中。 我在下面的示例中实现了这一点(将新文档图标拖到框中),但
我有一个带有两个链接下拉列表的表单,我需要制作许多克隆,但保留链接。 这是一个示例,链接组合在我的应用程序中带有 json。 链式代码:
我在使用少量 jQuery 时遇到了一些逻辑问题。 我很确定我需要一个循环设置,但我很难将其组合在一起。我引用了 tuts、视频、工作示例、幻灯片,甚至是原始 javascript,但仍然难以将逻辑端
我有一个对象,它是一个基本的树。我需要对其进行深度复制,并发现自己实现了 __clone 方法。成功的代码是: function __clone() { $object = new Custo
我可以克隆一个没有内容的文本框吗?意味着如果我在克隆后在文本框中输入一些值,我想要一个空文本框。这可能吗?或者jquery克隆将其返回为innerHtml? 最佳答案 默认情况下,克隆会复制 的值目
我想复制或克隆我自己编写的类的对象。但如果我调用复制函数,则仅复制指针。因此,如果我更改复制的对象,原始对象也会更改。 有没有一种方法/功能可以真正克隆一个对象? 最诚挚的问候梅兰妮 最佳答案 如果一
我有一些 javascripc 代码: $(this).parent().siblings().find('.year-dropdown').find('.date, .time, .details'
我们有一个包含三个命名分支的存储库,我想克隆其中一个分支。有一个善变的命令可以做到这一点吗?如果我使用 hg clone 提供(分支)路径,则会收到 404 错误。 最佳答案 hg clone htt
我有带有 ObservableCollection 和其他属性的类。所以它看起来有点像这样: public class A { public int Id { get; set; } ..
我正在尝试下载一个大型开源项目的源代码,以便我可以查看它。 它说要做: hg clone http://server/path 但是,这需要很长时间(我假设是因为这是一个大项目)。我并不真正关心变更集
我发现这段代码随处可见,用于复制列表或克隆列表。 代码随处可见: clone([],[]). clone([H|T],[H|Z]):- clone(T,Z). ?-clone([1,2,3],Z).
我正在打印一个JFrame。在此之前,我隐藏菜单栏并将 JFrame 设置为未修饰。这工作得很好,但可见的 JFrame 发生了变化,以反射(reflect)我稍后必须恢复的已删除的控件。 我想克隆
我正在尝试复制一个 div 并将其附加到它的克隆之上。不幸的是,它似乎正在创建额外的重复项。这是怎么回事? 这是一个示例:http://jsfiddle.net/QEN5N/ 最佳答案 live 会将
为什么我不能克隆 ConcurrentHashMap ? ConcurrentHashMap test = new ConcurrentHashMap(); test.put("hello",
我有这个代码: openPopup.hide(); var substr = popupId.split('-'); var clone = $("#po
这段代码几乎可以正常工作假设我的表中有 10 行,我单击顶行,它会被克隆,然后添加到表的底部,而原始数据被删除,重复这些步骤 5 次。现在,我以克隆在底部的五行结束。 现在,如果我单击第一个克隆行,它
我已经设置了JSFiddle来展示我的问题。 我改变了克隆方式,使其更加通用,因此我不需要为不同的表重用代码。通常,对于 select2 元素,我会这样做 $(".campaignType", $tr
1 2 3 $('#things').after($('#things').clone()); 克隆时如何在这两个元素之间插入中断?有没有一种巧妙的方法可以用一行代码来完成
我正在从现有类型动态装配中创建新类型,但只包含选定的属性: public class EmitTest { public Type Create(Type prototype, Type dy
在我的游戏引擎中实现对象克隆的过程中,我遇到了一些绊脚石。我的目标是拥有一个克隆系统,我不必逐个类地维护它,除非该类需要特殊处理。 我的游戏引擎的设置围绕着一个基类 Object2D,它包含一些 Te
我是一名优秀的程序员,十分优秀!