万物皆对象(下)

对象的强引用,软引用,弱引用和虚引用

Java中是JVM负责内存的分配和回收,这样虽然使用方便,程序不用再像使用c那样操心内存,但同时也是它的缺点(不够灵活)。为了解决内存操作不灵活这个问题,可以采用软引用等方法。

先介绍一下这四种引用:

在实际开发中,弱引用和虚引用不常用,用得比较多的是软引用,因为它可以加速jvm的回收。

软引用的使用方式:

这里写图片描述

关于软引用,我之后会单独写一篇文章,所以这里先一笔带过。

对象的复制

java除了用new来创建对象,还可以通过clone来复制对象。

那么这两种方式有什么相同和不同呢?

new操作符的本意是分配内存。程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。

如何利用clone的方式来得到一个对象呢?

看代码:

这里写图片描述

对Person类做了一些修改

看实现代码:

这里写图片描述

这样就得到了一个和原来一样的新对象。

深复制和浅复制

但是,细心并且善于思考的人可能一经发现了一个问题。

age是一个基本数据类型,支架clone没什么问题,但是name可是一个String类型的啊。我们clone后的对象里的name和原来对象的name是不是指向同一个字符串常量呢?

做个试验:

这里写图片描述

果然,是同一个对象。如果你不能理解,那么看这个图。

这里写图片描述

其实如果只是String还好,因为String的不可变性,当你随便修改一个值的时候,他们就会指向不同的地址了,但是除了String,其他都是可变的。这就危险了。

上面的这种情况,就是浅克隆。这种方式在你的属性列表中有其他对象的引用的时候其实是很危险的。所以,我们需要深克隆。也就是说我们需要将这个对象里的对象也clone一份。怎么做呢?

在内存中通过字节流的拷贝是比较容易实现的。把母对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与母对象之间并不存在引用共享的问题,真正实现对象的深拷贝。

//使用该工具类的对象必须要实现 Serializable 接口,否则是没有办法实现克隆的。
public class CloneUtils {
public static <T extends Serializable> T clone(T obj){
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新对象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}

使用该工具类的对象只要实现 Serializable 接口就可实现对象的克隆,无须继承 Cloneable 接口实现 clone() 方法。

测试一下:

这里写图片描述

很完美

这个时候,Person类实现了Serializable接口

是否使用复制,深复制还是浅复制看情况来使用。

关于序列化与反序列化以后会讲。


这篇文章到这里就暂时告一段落了,后续有补充的话我会继续补充,有错误的话,我也会及时改正。欢迎大家提出问题。

事例代码放在github:https://github.com/CleverFan/JavaImprove