一、概述

标记接口是一些没有属性和方法的接口,也是一种设计思想。Java中的一个标记接口表示的的是一种类的特性,实现了该标记接口的类则具有该特性。如实现了Serializable接口的类,表示这个类的对象是可以进行序列化和反序列化的。Java中常见的标记接口还有Cloneable接口、RandomAccess接口和Remote接口。可以用 if(对象名 instanceof 标记接口名)检测一个类是否实现某个标记接口。

二、四个标记接口的浅析

1、java.io.Serializable标记接口

该接口用来标记类的对象是否能够可以进行序列化,或者说串行化。将对象序列化之后,可以进行持久化的储存以及在网络中进行传输。如把对象变成字节流写入到一个文件中,就是一个序列化的过程,实现了对象的持久化储存,然后你的程序可以从这个文件中读取序列化的对象并且把它还原成原来的对象,进行反序列化。如果进行序列化的类的对象没有实现Serializable接口,则会抛出NotSerializableException。

 1)对象序列化有哪些特点?在对象序列化时,该对象引用的实例变量也会被序列化,如果这个实例变量是一个对象,这个对象也会被序列化。被transient修饰的实例变量也不参与序列化。在对象反序列化时transient修饰的变量将重新初始化,对象初始为null,基本数据类型被初始化为0、false等值。而static修饰的静态变量也不参与序列化的过程,在反序列化时,它的值是运行时虚拟机中该变量的值,并不是对象序列化时"储存"的值。

    2)serialVersionUID是什么,有什么用?在对象序列化时,这个对象都会有一个的serialVersionUID,它是根据类的结构信息计算出来的。在反序列化时,如果对象有了不同的serialVersionUID,则虚拟机将会抛出异常,还原操作将会失败。如在ArrayList类的源码(JDK8)中就可以发现如下属性,所以JDK8中ArrayList类的对象都有一个一致的serialVersionUID值。

 private static final long serialVersionUID = 8683452581122892189L;

   

       3)代码测试

  • 先创建一个Baby类,实现Serializable接口
package mytest;

import java.io.Serializable;

public class Baby implements Serializable{

    private int age;
private String name; public Baby(int age,String name) {
this.age=age;
this.name=name;
} @Override
public String toString() {
return "Baby [age=" + age + ", name=" + name + "]";
} }
  • 再创建一个Mother类,实现了Serializable接口。包含有一个int类型实例变量age,Baby类型的实例变量mybaby,一个String类型的静态变量name,一个修饰符为transient的String实例变量,用于序列化测试
package mytest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class Mother implements Serializable { private int i;
private Baby mybaby;// 一个实例变量,也是一个实现了Serializable的类的对象
private static String name;
private transient String nickname; public Mother(int i, Baby mybaby, String nickname) {
this.i = i;
this.mybaby = mybaby;
this.nickname = nickname;
} public void setName(String name) {
this.name = name;
} public String toString() {
return "Mother [i=" + i + ", mybaby=" + mybaby +", name="+name+", nickname=" + nickname + "]";
} public static void main(String[] args) {
Baby mybaby = new Baby(2, "小官");
Mother mother = new Mother(25, mybaby, "官妈");
mother.setName("小红");//此时静态变量name引用的字符串为"小红"
ObjectOutputStream oos = null;
try {
FileOutputStream fos = new FileOutputStream("MyMom.ser");
//"MyMom.ser"如果不存在会自动创建
oos = new ObjectOutputStream(fos);
oos.writeObject(mother);//将变量引用的对象序列化并写入MyMom.ser这个文件
} catch (Exception e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStream ois = null;
Mother reMother=null;
mother.setName("不是小红");//静态变量name引用的字符串修改为"不是小红"
try {
FileInputStream fis = new FileInputStream("MyMom.ser");
ois = new ObjectInputStream(fis);
reMother = (Mother) ois.readObject();
//将写入MyMom.ser的对象反序列化,由于返回的是Object对象,所以需要进行强转
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(reMother);//将反序列化得到的对象reMother打印出来
}
}
  • 输出结果如下
Mother [i=25, mybaby=Baby [age=2, name=小官], name=不是小红, nickname=null]

可以发现mother对象中,Baby类型的实例变量mybaby所引用的对象也进行了序列化,String类型静态变量name未参与序列化,以及transient修饰的String实例变量nickname也被初始化为null。

2、java.lang.Cloneable标记接口

该接口标记一个类的对象是否有安全的clone方法。在java.lang.Object类中有一个clone()方法如下

 protected native Object clone() throws CloneNotSupportedException;//native修饰符表示方法的实现不在此,这个方法是异地用C/C++实现的

由于Object类又是所有类的父类,所以所有的类都有一个继承自Object的clone()方法。如果一个类的对象调用了从Object继承来的clone方法,但是没有implements  Cloneable接口,虚拟机将抛出CloneNotSupportedException。

 1)Object类clone()的修饰符为protected产生的影响。由于Object的clone()方法是protected的,所以一个类从Object继承的clone方法默认只有java.lang包可见,以及子类可以继承,但子类也只能调用受保护的clone()方法来克隆它自己的对象(注意调用super.clone()不会克隆父类的对象,还是克隆的这个类的对象)。一个类必须重新定义clone()为public方法才能允许"所有方法"调用这个类的clone()方法来克隆这个类的实例。

 2)克隆对象和原对象有什么联系?克隆出来的对象与原来的对象属性是相同的,但不是同一个对象,他们的内存地址是不一样的,所以改变克隆出来的对象的属性一般不会影响到原来的对象。但是从Object继承的clone方法是一个浅拷贝,如果原对象包含子对象的引用,拷贝对象和原对象会拥有对同一个子对象的引用,这个情况下克隆对象对这个子对象的更改会改变原对象中这个子对象的属性。所以在原对象引用的子对象不都是String这种不可变的对象的话,必须重新定义clone方法来建立一个深拷贝。

  3)代码检验

  • 先创建一个People类,实现Cloneable接口,只有people_id和people_name属性
