栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

Java对象流

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java对象流

使用 DataInputStream 或者 DataOutputStream 可以读写对象数据,但是操作比较繁琐
//从文件中按照id值查找对应的对象
int id=dis.readInt(); //用户id--用户标识
int len=dis.readInt(); //用户名称的字符数
StringBuilder username=new StringBuilder(); //用户名称
for(int i=0;i 
 

SUN 提供了 ObjectInputStream/ObjectOutputStream 可以直接将 Object 写入或读出 这里实际上还有针对 8 种简单类型及其包装类的操作方法,以及针对 String 类型的操作方法
  • readObject():Object
  • writeObject(Object):void
//简单写法,应该使用try/finally结构或者使用try/resource的写法
Date now=new Date();
ObjectOutputStream oos=new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream("data3.txt")));
oos.writeObject(now);
oos.close();
ObjectInputStream ois=new ObjectInputStream(
new BufferedInputStream(new FileInputStream("data3.txt")));
Object obj=ois.readObject();
if(obj!=null && obj instanceof Date) {
Date dd=(Date)obj;
System.out.println(dd);
}
ois.close();
读写一个对象的前提是这个类型的对象是可以被序列化的;
  • NotSerializableException
对象序列化【简单来说就是将对象可以直接转换为二进制数据流】 / 对象的反序列化【可以将二进制数据流转换为对象】,这一般依靠JVM 实现,编程中只做声明对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将这种二进制流恢复成原来的 Java 对象 。 1 、如何声明对象所属于的类可以进行序列化和反序列化 Serializable/Externalizable 接口 其中的接口没有任何定义,仅仅只起到了说明的作用,这种接口叫做标志接口或者旗标接口 2 、可以通过 ObjectInputStream 【 readObject():Object 】和 ObjectOutputStream【 writeObject(Object):void 】提供的方法直接操作对象 3 、输出对象
User user = new User();
user.setId(100L);
user.setUsername("zhangsan");
user.setPassword("123456");
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("users.data"));
oos.writeObject(user);
oos.close();

4 、读取对象
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("users.data"));
Object temp = ois.readObject();
if (temp != null && temp instanceof User) {
User user = (User) temp;
System.out.println(user);
}
ois.close();

编码细节 1 、需要通过对象流读写的对象必须实现了序列化接口,否则 java.io.NotSerializableException
1 class User implements Serializable

2 、 Serializable 接口是标志接口,没有需要实现的方法,所有的序列化和反序列化操作由 VM 负责实现。 Externalizable 接口定义为 public interface Externalizable extends java.io.Serializable ,这个接口中包含两个方法需要实现writeExternal 自定义实现对象的序列化, readExternal 自定义实现对象的反序列 化。除非特殊需求一般不使用Externalizable 接口,因为没有必要自定义
class User implements Externalizable {
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// 写出对象的操作
System.out.println("现在需要写出对象:" + this);
out.writeLong(this.id);
out.writeUTF(this.username);
out.writeUTF(this.password);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
// 读取对象的操作
this.id = in.readLong();
this.username = in.readUTF();
this.password = in.readUTF();
}
public User(){}

3 、类型转换问题:
Object temp = ois.readObject();
if (temp != null && temp instanceof User) {
User user = (User) temp;
System.out.println(user);
}

4 、 private static final long serialVersionUID = 6889840055394511246L 如果不添加序列号,则会有警告信息,但是不是错误信息 一般选择 Add generated serial version ID 会生成一个在项目中永不重复的的序列版本编号 序列版本号可以不用添加 , 这个序列版本号是一种序列化和反序列化中快速识别类型的简单方法,比不加 序列号的识别效率高。引入的功能是如果版本号不对应,不会进行类型识别,而是直接报异常InvalidClassException 5 、一般针对敏感数据不应该进行序列化操作,针对不需要进行序列操作的属性可以添加一个关键字transient,表示该属性不参与序列化和反序列化操作
class User implements Serializable {
private transient String password; //transient用于声明该属性不支持序列化操作
class User implements Serializable {
private String username;
private transient String password;
private Role role;//因为Role没有实现序列化接口,所以写出user对象时会有报错
NotSerializableException。处理报错的方法有:1、可以给Role类定义添加序列接口。2、在role
属性上添加transient表示这个属性不序列化处理

6 、读文件的判断:读取文件时可以通过 EOFException 异常来判断文件读取结束
try (
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("users.data"));
) {
while (true) {
try {
Object temp = ois.readObject();
if (temp != null && temp instanceof User) {
User user = (User) temp;
System.out.println(user);
}
} catch (EOFException ex) { // 这个异常是用于判断文件结尾,所以不需要进行处
理
break;
}
}
}

已经向文件中写入数据后,继续追加存储,则读取数据会出现 StreamCorruptedException
  • ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("users.data",true)));
