深入理解java中的clone
- 格式:pdf
- 大小:692.01 KB
- 文档页数:13
JAVA克隆对象CLONE的用法和作用电脑资料Clone(方法是Java中Object类中提供的一个方法,它用于创建并返回一个对象的拷贝副本。
该方法可以用于实现对象的复制,在一些场景下,克隆对象可以提高程序的性能,同时降低内存的使用。
使用clone(方法进行对象的克隆,需要满足以下两个条件:1. 需要被克隆的类必须实现Cloneable接口。
Cloneable接口属于标记接口,在类上使用该接口并不会添加任何方法,而是用于告诉JVM该类可以被克隆。
2. 在被克隆的类中,需要重写clone(方法。
默认情况下,clone(方法是受保护的,因此在子类中需要进行重写,并设置为public。
clone(方法的调用形式为:object.clone(,它返回一个新的对象,与原对象相同。
此处需要注意的是,clone(方法返回的是一个浅拷贝的副本,对于对象的引用类型属性,仅复制了引用地址,而不会创建新的对象。
因此,在需要进行深拷贝时,需要注意对引用类型属性进行递归拷贝。
拷贝对象与原对象的关系:1.克隆对象与原对象是两个相互独立的对象,它们的值相等,但在内存中分别占用不同的地址空间。
2.修改克隆对象不会影响到原对象,同样修改原对象也不会影响到克隆对象。
拷贝对象与原对象的关系示例代码:```public class CloneDemopublic static void main(String[] args) }private String name;private int memorySize; = name;this.memorySize = memorySize;}// 重写clone(方法try} catch (CloneNotSupportedException e) e.printStackTrace(;return null;}}public String toStrin"name='" + name + '\'' +", memorySize=" + memorySize +'}';}public String getNamreturn name;}public void setName(String name) = name;}public int getMemorySizreturn memorySize;}public void setMemorySize(int memorySize)this.memorySize = memorySize;}```运行上述代码将输出以下结果:``````可以看到,克隆对象与原对象最初的取值是相同的,但在修改克隆对象的属性后,原对象的属性并未发生改变。
java clone方法
java的clone方法是java中一种深度复制的方法,也就是把一个对象的所有数据复制到另一个对象中。
clone方法位于
ng.Object类中,被广泛应用于Java中拷贝一个对象,深度复制一个对象。
clone方法是调用者需要明确做为实现对象的复制功能,调用者通过实现Cloneable接口来标识自身可被复制,因此实现ng.Cloneable接口是使用clone方法的先决条件。
clone方法是不安全的,因为它使用的是类型弱类型的浅复制,它只复制对象本身,而不复制对象的引用,因此当原始的对象被修改时,被复制的对象也会受到影响,导致意想不到的错误。
因此,为了解决这个问题,在java中定义了另一种clone方法,即Cloneable接口中的clone方法,它可以用于深度复制,即把一个对象的所有数据复制到另一个对象中。
此种深度复制不仅复制了基本数据类型,而且还复制了引用,从而使得复制的对象与原始对象完全分离,即即使原始对象更改了,也不会影响到复制的对象。
java中clone方法有两种,一种是Object类中的clone方法,它用于实现浅复制;另一种是Cloneable接口中的clone方法,它用于实现深度复制,由于深度复制更安全,因此。
一般情况下,建议使用Cloneable接口中的clone方法,以更好地实现对象的深度复制。
Java深度克隆解析——clone()深度克隆与浅克隆clone()⽅法中的三个要求:x.clone() != x 为 true(这⼀条是必须的)x.clone().getClass() == x.getClass() 为truex.clone().equals(x) 为true(⼀般情况下为true,但这并不是必须要满⾜的要求)浅克隆:对引⽤数据类型中的基础数据类型进⾏复制,且独⽴,即修改原来对象中基础数据,克隆的对象基础数据不会改变对引⽤数据类型中的引⽤数据类型的地址进⾏复制,即即修改原来对象中引⽤数据,克隆的对象引⽤数据会改变深度克隆:对引⽤数据类型中的基础数据类型进⾏复制,且独⽴,即修改原来对象中基础数据,克隆的对象基础数据不会改变对引⽤数据类型中的引⽤数据类型进⾏复制,且独⽴,即修改原来对象中引⽤数据,克隆的对象引⽤数据不会改变由上图可以知道,如果Students对象中还有引⽤类型的数据,则还可以继续套娃克隆。
怎么深度克隆⾸先,clone()是Object类下的⼀个⽅法:protected native Object clone() throws CloneNotSupportedException;该⽅法的修饰词为protected和native,所以如果要⽤这个⽅法,必须继承Object类。
前⽂已经说到,浅克隆只通过clone()即可,所以如果要进⾏深度克隆那么就需要对clone()⽅法进⾏重写。
此外,Java中Object类本⾝不实现Cloneable接⼝,所以类为Object的对象调⽤clone()时需要抛出异常CloneNotSupportedException。
重写clone()⽅法两个步骤:克隆基础数据部分克隆引⽤数据部分下⾯看⼀个对Animals类重写的例⼦class Animal implements Cloneable {int age;int weight;SleepTime sleepTime;public Animal(int age, int weight, SleepTime sleepTime) {this.age = age;this.weight = weight;this.sleepTime = sleepTime;}//重写Animal的clone()@Overrideprotected Animal clone() throws CloneNotSupportedException {Animal animal = (Animal) super.clone();//先克隆Animal的基础数据类型属性animal.sleepTime = this.sleepTime.clone();//再单独克隆Animal的引⽤数据类型return (Animal) super.clone();}}class SleepTime implements Cloneable {int time;public SleepTime(int time) {this.time = time;}//重写SleepTime的clone()@Overrideprotected SleepTime clone() throws CloneNotSupportedException {return (SleepTime) super.clone();}}super.clone()相关在JavaDoc中关于clone有这么⼀段* The method {@code clone} for class {@code Object} performs a* specific cloning operation. First, if the class of this object does* not implement the interface {@code Cloneable}, then a* {@code CloneNotSupportedException} is thrown. Note that all arrays* are considered to implement the interface {@code Cloneable} and that* the return type of the {@code clone} method of an array type {@code T[]}* is {@code T[]} where T is any reference or primitive type.* Otherwise, this method creates a new instance of the class of this* object and initializes all its fields with exactly the contents of* the corresponding fields of this object, as if by assignment; the* contents of the fields are not themselves cloned. Thus, this method* performs a "shallow copy" of this object, not a "deep copy" operation.上⾯这段话说⼈话就是:Object.clone()有特殊的含义,即对当前对象进⾏浅克隆综上对深度克隆中的super.clone()就不难理解了。
java中clone的用法在Java中,clone()方法用于创建并返回一个对象的副本。
通过使用clone()方法,我们可以在不通过构造函数创建新实例的情况下,复制已有实例的内容。
这个副本对象与原始对象在内部状态方面具有相同的值,但它是一个独立的对象,对它的修改不会影响原始对象。
要使用clone()方法,我们需要确保类实现了Cloneable接口,该接口是一个标记接口,表示类的实例可以被克隆。
在实现Cloneable接口的类中,我们可以覆盖Object类中的clone()方法以返回所需的副本对象。
clone()方法使用浅拷贝的方式进行复制,这意味着引用类型的成员变量仍然是原始对象和副本对象之间共享的。
如果我们需要实现深拷贝,即复制所有成员变量的副本,则需要通过重写clone()方法来实现。
在重写clone()方法时,我们可以通过先克隆基本类型成员变量,然后再克隆引用类型成员变量来实现深拷贝。
在Java中,我们可以通过两种方式执行对象的克隆操作:浅克隆和深克隆。
浅克隆只复制对象的字段本身,而不复制它们所引用的对象。
深克隆则会递归地复制整个对象图,包括所有嵌套对象。
要使用clone()方法进行克隆,我们可以在需要克隆的对象上调用clone()方法,并捕获可能抛出的CloneNotSupportedException异常。
我们也可以选择在类中实现自定义的clone()方法来控制克隆的行为并返回所需的副本对象。
总之,Java中的clone()方法提供了一种实现对象克隆的简单方式。
通过克隆,我们可以创建对象的副本,而无需显式地通过构造函数创建新实例。
但我们需要注意克隆操作在涉及引用类型成员变量时的浅拷贝行为,并根据需要考虑实现深拷贝。
Java中的数组复制(clone与arraycopy)代码详解JAVA数组的复制是引⽤传递,⽽并不是其他语⾔的值传递。
1、cloneprotectedObjectclone()throwsCloneNotSupportedException创建并返回此对象的⼀个副本。
“副本”的准确含义可能依赖于对象的类。
这样做的⽬的是,对于任何对象x,表达式:x.clone()!=x为true,表达式:x.clone().getClass()==x.getClass()也为true,但这些并⾮必须要满⾜的要求。
⼀般情况下:x.clone().equals(x)为true,但这并⾮必须要满⾜的要求。
按照惯例,返回的对象应该通过调⽤super.clone获得。
如果⼀个类及其所有的超类(Object除外)都遵守此约定,则x.clone().getClass()==x.getClass()。
按照惯例,此⽅法返回的对象应该独⽴于该对象(正被复制的对象)。
要获得此独⽴性,在super.clone返回对象之前,有必要对该对象的⼀个或多个字段进⾏修改。
这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使⽤对副本的引⽤替换对这些对象的引⽤。
如果⼀个类只包含基本字段或对不变对象的引⽤,那么通常不需要修改super.clone返回的对象中的字段。
Object类的clone⽅法执⾏特定的复制操作。
⾸先,如果此对象的类不能实现接⼝Cloneable,则会抛出CloneNotSupportedException。
注意,所有的数组都被视为实现接⼝Cloneable。
否则,此⽅法会创建此对象的类的⼀个新实例,并像通过分配那样,严格使⽤此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被⾃我复制。
所以,此⽅法执⾏的是该对象的“浅表复制”,⽽不“深层复制”操作。
Object类本⾝不实现接⼝Cloneable,所以在类为Object的对象上调⽤clone⽅法将会导致在运⾏时抛出异常。
java中clone的用法(一)Java中clone的用法概述在Java中,clone是一种用于复制对象的操作。
通过使用clone方法,可以创建一个新的对象,该对象的值和被复制对象相同,但是它们是两个独立的实例。
在使用clone方法时,需要注意一些规则和注意事项。
用法以下是Java中clone的一些常见用法:1.实现Cloneable接口:Java中的clone方法是定义在Cloneable接口中的。
在使用clone方法之前,需要确保被复制的对象的类实现了Cloneable接口,并且重写了clone方法。
否则,在调用clone方法时会抛出CloneNotSupportedException异常。
2.重写clone方法:在复制对象时,需要重写clone方法。
重写clone方法时,应该先调用方法获得被复制对象的副本,然后再对需要改变的属性进行相应修改。
@Overrideprotected Object clone() throws CloneNotSupportedEx ception {// 调用父类的clone方法获取被复制对象的副本MyClass cloned = (MyClass) ();// 对需要改变的属性进行相应修改= ();return cloned;}3.浅拷贝和深拷贝:在默认情况下,clone方法执行的是浅拷贝,即复制对象的字段,但是不复制引用对象本身。
如果需要实现深拷贝,即复制引用对象本身,需要在clone方法中手动对引用对象进行克隆。
4.使用clone方法复制对象:使用clone方法复制对象时,可以通过调用被复制对象的clone方法实现。
被复制对象的类应该实现Cloneable接口并重写clone方法。
MyClass original = new MyClass();MyClass cloned = ();5.clone方法的返回类型:clone方法的返回类型是Object,因此在使用复制后的对象时,需要进行类型转换。
javaclone方法Java中的clone方法是一个用于创建对象副本的重要方法。
它是Object类中的一个方法,被所有Java类继承并且默认实现。
clone 方法的作用是创建并返回一个与原始对象具有相同状态的新对象,新对象与原始对象是独立的,对新对象的修改不会影响原始对象。
在Java中,clone方法是一种浅拷贝的方式。
浅拷贝是指在创建副本时,只复制原始对象中的基本数据类型和引用类型的地址,而不复制引用类型对象本身。
这意味着,通过clone方法创建的新对象与原始对象共享引用类型对象,对引用类型对象的修改会同时影响到原始对象和新对象。
为了实现克隆功能,需要满足以下两个条件:1. 实现Cloneable接口:该接口是一个标记接口,没有任何方法,只是用于标识一个类可以被克隆。
2. 重写clone方法:在类中重写Object类的clone方法,并将其访问修饰符改为public。
下面是一个简单的示例,展示了如何使用clone方法创建对象的副本:```javapublic class Student implements Cloneable {private String name;private int age;public Student(String name, int age) { = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}public static void main(String[] args) {Student student1 = new Student("Alice", 20);try {Student student2 = (Student) student1.clone();System.out.println("原始对象name:" + student1.getName() + " age:" + student1.getAge());System.out.println("克隆对象name:" + student2.getName() + " age:" + student2.getAge());} catch (CloneNotSupportedException e) {e.printStackTrace();}}}```在上面的示例中,我们创建了一个名为Student的类,该类实现了Cloneable接口并重写了clone方法。
深⼊理解Java中的克隆前⾔Java克隆(Clone)是Java语⾔的特性之⼀,但在实际中应⽤⽐较少见。
但有时候⽤克隆会更⽅便更有效率。
对于克隆(Clone),Java有⼀些限制:1、被克隆的类必须⾃⼰实现Cloneable 接⼝,以指⽰Object.clone() ⽅法可以合法地对该类实例进⾏按字段复制。
Cloneable 接⼝实际上是个标识接⼝,没有任何接⼝⽅法。
2、实现Cloneable接⼝的类应该使⽤公共⽅法重写Object.clone(它是受保护的)。
某个对象实现了此接⼝就克隆它是不可能的。
即使 clone ⽅法是反射性调⽤的,也⽆法保证它将获得成功。
3、在ng.Object类中克隆⽅法是这么定义的:protected Object clone()throws CloneNotSupportedException创建并返回此对象的⼀个副本。
表明是⼀个受保护的⽅法,同⼀个包中可见。
按照惯例,返回的对象应该通过调⽤super.clone 获得。
Java中的赋值在Java中,赋值是很常⽤的,⼀个简单的赋值如下//原始类型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其实是有点奇怪的,它不同与我们常⽤到的接⼝,它内部不包含任何⽅法,它仅仅是⼀个标记接⼝。
其源码如下public interface Cloneable {}关于cloneable,需要注意的1、如果想要⽀持clone,就需要实现Cloneable 接⼝2、如果没有实现Cloneable接⼝的调⽤clone⽅法,会抛出CloneNotSupportedException异常。
java克隆对象clone()的用法和作用电脑资料假设需修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本,假设需修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本。
这也是本地副本最常见的一种用途。
假设决定制作一个本地副本,只需简单地使用clone()方法即可。
Clone是“克隆”的意思,即制作完全一模一样的副本。
这个方法在根底类Object中定义成“protected”(受保护)模式。
但在希望克隆的任何衍生类中,必须将其覆盖为“public”模式。
例如,标准库类Vector覆盖了clone(),所以能为Vector调用clone(),如下所示:clone()方法产生了一个Object,后者必须立即重新造型为正确类型。
这个例子指出Vector的clone()方法不能自动尝试克隆Vector内包含的每个对象——由于别名问题,老的Vector和克隆的Vector都包含了相同的对象。
我们通常把这种情况叫作“简单”或者“浅层”,因为它只了一个对象的“外表”局部。
实际对象除包含这个“外表”以外,还包括句柄指向的所有对象,以及那些对象又指向的其他所有对象,由此类推。
这便是“对象网”或“对象关系网”的由来。
假设能下所有这张网,便叫作“全面”或者“深层”。
在输出中可看到浅层的结果,注意对v2采取的行动也会影响到v:一般来说,由于不敢保证Vector里包含的对象是“可以克隆”(注释②)的,所以最好不要试图克隆那些对象。
尽管克隆方法是在所有类最根本的Object中定义的,但克隆仍然不会在每个类里自动进行。
这似乎有些,因为根底类方法在衍生类里是肯定能用的。
但Java确实有点儿反其道而行之;如果想在一个类里使用克隆方法,唯一的方法就是专门添加一些代码,以便保证克隆的正常进行。
使用protected时的技巧为防止我们创立的每个类都默认具有克隆能力,clone()方法在根底类Object里得到了“保存”(设为protected)。
深入理解java中的clone如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。
预备知识为了理解java的clone,有必要先温习以下的知识。
java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。
java引用类型的存储,java的引用类型都是存储在堆上的。
public class B {int a;String b;public B(int a, String b) {super();this.a = a;this.b = b;}}对这样一个引用类型的实例,我们可以推测,在堆上它的内存存储形式(除去指向class的引用,锁的管理等等内务事务所占内存),应该有一个int值表示a,以及一个引用,该引用指向b在堆上的存储空间。
为什么要clone恩,因为需要。
废话。
有名的GoF设计模式里有一个模式为原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.简单的说就是clone一个对象实例。
使得clone出来的copy和原有的对象一模一样。
插一个简单使用clone的例子,如果一个对象内部有可变对象实例的话,public API不应该直接返回该对象的引用,以防调用方的code改变该对象的内部状态。
这个时候可以返回该对象的clone。
问题来了,什么叫一模一样。
一般来说,有x.clone() != x (存储的位置不一样)x.clone().getClass() == x.getClass() (类型是一样的)x.clone().equals(x) (内容是一样的)但是这些都不是强制的。
我们需要什么样的clone就搞出什么样的clone好了。
一般而言,我们要的clone应该是这样的。
copy和原型的内容一样,但是又是彼此隔离的。
即在clone之后,改变其中一个不影响另外一个。
Object的clone以及为什么如此实现Object的clone的行为是最简单的。
以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。
看例子。
package c.d.e;class User {String name;int age;}class Account implements Cloneable {User user;long balance;@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}}public class Test {public static void main(String[] args) throws CloneNotSupportedException {// user.User user = new User(); = "user";user.age = 20;// account.Account account = new Account();er = user;account.balance = 10000;// copy.Account copy = (Account) account.clone();// balance因为是primitive,所以copy和原型是相等且独立的。
System.out.println(copy.balance==account.balance copy.balance = 20000;// 改变copy不影响原型。
System.out.println(copy.balance !=account.balance);// user因为是引用类型,所以copy和原型的引用是同一的。
System.out.println((er ==er)); = "newName";// 改变的是同一个东西。
System.out.println();}}恩,默认实现是帮了我们一些忙,但是不是全部。
primitive的确做到了相等且隔离。
引用类型仅仅是复制了一下引用,copy和原型引用的东西是一样的。
这个就是所谓的浅copy了。
要实现深copy,即复制原型中对象的内存copy,而不仅仅是一个引用。
只有自己动手了。
等等,是不是所有的引用类型都需要深copy呢?不是!我们之所以要深copy,是因为默认的实现提供的浅copy不是隔离的,换言之,改变copy的东西,会影响到原型的内部。
比如例子中,改变copy的user的name,影响了原型。
如果我们要copy的类是不可变的呢,如String,没有方法可以改变它的内部状态呢。
package c.d.e;class User {String name;int age;}class Account implements Cloneable {User user;long balance;String str1;@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}}public class Test {public static void main(String[] args) throws CloneNotSupportedException {// user.User user = new User(); = "user";user.age = 20;// account.Account account = new Account();er = user;account.balance = 10000;account.str1 = "ljh";// copy.Account copy = (Account) account.clone();// balance因为是primitive,所以copy和原型是相等且独立的。
System.out.println(copy.balance==account.balance copy.balance = 20000;// 改变copy不影响原型。
System.out.println(copy.balance !=account.balance);// user因为是引用类型,所以copy和原型的引用是同一的。
System.out.println((er ==er));System.out.println(copy.str1); = "newName";// 改变的是同一个东西。
System.out.println();// String为不可变类。
没有办法可以通过对的字符串的操作改变这个字符串。
// 改变引用新的对象不会影响原型。
copy.str1 = "cmx";System.out.println(copy.str1);, "bbb", "ccc"};String [] str4 = str3.clone();for (int i=0; i < str3.length; i++) System.out.println(str4[i]);}}可见,在考虑clone时,primitive和不可变对象类型是可以同等对待的。
java为什么如此实现clone呢?也许有以下考虑。
1 效率和简单性,简单的copy一个对象在堆上的的内存比遍历一个对象网然后内存深copy明显效率高并且简单。
2 不给别的类强加意义。
如果A实现了Cloneable,同时有一个引用指向B,如果直接复制内存进行深copy的话,意味着B在意义上也是支持Clone的,但是这个是在使用B的A中做的,B甚至都不知道。
破坏了B原有的接口。
3 有可能破坏语义。
如果A实现了Cloneable,同时有一个引用指向B,该B实现为单例模式,如果直接复制内存进行深copy的话,破坏了B的单例模式。
4 方便且更灵活,如果A引用一个不可变对象,则内存deep copy是一种浪费。
Shadow copy给了程序员更好的灵活性。
clone()对Object类对象本身是不可见的。
所以你会发现找不到clone()方法原因是:clone()方法是protected访问权限如何cloneclone三部曲。
1 声明实现Cloneable接口。
2 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。
3 把浅copy的引用指向原型对象新的克隆体。
给个例子。
class User implements Cloneable {String name;int age;@Overridepublic User clone() throws CloneNotSupportedException {return (User) super.clone();}}class Account implements Cloneable {User user;long balance;@Overridepublic Account clone() throws CloneNotSupportedException {Account account = null;account = (Account) super.clone();if (user != null) {er = user.clone();}return account;}}对clone的态度clone嘛,我觉得是个好东西,毕竟系统默认实现已经帮我们做了很多事情了。
但是它也是有缺点的。
1 手工维护clone的调用链。
这个问题不大,程序员有责任做好。
2 如果class的field是个final的可变类,就不行了。
三部曲的第三步没有办法做了。
考虑一个类对clone的态度,有如下几种。
1 公开支持:好吧,按照clone三部曲实现吧。
前提是父类支持(公开或者默默)。
2 默默支持:不实现Cloneable接口,但是在类里面有正确的protected的clone实现,这样,该类不支持clone,但是它的子类如果想支持的话也不妨碍。