java IO流之对象流ObjectInputStream ObjectInputStream Serializable
对象流:用于存储和读取基本数据类型数据或对象的处理流。
它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化和反序列化:
ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。序列化
ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。反序列化
ObjectOutputStream
- ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
- 只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
- writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。 还可以使用 DataOutput 中的适当方法将基本数据类型写入流中。还可以使用 writeUTF 方法写入字符串。
- 对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形恢复为最初写入它们时的形状。
例如,要写入可通过 ObjectInputStream 中的示例读取的对象,请执行以下操作:
FileOutputStream fos = new FileOutputStream("t.tmp");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeInt(12345);
oos.writeObject("Today");
oos.writeObject(new Date());
oos.close();
构造方法摘要
- protected ObjectOutputStream()
- 为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
- ObjectOutputStream(OutputStream out)
- 创建写入指定 OutputStream 的 ObjectOutputStream。
方法
Modifier and Type | Method | Description |
---|---|---|
protected void | annotateClass(Class<?> cl) | 子类可以实现此方法,从而允许在流中存储类数据。 |
protected void | annotateProxyClass(Class<?> cl) | 子类可以实现此方法,从而在流中存储定制数据和动态代理类的描述符。 |
void | close() | 关闭流。 |
void | defaultWriteObject() | 将当前类的非静态和非瞬态字段写入此流。 |
protected void | drain() | 排空 ObjectOutputStream 中的所有已缓冲数据。 |
void | flush() | 刷新该流的缓冲。 |
protected Object | replaceObject(Object obj) | 在序列化期间,此方法允许 ObjectOutputStream 的受信任子类使用一个对象替代另一个对象。 |
void | reset() | 重置将丢弃已写入流中的所有对象的状态。 |
void | useProtocolVersion(int version) | 指定要在写入流时使用的流协议版本。 |
void | write(byte[] buf) | 写入一个 byte 数组。 |
void | write(byte[] buf, int off, int len) | 写入字节的子数组。 |
void | write(int val) | 写入一个字节。 |
void | writeBoolean(boolean val) | 写入一个 boolean 值。 |
void | writeByte(int val) | 写入一个 8 位字节。 |
void | writeBytes(String str) | 以字节序列形式写入一个 String。 |
void | writeChar(int val) | 写入一个 16 位的 char 值。 |
void | writeChars(String str) | 以 char 序列形式写入一个 String。 |
void | writeDouble(double val) | 写入一个 64 位的 double 值。 |
void | writeFields() | 将已缓冲的字段写入流中。 |
void | writeFloat(float val) | 写入一个 32 位的 float 值。 |
void | writeInt(int val) | 写入一个 32 位的 int 值。 |
void | writeLong(long val) | 写入一个 64 位的 long 值。 |
void | writeObject(Object obj) | 将指定的对象写入 ObjectOutputStream。 |
protected void | writeObjectOverride(Object obj) | 子类用于重写默认 writeObject 方法的方法。 |
void | writeShort(int val) | 写入一个 16 位的 short 值。 |
protected void | writeStreamHeader() | 提供 writeStreamHeader 方法,这样子类可以将其自身的头部添加或预加到流中。 |
void | writeUnshared(Object obj) | 将“未共享”对象写入 ObjectOutputStream。 |
void | writeUTF(String str) | 以 UTF-8 修改版格式写入此 String 的基本数据。 |
protected boolean enableReplaceObject(boolean enable) 允许流对流中的对象进行替换。
protected void writeClassDescriptor (ObjectStreamClass desc) 将指定的类描述符写入 ObjectOutputStream。
ObjectOutputStream.PutField putFields() 获取用于缓冲写入流中的持久存储字段的对象。
- 从类 java.lang.Object 继承的方法
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
ObjectInputStream
-
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
-
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
-
ObjectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中显示的类相匹配。使用标准机制按需加载类。 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
-
readObject
方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。 -
可以使用 DataInput 上的适当方法从流读取基本数据类型。
-
默认情况下,对象的反序列化机制会将每个字段的内容恢复为写入时它所具有的值和类型。反序列化进程将忽略声明为瞬态或静态的字段。对其他对象的引用使得根据需要从流中读取这些对象。使用引用共享机制能够正确地恢复对象的图形。反序列化时始终分配新对象,这样可以避免现有对象被重写。
-
读取对象类似于运行新对象的构造方法。为对象分配内存并将其初始化为零 (NULL)。为不可序列化类调用无参数构造方法,然后从以最接近 java.lang.object 的可序列化类开始和以对象的最特定类结束的流恢复可序列化类的字段。
例如,要从由 ObjectOutputStream 中的示例写入的流读取:
FileInputStream fis = new FileInputStream("t.tmp");
ObjectInputStream ois = new ObjectInputStream(fis);
int i = ois.readInt();
String today = (String) ois.readObject();
Date date = (Date) ois.readObject();
ois.close();
构造方法摘要
- protected ObjectInputStream()
- 为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
- ObjectInputStream(InputStream in)
- 创建从指定 InputStream 读取的 ObjectInputStream。
方法
Modifier and Type | Method | Description |
---|---|---|
int | available() | 返回可以不受阻塞地读取的字节数。 |
void | close() | 关闭输入流。 |
void | defaultReadObject() | 从此流读取当前类的非静态和非瞬态字段。 |
int | read() | 读取数据字节。 |
int | read(byte[] b) | 从输入流中读取一定数量的字节存储在缓冲区数组 b中。 |
int | read(byte[] buf, int off, int len) | 将输入流中最多 len 个数据字节读入 byte 数组。 |
boolean | readBoolean() | 读取一个 boolean 值。 |
byte | readByte() | 读取一个 8 位的字节。 |
char | readChar() | 读取一个 16 位的 char 值。 |
protected Object | resolveObject(Object obj) | 在反序列化期间,此方法允许 ObjectInputStream 的受信任子类使用一个对象替代另一个。 |
double | readDouble() | 读取一个 64 位的 double 值。 |
protected ObjectStreamClass | readClassDescriptor() | 从序列化流读取类描述符。 |
float | readFloat() | 读取一个 32 位的 float 值。 |
void | readFully(byte[] buf) | 读取字节,同时阻塞直至读取所有字节。 |
void | readFully(byte[] buf, int off, int len) | 读取字节,同时阻塞直至读取所有字节。 |
int | readInt() | 读取一个 32 位的 int 值。 |
String | readLine() | 已过时。 此方法不能正确地将字节转换为字符。请参见 DataInputStream 以获取详细信息和替代方法。 |
long | readLong() | 读取一个 64 位的 long 值。 |
Object | readObject() | 从 ObjectInputStream 读取对象。 |
protected Object | readObjectOverride() | 此方法由 ObjectOutputStream 的受信任子类调用,这些子类使用受保护的无参数构造方法构造 ObjectOutputStream。 |
short | readShort() | 读取一个 16 位的 short 值。 |
protected void | readStreamHeader() | 提供的 readStreamHeader 方法允许子类读取并验证它们自己的流头部。 |
Object | readUnshared() | 从 ObjectInputStream 读取“非共享”对象。 |
int | readUnsignedByte() | 读取一个无符号的 8 位字节。 |
int | readUnsignedShort() | 读取一个无符号的 16 位 short 值。 |
String | readUTF() | 读取 UTF-8 修改版格式的 String。 |
int | skipBytes(int len) | 跳过字节。 |
boolean | markSupported() | 测试此输入流是否支持 mark 和 reset 方法。 |
void | mark(int readlimit) | 在此输入流中标记当前的位置,readlimit - 在标记位置失效前可以读取字节的最大限制。 |
void | reset() | 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置,以便后续读取重新读取相同的字节。 |
long | skip(long n) | 跳过和丢弃此输入流中数据的 n 个字节。 |
- ObjectInputStream.GetField readFields() 按名称从流中读取持久字段并使其可用。
- protected Class<?> resolveClass(ObjectStreamClass desc) 加载指定流类描述的本地等价类。
- protected boolean enableResolveObject(boolean enable) 使流允许从该流读取的对象被替代。
- void registerValidation(ObjectInputValidation obj, int prio) |在返回图形前注册要验证的对象。
- protected Class<?> resolveProxyClass(String[] interfaces) 返回一个代理类,该类实现在代理类描述符中命名的接口;子类可以实现此方法,以便从流及动态代理类的描述符中读取自定义数据,允许它们使用接口和代理类的替换加载机制。
Serializable
你想要序列化的那个对象对应的类,就必须要实现这一个接口
-
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。static,transient修饰的属性不可以被序列化。
-
要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和恢复超类型的公用 (public)、受保护的 (protected) 和(如果可访问)包 (package) 字段的状态。仅在子类型扩展的类有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此职责。如果不是这种情况,则声明一个类为可序列化类是错误的。该错误将在运行时检测到。
-
在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数构造方法。可序列化子类的字段将从该流中恢复。
serialVersionUID
serialVersionUID:凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。
如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
实例化
存在类实例化
序列化
public class InT {
public static void main(String[] args){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(new File("IOStream/src/testfile/test.txt")));//将内存中的字符串写出到文件中:
oos.writeInt(12345);
oos.writeObject("Today");
oos.writeObject(new Date());
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭流:
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
反序列化
public class OuT {
public static void main(String[] args){
try (
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("IOStream/src/testfile/test.txt")));//将文件中保存的内容读入到内存:
){
//读取:
int i = ois.readInt();
String today = (String) ois.readObject();
Date date = (Date) ois.readObject();
System.out.println(i+"\n"+today+"\n"+date);
}catch(Exception e){
e.printStackTrace();
}
}
}
自定义类实例化
自定义的User类
import java.io.Serializable;
public class User implements Serializable {
// private static final long serialVersionUID = 9050691344308365540L;
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
序列化
public class UInT {
public static void main(String[] args){
User user = new User("fyz");
try (
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("test.txt")));
){
//将自定义类对象user写出到文件中:
oos.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
}
反序列化
public class UOuT {
public static void main(String[] args){
//将文件中保存的字符串 读入到 内存:
try (
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("test.txt")));
){
//读取:
User user = (User)(ois.readObject());
System.out.println(user);
}catch(Exception e){
e.printStackTrace();
}
}
}