// 向文件中追加新数据 true
// 首先读取数据,然后再统一写入
Object[] arr = new Object[100];
int counter = 0;
File ff = new File("user.data");
if (ff.exists()) {
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("user.data"));
while (true) {
try {
Object temp = ois.readObject();
arr[counter++] = temp;
} catch (EOFException e) {
break;
}
}
ois.close();
}
// 追加数据
User user = new User();
user.setId(299L); user.setUsername("name299");
user.setPassword("pwd299");
arr[counter++] = user;
// 然后统一写出到文件中
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("user.data"));
for (int i = 0; i < arr.length; i++) {
Object temp = arr[i];
if (temp != null)
oos.writeObject(temp);
}
oos.close();
问题:如果某个类的属性类型不是基本类型或者 String 类型,且没有实现可序列化接口,则该类型属性 类是不可序列化 针对于对象中的 InputStream/OutputStream 之类的资源类型的属性,不仅不能进行序列化操作,而且 在序列化之前应该释放资源,在反序列化后应该重新创建资源链接。Externalizable

class User implements Externalizable {
private Long id; // 要求自增长
private String username;
private String password;
private InputStream is; //输入流不能被序列
@Override
public void writeExternal(ObjectOutput out) throws IOException {
//writeObject方法时调用
is.close(); //释放资源
out.writeLong(this.id);
out.writeUTF(this.username);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException { //readObject方法时执行
this.id=in.readLong();
this.username=in.readUTF();
is=new FileInputStream("ddd.txt");//重新获取资源
}
}

序列化总结 Java 序列化就是将一个对象转化为一个二进制表示的字节数组,通过保存或则转移这些二进制数组达到 持久化的目的。要实现序列化,需要实现java.io.Serializable 接口。反序列化是和序列化相反的过程,就 是把二进制数组转化为对象的过程。在反序列化的时候,必须有原始类的模板才能将对象还原。
  • 当父类实现了Serializable接口的时候,所有的子类都能序列化
  • 子类实现了Serializable接口,父类没有,父类中的属性不能被序列化(不报错,但是数据会丢失)
  • 如果序列化的属性是对象,对象必须也能序列化,否则会报错
  • 反序列化的时候,如果对象的属性有修改或则删减,修改的部分属性会丢失,但是不会报错
  • 在反序列化的时候serialVersionUID被修改的话,会反序列化失败
  • 在存Java环境下使用Java的序列化机制会支持的很好,但是在多语言环境下需要考虑别的序列化机制,比如xml、json或protobuf等

serialVersionUID 值是用于确保类序列化与反序列化的兼容性问题的,如果序列化和反序列化过程中这 两个值不一样,那么将导致序列化失败 可以看到编译器推荐两种方式,一种是生成默认的 versionID ,这个值为 1L ,还有一种方式是根据类名、接口名、成员方法及属性等来生成一个 64 位的哈希字段,只要类名、方法名、变量有修改或者有空格、注释、换行等操作,计算出来的哈希字段都会不同,当然这里需要注意,每次有以上的操作的时候 尽量都要重新生成一次serialVerionUID ,编译器并不会自动修改
  • Java 序列化只是针对对象的属性的传递,至于方法和序列化过程无关
  • 当一个父类实现了序列化,那么子类会自动实现序列化,不需要显示实现序列化接口,反过来,子类实现序列化,而父类没有实现序列化则序列化会失败---即序列化具有传递性
  • 当一个对象的实例变量引用了其他对象,序列化这个对象的时候会自动把引用的对象也进行序列化 (实现深度克隆)
  • 当某个字段被申明为 transient 后,默认的序列化机制会忽略这个字段
  • 被申明为 transient 的字段,如果需要序列化,可以添加两个私有方法writeObject和readObject或者实现Externalizable接口

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1036664.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号