在某一时刻对象A中已经包括了一些有效值
,此时可能会需要一个和A完全相同的新对象B,并且对B任何改动不会影响到A的值
A和B是两个对象,但B的初始值由A确定.
在Java中用简单的赋值语句不能满足这样的需求,
可以new一个新对象B,给B赋值,但是这样比较麻烦
clone()最简单最高效
new的本意是分配内存,程序执行到new操作符时,实现去看new操作符后面的类型,
因为真的了类型才知道分配多大的内存空间,分配内存后
,再调用构造函数,填充对象的各个域
这就是对象的初始化
构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象
clone第一步也是分配内存,分配的内存和原对象相同
,然后使用原对象中对应的各个域,填充新对象的域
,然后clone方法返回,一个新的相同的对象创建,同样可以把这个新对象的引用发布到外部
protected修饰的类和属性对于自己,本包及其子类可见 //这样理解是片面的
protected的成员或方法,要分子类和超类是否在同一个包下
不在同一个包中的子类:
只能访问自身从基类继承而来的受保护成员
不能访问基类实例本身的受保护成员
在同一个包下:
protected和public是一样的
如果直接使用调用clone()方法
编译器会报错误:‘clone() has protected access in ‘java.lang.Object’’
\\
public class classroom {
public static void main(String... args){
classroom a = new classroom ();
a.clone();
}
}
class student {
}//注意这里是classroom 调用clone(),不是student调用,编译器就不报protected
而java.lang.Cloneable的源码:
///**
// * A class implements the Cloneable
interface to
// * indicate to the {@link java.lang.Object#clone()} method that it
// * is legal for that method to make a
// * field-for-field copy of instances of that class.
// */
//public interface Cloneable {
//}
这就是一个标识接口
如果不实现Cloneable接口
编译器会报错误:Unhandled exception:java.lang.CloneNotSupportedException
底层是c++语言,处理快
(A对象拷贝后是A1,B对象拷贝后是B1,A对象引用值是B对象)
浅拷贝(浅克隆):
直接将A对象中引用B对象的引用值拷贝给A1对象,
深拷贝(深克隆):
根据A对象中引用指向的B对象创建一个新的相同的B1对象
,将对象A1的引用指向新创建的B1对象
public class Player implements Cloneable {
private String name;
private int age;
private Coach coach;
public Player(String name, int age, Coach coach) {
this.name = name;
this.age = age;
this.coach = coach;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Coach getCoach() {
return coach;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//根据Object的clone()的注解,我们子类重写超类的clone()方法
/*
public Player clone() {
Player player = null;
try {
player = (Player) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return player;
}*/
public static void main(String... args) {
//深拷贝or浅拷贝
Coach coach = new Coach("Popvinch");
Player duncan = new Player("Duncan", 42, coach);
Player clone = null;
try {
clone = (Player) duncan.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//Player clone = (Player) duncan.clone();此处引用99-109---------------------------
clone.getCoach().setName("Kerr");
clone.setName("Curry");
clone.setAge(30);
System.out.println("His name is " + duncan.getName() + ",His age is " + duncan.getAge() + ",His coach is " + duncan.getCoach().getName());
//His name is Duncan,His age is 42,His coach is Kerr
System.out.println("His name is " + clone.getName() + ",His age is " + clone.getAge() + ",His coach is " + clone.getCoach().getName());
//His name is Curry,His age is 30,His coach is Kerr
System.out.println(duncan.getCoach() == clone.getCoach());//true
String result = duncan.getCoach() == clone.getCoach() ? "clone是浅拷贝" : "clone是深拷贝";
System.out.println(result);
//从上面结果可知,克隆出来的Player对象里的name和age是新的,
//通过setName和setAge方法可以改变,但是coach是和原来的共享的,
//对克隆对象的coach进行修改同样会影响到原来对象的coach,
//这就是浅克隆。
//如何进行深克隆?
//请看Boby
//-----------------------
//复制对象or复制引用
Player p = new Player("zhangs", 18, coach);
Player p2 = null;
try {
p2 = (Player) p.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("p " + p);//cloneAndNew.Player@49097b5d
System.out.println("p2 " + p2);//cloneAndNew.Player@6e2c634b
//地址不同,创建了新对象,不是把原对象的地址赋值给了一个新的引用变量
Player p3 = new Player("zhangs", 18, coach);
Player p4 = p3;
System.out.println("p3 " + p3);//cloneAndNew.Player@17a7cec2
System.out.println("p4 " + p4);//cloneAndNew.Player@17a7cec2
//地址一样,所以是同一个对象,
//p3,p4只是引用他们俩都指向相同的对象Player("zhangs",18,coach);
//这就是: 引用的赋值
}
}
class Coach implements Cloneable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Coach(String name) {
this.name = name;
}
}
clone()方法对对象中的String的克隆应该属于深克隆。原因在于String的不可变,比如String str = “java”;,除非你用反射,否则你无法改变“java”任何一个字符。所以 ,当克隆的时候把原型对象中的String引用复制给新对象的引用时,虽然新对象和原型指向相同的内容,但是原型不会影响到克隆对象的内容,从这一角度来说,对String应用的克隆属于深度克隆。
有两种方式:
一:
1.克隆类与克隆类的引用类都实现Cloneable
2.克隆类与引用类重写Object的clone()方法
3.参数
二:
1.克隆类与克隆类的引用类都实现Serialelzable接口
2.用对象的序列化和反序列化流读写对象
下面是第一种方式:
package cloneAndNew;
//实现深拷贝
//1:克隆类与克隆类的引用类都实现Cloneable
//2:克隆类的引用类重写Object的clone()方法
//3:测试
public class Boby implements Cloneable {
private Head head;
public Boby() {
}
public Boby(Head head) {
this.head = head;
}
public Head getHead() {
return head;
}
public void setHead(Head head) {
this.head = head;
}
@Override
protected Object clone() {
Boby newBoby = null;
try {
newBoby = (Boby)
super.clone();
newBoby.head = (Head) head.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return newBoby;
}
public static void main(String[] args) {
Boby boby = new Boby(new Head("zhangs"));
Boby newBoby = null;
try {
newBoby = (Boby) boby.clone();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("boby == newBoby: " + (boby == newBoby));//boby == newBoby: false
System.out.println("boby.head == newBoby.head: " + (boby.head == newBoby.head));
}
}
class Head implements Cloneable {
private String name;
public Head() {
}
public Head(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
拷贝前后引用的对象不同说明拷贝前后引用的对象也发生了拷贝,这样就证明实现了深拷贝.
下面是第二种方式:
//方式二:
//1.实现Serialilzable接口
//2.对象的序列化和反序列化克隆
public class Person implements Serializable {
private static final long serialVersionUID = 123131221321313L;
private String name;
private int age;
private Car car;
public Person() {
}
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Person{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append(", car=").append(car);
sb.append('}');
return sb.toString();
}
public static void main(String[] args) {
try {
Person person = new Person("zhangs", 18, new Car("hah"));
Person newPerson = Person.clone(person);
System.out.println(person.getCar().getCarname());
newPerson.getCar().setCarname("sussess");
System.out.println(newPerson.getCar().getCarname());
System.out.println(person.getCar()== newPerson.car?"不是深克隆":"是");
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj)throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T)ois.readObject();
}
}
class Car implements Serializable{
private static final long serialVersionUID = 123131221321313L;
private String Carname;
public Car() {
}
public Car(String carname) {
Carname = carname;
}
public String getCarname() {
return Carname;
}
public void setCarname(String carname) {
Carname = carname;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Car{");
sb.append("Carname='").append(Carname).append('\'');
sb.append('}');
return sb.toString();
}
}
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对
象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object 类的 clone 方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时
这也是一个标识接口
@SuppressWarnings(“unchecked”)用在方法上,表示压制本方法的警告
@SuppressWarnings(“all”)用在类上,表示压制本类范围的警告
欢迎批斗