package mytest2;

public class People  implements Cloneable {
private int people_age;
private String people_name;
public People(int people_age, String people_name) {
super();
this.people_age = people_age;
this. people_name = people_name;
}
@Override
public String toString() {
return " [people_age=" +people_age + ", people_name=" + people_name + "]";
} }
  • 在创建一个继承自People的Adult类,实现了Cloneable接口,有adult_age和adult_name属性,和一个Date类型的testDay属性。在main方法中进行有关测试
package mytest2;

import java.util.Date;

public class Adult extends People implements Cloneable {
int adult_age;
String adult_name;
Date testDay; public Adult(int adult_age, String adult_name, int people_age, String people_name) {
super(people_age, people_name);
this.adult_age = adult_age;
this.adult_name = adult_name;
this.testDay= new Date(); } public People superClone() throws CloneNotSupportedException {
return (People) super.clone();// 这里克隆得到的依然会是一个Adult类的对象
} @Override
public String toString() {
return "[adult_age=" + adult_age + ", adult_name=" + adult_name + ", testDay=" + testDay + super.toString()+"]";
} public static void main(String[] args) {
People people = new People(26, "小官");
// people.clone();//父类的clone方法这里不可见,报错误
Adult adult = new Adult(18, "成年官",10,"小官");
Adult adultclone = null;
try {
adultclone = (Adult) adult.clone();// Object的clone方法返回值为Object对象,这里需要进行强转
people = (People) adult.superClone();// 这里得到的应该依然是一个Adult对象 } catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(adultclone);
System.out.println(people);
adultclone.testDay.setTime(System.currentTimeMillis()+1000000000);
/* System.currentTimeMillis()得到的是从1970年1月1日0时到当前时间的毫秒数
这里改变下testDay的值来测试原对象的testday属性会不会发生改变,以及发生了怎样的改变?*/
System.out.println("--------------------");
System.out.println(adultclone);
System.out.println(adult); } }
 
  • 输出结果为

[adult_age=18, adult_name=成年官, testDay=Wed Apr 17 13:05:17 CST 2019 [people_age=10, people_name=小官]]
     [adult_age=18, adult_name=成年官, testDay=Wed Apr 17 13:05:17 CST 2019 [people_age=10, people_name=小官]]
     --------------------
     [adult_age=18, adult_name=成年官, testDay=Mon Apr 29 02:51:57 CST 2019 [people_age=10, people_name=小官]]
     [adult_age=18, adult_name=成年官, testDay=Mon Apr 29 02:51:57 CST 2019 [people_age=10, people_name=小官]]

第一行的输出结果和第二行一致,可以看出super.clone()并不是克隆父类,克隆的Adult类的对象。第三行和第四行的输出与前两行的输出相比发生了改变,可以验证改变克隆对象引用的子对象,原对象引用的子对象也发生了同样的更改。

