谈谈Object中为什么要出现clone()
其实这部分是最难讲清楚的,很多文章只讲了,为什么要用super.clone(),为什么要implements Cloneable,如何clone等等
- 都知道Java没有指针的概念,实际上是有“指针”引用的概念;八大基本数据类型的参数传递是按照值传递(即传递的是参数的一个拷贝,参数的值改变不会影响原值);除此之外的其他类型都是按照引用传递(传递对象的引用,指向原对象);"="传递也是引用传递;
- 实际中当我们将对象作为参数传递时,我们不允许某一个方法对该对象有set的权限,只能get,但是其他方法又可以set对象里面的属性(这只是一种假设),或许又干脆不想别人碰你的对象,所以不得不在对象里提供set方法,即这个对象是可更改的对象,或许这可以用封装(在下面演示自己想到的一种方法)或者aop的思想来实现(其实怎么实现自己也不太熟悉),但仅仅为了这个就用封装或者aop可能显得有些麻烦,这时候你只需要在某些地方传递一份对象的拷贝就可以了,随他怎么玩都跟你没有关系。
演示:
一种利用接口来实现第二点需求(红色字体)的代码演示(这似乎违背了接口出现的初衷,即接口不是为了用来干这个事情的)
package modifyTest;
//将所有的get()抽象出来
public interface Info {
String getName();
int getAge();
}
//有一个Person类
package modifyTest;
public class Person implements Info {
private String name;
private int age;
public Person(String name, int typeName) {
this.name = name;
this.age = typeName;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
测试:
package modifyTest;
import org.junit.Test;
public class main {
@Test
public void run(){
Person p = new Person("zhangsan",24);
justGet(p);
canSet(p);
}
//在不允许修改属性的方法里传递接口,那么就只能使用get方法了
private static void justGet(Info inf){
System.out.println("只能获得属性:"+inf.getName()+":"+inf.getAge());
}
//在一般的方法里传递实现类,可以正常访问
private static void canSet(Person p){
System.out.println("原信息:"+p.toString());
p.setName("我改名字了");
System.out.println("修改后:"+p.toString());
}
}
运行结果:
只能获得属性:zhangsan:24
原信息:Person{name='zhangsan', age='24'}
修改后:Person{name='我改名字了', age='24'}
对象的拷贝
- 前面讲了,对象的传递是引用传递,所以一般的赋值法肯定不行的,所以出现了clone,即将对象复制一份。在讲clone()之前,先谈谈另外一种拷贝对象的思路:叫做拷贝构造函数的方式拷贝一个对象,代码演示如下:
-
package CopyConstructors; /* 想要复制一个对象,无非就是复制对象的成员而已,我们可以通过重载构造函数的方式实现 */ public final class Galaxy { private Double mass; private final String name; /** * 正常的构造函数 */ public Galaxy(Double mass, String name) { this.mass = mass; this.name = name; } /** * 拷贝构造函数 */ public Galaxy(Galaxy galaxy) { this(galaxy.getMass(), galaxy.getName()); //no defensive copies are created here, since //there are no mutable object fields (String is immutable) } /** * 或者利用newInstance的方法 */ public static Galaxy newInstance(Galaxy galaxy) { return new Galaxy(galaxy.getMass(), galaxy.getName()); } public Double getMass() { return mass; } /* 这是唯一一个能修改Galaxy的属性(状态)的方法,如果这个方法都没有了,拷贝构造函数就没有了意义 因为不可变的对象不需要。 */ public void setMass(Double aMass){ mass = aMass; } public String getName() { return name; } //测试 public static void main (String... args){ Galaxy m101 = new Galaxy(15.0, "M101"); Galaxy m101CopyOne = new Galaxy(m101); m101CopyOne.setMass(25.0); Galaxy m101CopyTwo = Galaxy.newInstance(m101); m101CopyTwo.setMass(35.0); log("M101 mass: " + m101.getMass()); log("M101CopyOne mass: " + m101CopyOne.getMass()); log("M101CopyTwo mass: " + m101CopyTwo.getMass()); } private static void log(String msg) { System.out.println(msg); } }
M101 mass: 15.0
M101CopyOne mass: 25.0
M101CopyTwo mass: 35.0
通过这种方式实现了对象的简单复制,但是如果对象里面含有引用对象呢?那么我们就要在get该属性的时候,get一个new出来的引用,且这个引用的初始值就是本类对象当前的值,这样返回的就不是本类的引用而是和本类引用一样的新的对象,这样在拷贝构造函数的时候拷贝的该引用也是一个新的sub对象。(感觉这里没有阐述清楚,因为不是特别想说太多)。
-
谈谈如何使用clone()
现在来谈clone,关于clone这里不说太多,有很多写的很好的博文,只是这里自己想看看源码,也算是更深一步了解Object吧
protected native Object clone() throws CloneNotSupportedException;
package java.lang;
public interface Cloneable {
}
如何使用:
import org.junit.Test;
import java.io.Serializable;
/**
* Person类
*
*/
class Person extends Object implements Cloneable {
private static final long serialVersionUID = -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
/**
* 演示clone()
* @return
*/
@Override
public Object clone(){
Object o=null;
try {
o = (Object)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
@Override
public String toString() {
return "Info [name=" + name + ", age=" + age + "]";
}
}
public class CloneTest {
/**
* object的clone()方法实现对象的浅复制
*/
@Test
public void Test() {
Person p1 = new Person("zhang san",34);
System.out.println("p1:"+p1);
System.out.println("将p1 克隆 得到新对象p2: ");
Person p2 = (Person)p1.clone();
System.out.println("p1:"+p1);
System.out.println("p2:"+p2);
}
}
-
谈谈clone()在什么情况下出现什么问题(有引用对象时,浅复制不行,需要深复制)
当对象的属性包含引用对象时,就出现问题了
package SerializableTest;
import org.junit.Test;
import java.io.Serializable;
/**
* Person类
*
*/
class Person extends Object implements Serializable,Cloneable {
private static final long serialVersionUID = -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
private Car car; // 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
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;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
/**
* 演示clone()
* @return
*/
@Override
public Object clone(){
Object o=null;
try {
o = (Object)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
@Override
public String toString() {
return "Info [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
/**
* 汽车类
* @author
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
}
}
public class CloneTest {
/**
* object的clone()方法实现对象的浅复制
*/
@Test
public void Test() {
Person p1 = new Person("zhang san",34,new Car("IHG",330));
System.out.println("p1:"+p1);
System.out.println("将p1 克隆 得到新对象p2: ");
Person p2 = (Person)p1.clone();
System.out.println("p1:"+p1);
System.out.println("p2:"+p2);
System.out.println("修改p2的Car属性的brand属性为Lj:");
//修改克隆的Car引用对象的属性(考察这是浅复制还是深复制)
p2.getCar().setBrand("Lj");
System.out.println("p1:"+p1);
System.out.println("p2:"+p2);
}
}
结果:
p1:Info [name=zhang san, age=34, car=Car [brand=IHG, maxSpeed=330]]
将p1 克隆 得到新对象p2:
p1:Info [name=zhang san, age=34, car=Car [brand=IHG, maxSpeed=330]]
p2:Info [name=zhang san, age=34, car=Car [brand=IHG, maxSpeed=330]]
修改p2的Car属性的brand属性为Lj:
p1:Info [name=zhang san, age=34, car=Car [brand=Lj, maxSpeed=330]]
p2:Info [name=zhang san, age=34, car=Car [brand=Lj, maxSpeed=330]]
-
第一种深复制(略过,通过new,依次get,set)
-
第二种(在重写clone方法的时候,对引用对象也进行clone)
上述clone()修改为:
/**
* 演示clone()
* @return
*/
@Override
public Object clone(){
Person o=null;
try {
o = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
o.car= new Car(o.getCar().getBrand(),o.getCar().getMaxSpeed());
return o;
}
即可
-
第三种:
-
介绍序列化与反序列化(概念,能做什么,如何做,优势)
- Java序列化和反序列化
-
如何做?
代码演示:
一个工具类,即利用序列化复制对象
package SerializableTest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 利用序列化与反序列化实现对象的深度克隆的工具类
*
*/
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
public static <T> T clone(T obj) throws Exception {
//这几部实现序列化(将要被克隆的对象写进一个字节数组中)
//创建字节数组输出流
ByteArrayOutputStream bout = new ByteArrayOutputStream();
//这是序列化要使用的对象,以字节输出流作bout为构造参数
ObjectOutputStream oos = new ObjectOutputStream(bout);
//将要被克隆(序列化)的对象写进一个内部的字节数组中
oos.writeObject(obj);
//进行反序列化,在这里其实就是进行克隆
//创建字节数组的输入流,构造参数是bout内部的那个字节数组
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
//这是反序列化需要使用的对象,以bin为参数来构造
ObjectInputStream ois = new ObjectInputStream(bin);
//进行反序列化(即把之前的字节数据读取,并返回一个Object对象)
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}
将之前的代码修改过后,利用序列化和反序列化实现了对象的深复制
package SerializableTest;
import org.junit.Test;
import java.io.Serializable;
/**
* Person类
*
*/
class Person extends Object implements Serializable,Cloneable {
private static final long serialVersionUID = -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
private Car car; // 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
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;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
/**
* 演示clone()
* @return
*/
@Override
public Object clone(){
Person o=null;
try {
o = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
o.car= new Car(o.getCar().getBrand(),o.getCar().getMaxSpeed());
return o;
}
@Override
public String toString() {
return "Info [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
/**
* 汽车类
* @author
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
}
}
public class CloneTest {
public static void main(String[] args) {
try {
Person p1 = new Person("SL Ren", 25, new Car("Benz", 300));
Person p2 = MyUtil.clone(p1);// 深度克隆
System.out.println("克隆后的p1:"+p1);
System.out.println("克隆对象p2:"+p2);
System.out.print("p1==p2?:");
System.out.println(p1==p2);
System.out.println("-----------------------------------------");
p2.getCar().setBrand("KDFIO");
p2.getCar().setMaxSpeed(400);
p2.setName("rsl");
// 修改克隆的Person对象p2关联的汽车对象的品牌属性
// 原来的Person对象p1关联的汽车不会受到任何影响
// 因为在克隆Person对象时其关联的汽车对象也被克隆了
System.out.println("更改克隆对象P2后的结果:");
System.out.println("p1:"+p1);
System.out.println("p2:"+p2);
} catch (Exception e) {
e.printStackTrace();
}
}
}