您的位置:首页 > 编程语言 > Java开发

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