 4)建立深克隆

  • 由于Date类型已经将重新覆盖了clone方法,并将clone()的修饰符改为public,所以在外部可以用Date的实例直接调用这个clone方法来克隆Date的实例。Date相关源码(JDK8)如下:
   public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}
  • 在Adult类中覆盖clone方法,并实现深克隆,有关代码如下
    public Object clone() {
Adult adultClone = null;
try {
adultClone = (Adult) super.clone();
adultClone.testDay = (Date) this.testDay.clone();//将克隆对象的testDay域变为原对象test域的一个拷贝,实现深克隆 } catch (CloneNotSupportedException e) {}
return adultClone;
}
  • 覆盖clone()后,测试结果为
[adult_age=18, adult_name=成年官, testDay=Wed Apr 17 13:28:43 CST 2019 [people_age=10,  people_name=小官]]
[adult_age=18, adult_name=成年官, testDay=Wed Apr 17 13:28:43 CST 2019 [people_age=10, people_name=小官]]
--------------------
[adult_age=18, adult_name=成年官, testDay=Mon Apr 29 03:15:23 CST 2019 [people_age=10, people_name=小官]]
[adult_age=18, adult_name=成年官, testDay=Wed Apr 17 13:28:43 CST 2019 [people_age=10, people_name=小官]]//原对象引用的子对象testDay这次没有发生改变!

 3、java.util.RandomAccess标记接口

若一个容器类对象具有能够快速随机访问对象内元素的特性,就可以实现RandomAccess标记接口。比如ArrayList类就实现了这个接口,因为ArrayList是一个动态数组,底层是由数组实现的,所以ArrayList对象能够根据索引对对象内包含的元素进行快速随机的访问,其时间复杂度为O(1)。

4、java.rmi.Remote标记接口

这个标记接口用于远程方法调用,即RMI(Remote Method Invocation)技术。若一个类要实现远程方法调用,需要先实现这个接口。RMI技术也是EJB技术的原理。

(小官原创,若有谬误,望各位大佬批评指正)

https://www.cnblogs.com/jianguan/p/10719764.html

Java的四个标记接口:Serializable、Cloneable、RandomAccess和Remote接口的更多相关文章

  1. Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么 ...

  2. (转)Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    原文地址:http://blog.csdn.net/kenthong/article/details/5758884 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的 ...

  3. [Effective Java]第四章 类和接口

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  4. 什么是Java Marker Interface(标记接口)

    先看看什么是标记接口?标记接口有时也叫标签接口(Tag interface),即接口不包含任何方法.在Java里很容易找到标记接口的例子,比如JDK里的Serializable接口就是一个标记接口. ...

  5. Java基础知识强化104:Serializable接口 与 Parcelable接口

    1. 什么是 序列化 和 反序列化 ?     序列化 :序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间.序 ...

  6. Java I/O---序列化接口Serializable

    1.JDK API 中关于Serializable的描述 public interface Serializable 类通过实现 java.io.Serializable 接口以启用其序列化功能.未实 ...

  7. Java序列化接口Serializable接口的作用总结

    一.Java序列化接口Serializable的作用: 一个对象有对应的一些属性,把这个对象保存在硬盘上的过程叫做”持久化”. 对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字 ...

  8. Java实体对象为什么要实现Serializable接口?

    前言 Java实体对象为什么一定要实现Serializable接口呢?在学JavaSE的时候有些实体对象不实现Serializable不是也没什么影响吗? 最近在学习mybatis的时候发现,老师写的 ...

  9. java基础(四)-----抽象类与接口

    抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的面向对象的能力.他们两者之间对抽象概念的支持有很大的相似,甚至可以互换,但是也有区别. 一.抽象类 我们 ...

随机推荐

  1. qt手写输入法资料

    论文: https://max.book118.com/html/2015/1229/32204490.shtm 开源库: zinna Linux下使用的Tegaki就是使用的这个库 csdn博客资料 ...

  2. Exe资源文件

    在 Win NT/2000/XP/2003 下,有现成的API函数,这些函数在 Win 9x/ME 下不能使用. HANDLE BeginUpdateResource(LPCTSTR pFileNam ...

  3. MySQL复制表-CREATE SELECT

    假设存在以下Table: mysql> select * from staff; +----+----------+-------+ | id | name | slary | +----+-- ...

  4. java字符串对象存储机制

    String s1="abc";创建了几个String对象 ? String s2 = new String("abc");创建了几个String对象? s1= ...

  5. 18B20驱动小经验

    在写命令时P14拉高在最后 在读命令时P14在拉低后拉高

  6. Oracle中的位图索引和函数索引

    位图索引 同样的,先说是什么,再说为什么. 上篇我们说过BTREE索引是将数据表的索引列和行号排序后以树状形式存在磁盘中.那位图索引是什么样的呢? 现有如下日志表,有操作类型字段op_type,该字段 ...

  7. ERROR: Got error reading packet from server: A slave with the same server_uuid/server_id as this slave has connected to the master

    centos7.5 做binlog-server,拉取主库binlog报错 问题: [root@db03-53 binlog]# mysqlbinlog -R --host=10.0.0.55 --u ...

  8. JS(JavaScript)的进一步了解2(更新中···)

    js数据类型 基本数据类型:string   undefined   null  boolean  number 引用数据类型  Object  array  function 二者的区别 基本数据类 ...

  9. jQuery_$方法、属性、点击切换

    jQuery_$方法 1.$.each():遍历数组或对象中的数据 2.$.trim():去除字符串两边的空格 3.$.type(obj):得到数据的类型 4.$.isArray(obj):判断是否为 ...

  10. Python单元测试框架unittest

    学习接口自动化测试时接触了unittest单元测试框架,学习时参照了虫师编写的<selenium2自动化测试实战>,个人觉得里面讲的例子还比较容易理解的. 一.基础 1.main()和框架 ...