implements cloneable ,(实现此接口表示该java类支持克隆,如果没有实现此接口就
调用了clone方法,则会报出来clonenotsupportedexception异常)
覆写object类中的clone(),并将可见性从protect改为public
浅拷贝和深拷贝出来新类
考虑原类和新类的 成员变量是否独立
(两种类型:primitive类型成员变量,引用类型成员变量)
浅拷贝和深拷贝的区别:类中的引用对象是否也implements cloneable, 是否也覆写了
object类中的clone()
相同点:原类implements cloneable, 是否也覆写了object类中的clone()
预备知识?????
java的类型,java的类型分为两大类,一类为primitive,如int,另一类为引用类型,如String,Object等等。
java引用类型的存储,java的引用类型都是存储在堆上的。
1.浅复制与深复制概念
⑴浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
{
int a;
String b;
public B(int a, String b)
{
super();
this.a = a;
this.b = b;
}
}
对这样一个引用类型的实例,我们可以推测,在堆上它的内存存储形式(除去指向class的引用,锁的管理等等内务事务所占内存),应该有一个int值表示a,以及一个引用,该引用指向b在堆上的存储空间。
为什么要clone?????
有名的GoF设计模式里有一个模式为原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
2.Java的clone()方法
⑴clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象
②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样,都是同一个类new出来的不同对象!!!
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
⑵Java中对象的克隆
①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
②在派生类中覆盖基类的clone()方法,并声明为public。
③在派生类的clone()方法中,调用super.clone()。
④在派生类中实现Cloneable接口。
一般而言,我们要的clone应该是这样的。copy和原型的内容一样,但是又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。
Object的clone以及为什么如此实现????????
Object的clone的行为是最简单的。以堆上的内存存储解释的话(不计内务内存),对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。
看例子。
java.lang.Cloneable 接口是一个空接口,该接口用来指明一个对象是否可以进行克隆.实现了该接口的对象可以调用clone()方法来进行对象的浅克隆.
通过调用父类的super.clone()方法(浅拷贝)可以重新生成一个对象,解决因对象引用赋值造成的原对象的修改.
①为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
②继承自java.lang.Object类的clone()方法是浅复制。以下代码可以证明之。
浅拷贝(考虑成员变量是否独立)
类的浅拷贝出来新的类
primitive类型成员变量在两个类中是独立的,虽然值是相等的,但
是改变其中一个类中的primitive值不会影响另一个类的primitive值。
引用类型成员变量在两个类中是同一个,也是同一个堆空间,
即:改变其中一个类中的引用值,也就是改变了另一个类的引用值。
String name;
int age;
}
class Account implements Cloneable
User user;
long balance;
@Override
public Object clone() throws CloneNotSupportedException
return super.clone();
}
}
user.name = "user";
user.age = 20;
Account account = new Account();
account.user = user;
account.balance = 10000;
// copy.
Account copy = (Account) account.clone();
// balance因为是primitive,所以copy和原型是相等且独立的。
Assert.assertEquals (copy.balance,account. balance); //为真!
copy.balance =20000;
// 改变copy不影响primitive类型。
Assert.assertTrue (copy.balance != account.balance); //为真!
// user因为是引用类型,所以copy和原型的引用是同一的。
Assert.assertTrue(copy.user == account.user); //为真!
copy.user.age = 22;
// 改变的是同一个东西。
Assert.assertEquals (copy.user.age, account.user.age); //为真!
又一例子如下:
class User implements Cloneable
{
String name;
int age;
@Override
publicObject clone() throws CloneNotSupportedException
{
return super.clone();
}
}
//测试类如下:
User user = new User();
user.name = "user";
user.age = 20;
// copy
User copy = (User) user.clone();
// age因为是primitive,所以copy和原型是相等且独立的。
Assert.assertEquals(copy.age, user.age); //为真!
copy.age = 30;
// 改变copy不影响原型。
Assert.assertTrue(copy.age != user.age); //为真
// name因为是引用类型,所以copy和原型的引用是同一的。
Assert.assertTrue(copy.name == user.name); //为真!
// String为不可变类。没有办法可以通过对copy.name的字符串的操作改变这个字符串。
// 改变引用新的对象不会影响原型。
copy.name = "newname"; //这里应该会报错吧
Assert.assertEquals("newname", copy.name); //为假,因为string类型不可变!
Assert.assertEquals("user", user.name); //为真!
可见,在考虑浅clone时,primitive和不可变对象类型是可以同等对待的。
类的深拷贝出来新的类
primitive类型成员变量在两个类中是独立的,虽然值是相等的,但
是改变其中一个类中的primitive值不会影响另一个类的primitive
值。
引用类型成员变量在两个类中是独立的,是两个不同的堆空间,
即:改变其中一个类中的引用值不会影响另一个类的引用类型。
class User implements Cloneable {
String name;
int age;
@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
class Account implements Cloneable {
User user;
long balance;
@Override
public Account clone() throws CloneNotSupportedException {
Account account = null;
account = (Account) super.clone();
if (user != null) {
account.user = user.clone();