Java 基础接口(Serializable、Cloneable、RandomAccess)
2017-12-22 00:00
399 查看
一、 java.io.Serializable 接口
public interface Serializable { }
实现 java.io.Serializable 接口的类是可序列化的。
序列化类的所有子类本身都是可序列化的。
Java序列化的目的主要有: 1.网络传输 2.对象持久化;
基于Java提供的ObjectInputStream/ObjectOutputStream 可以直接将Java对象作为可存储的字节数组写入文件,也可以传输到网络上。
基于JDK默认的序列化机制可以避免直接操作底层的字节数组,从而提升开发效率;
当进行远程跨进程服务调用时,需要把被传输的Java对象编码为字节数组或者ByteBuffer对象,而当远程服务读取到ByteBuffer对象或者字节数组时,需要将其解码为发送时的Java对象,这被称为Java编解码技术;
# Java 序列化的缺点
1. 无法跨语言; RPC框架往往需要支持跨语言调用;
2. 序列化后的码流太大;
3. 序列化性能太低
# 编解码框架的优劣:
1. 是否支持跨语言,支持的语言种类是否丰富
2. 编码后的码流大小
3. 编解码的性能
4. 类库是否小巧,API使用是否方便
5. 使用者需要手工开发的工作量和难度
XML 解析的时间开销 和 XML 为了可读性而牺牲的空间开销都非常大,因此不适合做高性能的通信协议;
# Google 的 ProtoBuf 使用 二进制编码
# FaceBoook 的 Thrift
# JBoss Marshalling
二、java.lang.Cloneable 接口
public interface Cloneable { }
Cloneable是一个标记接口,没有定义任何方法。原因是: 在Java中,所有类的终极父类 java.lang.Object 已经将clone()方法定义为所有类都应该具有的基本功能,只是将该方法声明为了protected类型。该方法定义了逐字段拷贝实例的操作。它是一个native本地方法,因此没有实现体,而且在拷贝字段时,除了Object类的字段外,其子类的新字段也将被拷贝到新的实例中。
既然Object类中已经有了 clone() 方法,为什么还是需要让想具备实力拷贝功能的类实现Cloneable接口呢?其实,Cloneable接口在这里起到了一种标识的作用,表明实现它的类具备了实例拷贝功能,在Cloneable接口的官方javadoc文档中有这样一段话:
"Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown. JDK1.8"
也就是说,如果一个类不实现该接口就直接调用clone()方法的话,即便已将clone()方法重写为public,那还是会抛出“CloneNotSupportedException”异常。因此,要想使一个类具备拷贝实例的功能,那么除了要重写Object类的clone()方法 ,把clone()方法修改为public外,还必须要实现Cloneable接口;
1. 浅拷贝
在java中,对象创建后需要有一个引用变量来指向该对象实际的地址空间,也就是说引用变量与对象实体是两个不同的数据体。在Object类的clone()方法中,对对象字段进行复制时,如果字段是基本数据类型(如int、double等),则会复制字段的值到一个新的变量中,而字段是引用类型,则仅会将引用值复制给新对象中的相应字段中,也就是说,两个字段指向了同一个对象实例。package com.fast.java.a00; import org.junit.Test; public class Clone_Test { /** * 在java中,对象创建后需要有一个引用变量来指向该对象实际的地址空间,也就是说引用变量与对象实体 * 是两个不同的数据体。在Object类的clone()方法中,对对象字段进行复制时,如果字段是基本数据类型 * (如int、double等),则会复制字段的值到一个新的变量中,而字段是引用类型,则仅会将引用值复制 * 给新对象中的相应字段中,也就是说,两个字段指向了同一个对象实例。 */ @Test public void test_ShallowClone() throws CloneNotSupportedException { UserInfo dataObj = new UserInfo("original data"); StringBuffer strBuf = new StringBuffer("origin data"); ShallowCloneClass objOrigin = new ShallowCloneClass(dataObj, 1.0, "original", strBuf); ShallowCloneClass objCopy = null; Object objTemp = objOrigin.clone(); if (objTemp instanceof ShallowCloneClass) { objCopy = (ShallowCloneClass)objTemp; } /** * false说明copy和org是两个不同的对象实例 * (对于引用类型的比变量,“==”判断的是对象地址是否相同,也就是是不是指向了同一个对象) */ System.out.println("objCopy == objOrigin? " + (objCopy == objOrigin)); System.out.println(); System.out.println("data of objOrigin:"); objOrigin.show(); System.out.println(); System.out.println("data of objCopy:"); objCopy.show(); /** * 但是他们字段的值却是相同的。objCopy和objOrigin各字段的“==”判断全为true也说明本地Object本地 * clone()对实例引用型字段进行的是浅拷贝。 */ System.out.println(); System.out.println("objOrigin.dataObj == objCopy.dataObj? " + (objOrigin.dataObj == objCopy.dataObj)); System.out.println("objOrigin.dataDbl == objCopy.dataDbl? " + (objOrigin.dataDbl == objCopy.dataDbl)); System.out.println("objOrigin.dataStr == objCopy.dataStr? " + (objOrigin.dataStr == objCopy.dataStr)); System.out.println("objOrigin.dataStrBuf == objCopy.dataStrBuf? " + (objOrigin.dataStrBuf == objCopy.dataStrBuf)); // 修改copy中各字段指向对象的属性 objCopy.dataObj.userData = "Copy data"; objCopy.dataDbl = 2.0; objCopy.dataStr = "Copy"; objCopy.dataStrBuf.replace(0, objCopy.dataStrBuf.length(), "Copy data"); /** * 由于CloneableClass的dataObj和dataStrBuf字段均为引用型变量,所以通过objOrigin浅拷贝出来的 * objCopy实例中的dataObj和dataStrBuf字段与 objOrigin 中的相应字段实际上指向的是同一个对象, * 因此,objCopy 对成员对象的修改也就导致了objOrigin 中相应成员对象的随之改变。 * 需要说明的是,dataStr 虽然也是引用型变量,但由于字符串是常量,所以语句 * objCopy.dataStr = “Copy”并没有对原字符串进行修改, * 而是将objCopy的 dataStr字段指向了一个新的字符串“Copy”。 */ System.out.println(); System.out.println("After modify, data of original:"); objOrigin.show(); System.out.println(); System.out.println("After modify, data of copy:"); objCopy.show(); } } class ShallowCloneClass implements Cloneable { public UserInfo dataObj = null; public double dataDbl = 0; public String dataStr = null; public StringBuffer dataStrBuf = null; public ShallowCloneClass(UserInfo dataObj, double dataDbl, String dataStr, StringBuffer dataStrBuf) { this.dataObj = dataObj; this.dataDbl = dataDbl; this.dataStr = dataStr; this.dataStrBuf = dataStrBuf; } public void show() { System.out.println("dataObj = " + dataObj.userData); System.out.println("dataDbl = " + dataDbl); System.out.println("dataStr = " + dataStr); System.out.println("dataStrBuf = " + dataStrBuf); } /** * 重写clone()方法为public类型,并调用Object的本地clone()方法,实现浅拷贝功能 */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } class UserInfo { public String userData = null; public UserInfo(String userData) { this.userData = userData; } } 输出结果: objCopy == objOrigin? false data of objOrigin: dataObj = original data dataDbl = 1.0 dataStr = original dataStrBuf = origin data data of objCopy: dataObj = original data dataDbl = 1.0 dataStr = original dataStrBuf = origin data objOrigin.dataObj == objCopy.dataObj? true objOrigin.dataDbl == objCopy.dataDbl? true objOrigin.dataStr == objCopy.dataStr? true objOrigin.dataStrBuf == objCopy.dataStrBuf? true After modify, data of original: dataObj = Copy data dataDbl = 1.0 dataStr = original dataStrBuf = Copy data After modify, data of copy: dataObj = Copy data dataDbl = 2.0 dataStr = Copy dataStrBuf = Copy data
2. 深拷贝
从上文讲的浅拷贝概念与性质中不难退出深拷贝的含义,那就是对于引用型变量,深拷贝会开辟一块新的内存空间,将被复制引用所指向的对象实例的各个属性复制到新的内存空间中,然后将新的引用指向块内存(也就是一个新的实例)。
要想实现深拷贝的功能,我们在重写clone()方法的时候,就不能再简单地调用Object的本地clone()方法,而是要对上文中的CloneableClass做如下修改:
class DeepCloneClass implements Cloneable { public UserInfo dataObj = null; public double dataDbl = 0; public String dataStr = null; public StringBuffer dataStrBuf = null; public DeepCloneClass(UserInfo dataObj, double dataDbl, String dataStr, StringBuffer dataStrBuf) { this.dataObj = dataObj; this.dataDbl = dataDbl; this.dataStr = dataStr; this.dataStrBuf = dataStrBuf; } public void show() { System.out.println("dataObj = " + dataObj.userData); System.out.println("dataDbl = " + dataDbl); System.out.println("dataStr = " + dataStr); System.out.println("dataStrBuf = " + dataStrBuf); } /** * 重写clone()方法为public类型,对每一个字段都创建一个新的对象,并将原字段指向对象中的属性 * 复制到新的对象中,从而实现深拷贝功能。 */ @Override public Object clone() throws CloneNotSupportedException { UserInfo dataObj = new UserInfo(this.dataObj.userData); double dataDbl = this.dataDbl; String dataStr = new String(this.dataStr); StringBuffer dataStrBuf = new StringBuffer(this.dataStrBuf.toString()); DeepCloneClass deepCopy = new DeepCloneClass(dataObj, dataDbl, dataStr, dataStrBuf); return deepCopy; } } @Test public void test_DeepClone() throws CloneNotSupportedException { UserInfo dataObj = new UserInfo("original"); StringBuffer strBuf = new StringBuffer("origin"); DeepCloneClass objOrigin = new DeepCloneClass(dataObj, 1.0, "original", strBuf); DeepCloneClass objCopy = null; Object objTemp = objOrigin.clone(); if (objTemp instanceof DeepCloneClass) { objCopy = (DeepCloneClass)objTemp; } /** * false说明copy和org是两个不同的对象实例 * (对于引用类型的比变量,“==”判断的是对象地址是否相同,也就是是不是指向了同一个对象) */ System.out.println("objCopy == objOrigin? " + (objCopy == objOrigin)); System.out.println(); System.out.println("objOrigin.dataObj == objCopy.dataObj? " + (objOrigin.dataObj == objCopy.dataObj)); System.out.println("objOrigin.dataDbl == objCopy.dataDbl? " + (objOrigin.dataDbl == objCopy.dataDbl)); System.out.println("objOrigin.dataStr == objCopy.dataStr? " + (objOrigin.dataStr == objCopy.dataStr)); System.out.println("objOrigin.dataStrBuf == objCopy.dataStrBuf? " + (objOrigin.dataStrBuf == objCopy.dataStrBuf)); System.out.println(); // 修改copy中各字段指向对象的属性 objCopy.dataObj.userData = "Copy data"; objCopy.dataDbl = 2.0; objCopy.dataStr = "Copy"; objCopy.dataStrBuf.replace(0, objCopy.dataStrBuf.length(), "Copy data"); System.out.println("After modify, data of original:"); objOrigin.show(); System.out.println(); System.out.println("After modify, data of copy:"); objCopy.show(); } 输出结果: objCopy == objOrigin? false objOrigin.dataObj == objCopy.dataObj? false objOrigin.dataDbl == objCopy.dataDbl? true objOrigin.dataStr == objCopy.dataStr? false objOrigin.dataStrBuf == objCopy.dataStrBuf? false After modify, data of original: dataObj = original dataDbl = 1.0 dataStr = original dataStrBuf = origin After modify, data of copy: dataObj = Copy data dataDbl = 2.0 dataStr = Copy dataStrBuf = Copy data
三、 java.util.RandomAccess 接口
public interface RandomAccess { }
实现 RandomAccess接口, 用来表明其支持快速(通常是固定时间)随机访问。
JDK中推荐的是对List集合尽量要实现RandomAccess接口;
如果集合类是RandomAccess的实现,则尽量用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,在效率上要差一些。反过来,如果List是Sequence List,则最好用迭代器来进行迭代。
JDK中说的很清楚,在对List特别是Huge size的List的遍历算法中,要尽量来判断是属于RandomAccess(如ArrayList)还是Sequence List (如LinkedList),因为适合RandomAccess List的遍历算法,用在Sequence List上就差别很大,常用的作法就是:
if (list instance of RandomAccess) { for(int m = 0; m < list.size(); m++){ } }else{ Iterator iter = list.iterator(); while(iter.hasNext()){ } }
package com.fast.java.a00; import java.util.*; public class RandomAccess_Test { public static void main(String[] args) { ArrayList arraylist = new ArrayList(); LinkedList linkedlist = new LinkedList(); initList(arraylist, 1000); initList(linkedlist, 1000); traverse(arraylist); traverse(linkedlist); System.out.println("迭代 arraylist: "); traverseWithIterator(arraylist); traverseWithLoop(arraylist); System.out.println("迭代 linkedlist: "); traverseWithIterator(linkedlist); traverseWithLoop(linkedlist); } public static void traverse(List list) { long starttime = 0; long endtime = 0; if (list instanceof RandomAccess) { System.out.println("该list实现了RandomAccess接口"); starttime = System.currentTimeMillis(); for (int count = 0; count <= 1000; count++) { for (int i = 0; i < list.size(); i++) { list.get(i); } } endtime = System.currentTimeMillis(); System.out.println("\tLoop 迭代一共花了" + (endtime - starttime) + "ms时间"); } else { System.out.println("该list未实现RandomAccess接口"); starttime = System.currentTimeMillis(); for (int count = 0; count <= 1000; count++) { for (Iterator itr = list.iterator(); itr.hasNext();) { itr.next(); } } endtime = System.currentTimeMillis(); System.out.println("\tIterator 迭代一共花了" + (endtime - starttime) + "ms时间"); } } public static void initList(List list, int n) { for (int i = 0; i < n; i++) { list.add(i); } } //使用循环进行对列表的迭代 public static void traverseWithLoop(List list) { long starttime = 0; long endtime = 0; starttime = System.currentTimeMillis(); for (int count = 0; count <= 1000; count++) { for (int i = 0; i < list.size(); i++) { list.get(i); } } endtime = System.currentTimeMillis(); System.out.println("\t使用loop迭代一共花了" + (endtime - starttime) + "ms时间"); } //使用迭代器对列表进行迭代 public static void traverseWithIterator(List list) { long starttime = 0; long endtime = 0; starttime = System.currentTimeMillis(); for (int count = 0; count <= 1000; count++) { for (Iterator itr = list.iterator(); itr.hasNext();) { itr.next(); } } endtime = System.currentTimeMillis(); System.out.println("\t使用Iterator迭代一共花了" + (endtime - starttime) + "ms时间"); } } 输出结果: 该list实现了RandomAccess接口 Loop 迭代一共花了11ms时间 该list未实现RandomAccess接口 Iterator 迭代一共花了12ms时间 迭代 arraylist: 使用Iterator迭代一共花了16ms时间 使用loop迭代一共花了10ms时间 迭代 linkedlist: 使用Iterator迭代一共花了21ms时间 使用loop迭代一共花了334ms时间
参考文章: http://blog.csdn.net/u013916933/article/details/51590332 http://blog.csdn.net/keda8997110/article/details/8635005
相关文章推荐
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java 基础数据类型 和 深度克隆对象的2种方法(实现Cloneable接口或者实现对象序列化)
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- java基础入门补充(002) Cloneable接口 深复制与浅复制
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java中 Cloneable 、Serializable 接口详解
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
- java对象 深度克隆(不实现Cloneable接口)和浅度克隆
- JAVA基础学习(二十二)--IO流四-对象序列化、管道流、RandomAccessFile、DataStream、ByteArrayStream、转换流的字符编码
- java基础-RandomAccessFil(随机访问)
- 13.Java基础:常见流----->随机读写流:RandomAccessFile
- Java深拷贝除了通过实现Cloneable接口,另外还可以通过序列化实现对象的拷贝。
- java基础问题---什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。
- Java基础-打印流和合并流、分割与管道流、RandomAccessFile及其它流