Java数据流、对象流

文章目录

  • 数据流与对象流说明
  • 对象流API
  • 认识对象序列化机制
  • 如何实现序列化机制
  • 反序列化失败问题

数据流与对象流说明

如果需要将内存中定义的变量(包括基本数据类型或引用数据类型)保存在文件中,那怎么办呢?

int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;String name = "巫师";
Student stu = new Student("张三",23,89);

Java提供了数据流和对象流来处理这些类型的数据:

  • 数据流:DataOutputStream、DataInputStream
    • DataOutputStream:允许应用程序将基本数据类型、String类型的变量写入输出流中

    • DataInputStream:允许应用程序以与机器无关的方式从底层输入流中读取基本数据类型、String类型的变量。

  • 对象流DataInputStream中的方法:
 byte readByte()                short readShort()int readInt()                  long readLong()float readFloat()              double readDouble()char readChar()				 boolean readBoolean()					String readUTF()               void readFully(byte[] b)
  • 对象流DataOutputStream中的方法:将上述的方法的read改为相应的write即可。

  • 数据流的弊端:只支持Java基本数据类型和字符串的读写,而不支持其它Java对象的类型。而ObjectOutputStream和ObjectInputStream既支持Java基本数据类型的数据读写,又支持Java对象的读写,所以重点介绍对象流ObjectOutputStream和ObjectInputStream。

  • 对象流:ObjectOutputStream、ObjectInputStream

    • ObjectOutputStream:将 Java 基本数据类型和对象写入字节输出流中。通过在流中使用文件可以实现Java各种基本数据类型的数据以及对象的持久存储。
    • ObjectInputStream:ObjectInputStream 对以前使用 ObjectOutputStream 写出的基本数据类型的数据和对象进行读入操作,保存在内存中。

说明:对象流的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

对象流API

ObjectOutputStream中的构造器:

public ObjectOutputStream(OutputStream out) : 创建一个指定的ObjectOutputStream。

FileOutputStream fos = new FileOutputStream("game.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);

ObjectOutputStream中的方法:

- public void writeBoolean(boolean val):写出一个 boolean 值。
- public void writeByte(int val):写出一个8位字节
- public void writeShort(int val):写出一个16位的 short- public void writeChar(int val):写出一个16位的 char- public void writeInt(int val):写出一个32位的 int- public void writeLong(long val):写出一个64位的 long- public void writeFloat(float val):写出一个32位的 float 值。
- public void writeDouble(double val):写出一个64位的 double- public void writeUTF(String str):将表示长度信息的两个字节写入输出流,后跟字符串 s 中每个字符的 UTF-8 修改版表示形式。根据字符的值,将字符串 s 中每个字符转换成一个字节、两个字节或三个字节的字节组。注意,将 String 作为基本数据写入流中与将它作为 Object 写入流中明显不同。 如果 s 为 null,则抛出 NullPointerException- `public void writeObject(Object obj)`:写出一个obj对象
- public void close() :关闭此输出流并释放与此流相关联的任何系统资源

ObjectInputStream中的构造器:
public ObjectInputStream(InputStream in) : 创建一个指定的ObjectInputStream。

FileInputStream fis = new FileInputStream("game.dat");
ObjectInputStream ois = new ObjectInputStream(fis);

ObjectInputStream中的方法:

- public boolean readBoolean():读取一个 boolean- public byte readByte():读取一个 8 位的字节
- public short readShort():读取一个 16 位的 short- public char readChar():读取一个 16 位的 char- public int readInt():读取一个 32 位的 int- public long readLong():读取一个 64 位的 long- public float readFloat():读取一个 32 位的 float- public double readDouble():读取一个 64 位的 double- public String readUTF():读取 UTF-8 修改版格式的 String
- `public void readObject(Object obj)`:读入一个obj对象
- public void close() :关闭此输入流并释放与此流相关联的任何系统资源

认识对象序列化机制

1、何为对象序列化机制?

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

  • 序列化过程:用一个字节序列可以表示一个对象,该字节序列包含该对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

  • 反序列化过程:该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。
    在这里插入图片描述
    2、序列化机制的重要性
    序列化是 RMI(Remote Method Invoke、远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。

序列化的好处,在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
3、实现原理

  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制。方法为:

    • public final void writeObject (Object obj) : 将指定的对象写出。
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制。方法为:

    • public final Object readObject () : 读取一个对象。
      在这里插入图片描述

如何实现序列化机制

如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现java.io.Serializable 接口。Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException

  • 如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现Serializable 接口
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
  • 静态(static)变量的值不会序列化。因为静态变量的值不属于某个对象。

举例1:

import org.junit.Test;import java.io.*;public class ReadWriteDataOfAnyType {@Testpublic void save() throws IOException {String name = "巫师";int age = 300;char gender = '男';int energy = 5000;double price = 75.5;boolean relive = true;ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("game.dat"));oos.writeUTF(name);oos.writeInt(age);oos.writeChar(gender);oos.writeInt(energy);oos.writeDouble(price);oos.writeBoolean(relive);oos.close();}@Testpublic void reload()throws IOException{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("game.dat"));String name = ois.readUTF();int age = ois.readInt();char gender = ois.readChar();int energy = ois.readInt();double price = ois.readDouble();boolean relive = ois.readBoolean();System.out.println(name+"," + age + "," + gender + "," + energy + "," + price + "," + relive);ois.close();}
}

举例2:


import java.io.Serializable;public class Employee implements Serializable {//static final long serialVersionUID = 23234234234L;public static String company; //static修饰的类变量,不会被序列化public String name;public String address;public transient int age; // transient瞬态修饰成员,不会被序列化public Employee(String name, String address, int age) {this.name = name;this.address = address;this.age = age;}public static String getCompany() {return company;}public static void setCompany(String company) {Employee.company = company;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", address='" + address + '\'' +", age=" + age +", company=" + company +'}';}
}

import org.junit.Test;
import java.io.*;public class ReadWriteObject {@Testpublic void save() throws IOException {Employee.setCompany("小和尚");Employee e = new Employee("小姐姐", "美女", 23);// 创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.dat"));// 写出对象oos.writeObject(e);// 释放资源oos.close();System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。}@Testpublic void reload() throws IOException, ClassNotFoundException {// 创建反序列化流FileInputStream fis = new FileInputStream("employee.dat");ObjectInputStream ois = new ObjectInputStream(fis);// 读取一个对象Employee e = (Employee) ois.readObject();// 释放资源ois.close();fis.close();System.out.println(e);}
}

举例3:如果有多个对象需要序列化,则可以将对象放到集合中,再序列化集合对象即可。

import org.junit.Test;import java.io.*;
import java.util.ArrayList;public class ReadWriteCollection {@Testpublic void save() throws IOException {ArrayList<Employee> list = new ArrayList<>();list.add(new Employee("张三", "宏福苑", 23));list.add(new Employee("李四", "白庙", 24));list.add(new Employee("王五", "平西府", 25));// 创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employees.dat"));// 写出对象oos.writeObject(list);// 释放资源oos.close();}@Testpublic void reload() throws IOException, ClassNotFoundException {// 创建反序列化流FileInputStream fis = new FileInputStream("employees.dat");ObjectInputStream ois = new ObjectInputStream(fis);// 读取一个对象ArrayList<Employee> list = (ArrayList<Employee>) ois.readObject();// 释放资源ois.close();fis.close();System.out.println(list);}
}

反序列化失败问题

问题1:

对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。

问题2:

当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
  • 该类包含未知数据类型

解决办法:

Serializable 接口给需要序列化的类,提供了一个序列版本号:serialVersionUID 。凡是实现 Serializable接口的类都应该有一个表示序列化版本标识符的静态变量:

static final long serialVersionUID = 234242343243L; //它的值由程序员随意指定即可。
  • serialVersionUID用来表明类的不同版本间的兼容性。简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
  • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。因此,建议显式声明。
  • 如果声明了serialVersionUID,即使在序列化完成之后修改了类导致类重新编译,则原来的数据也能正常反序列化,只是新增的字段值是默认值而已。
import java.io.Serializable;public class Employee implements Serializable {private static final long serialVersionUID = 1324234L; //增加serialVersionUID//其它结构:略
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/56462.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…

新手注意事项-visual studio 来实现别踩白块儿

自己之前为了熟悉easyx练习过一个简单的项目&#xff0c;别踩白块儿&#xff0c;链接在这里&#xff0c;别踩白块儿&#xff0c;当时比较稚嫩&#xff0c;很多东西都不会&#xff0c;可以说是只知道最基本的语法&#xff0c;头文件都不知道&#xff0c;一个一个查资料弄懂的&am…

FreeRTOS(vTaskList与vTaskGetRunTimeStats)

目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskList&#xff…

c语言每日一练(2)

前言&#xff1a; 每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

VUE框架:vue2转vue3全面细节总结(3)路由组件传参

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人_python人工智能视觉&#xff08;opencv&#xff09;从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了&#xff1a; https://blog.csdn.net/lbcy…

嵌入式开发学习(STC51-18-LCD液晶显示)

内容 在LCD1602液晶上显示字符信息&#xff1b; LCD1602介绍 简介 1602液晶也叫1602字符型液晶&#xff0c;它能显示2行字符信息&#xff0c;每行又能显示16个字符&#xff1b; 它是一种专门用来显示字母、数字、符号的点阵型液晶模块&#xff1b; 它是由若干个5x7或者5x…

防火墙第二次作业

一、什么是防火墙&#xff1f; 百度给出个一个定义&#xff1a;防火墙技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资料与信息安全性的一种技术。 通俗的来讲&#…

C高级 作业 day4 8/7

1.整理思维导图 2.写一个函数&#xff0c;获取用户的uid和gid并使用变量接收 3.整理冒泡排序、选择排序和快速排序的代码 冒泡排序 //冒泡排序 void bubble_sort(int a[],int len) {int count0;for(int i1;i<len;i) //控制轮数{for(int j0;j<len-i;j) //控制每一轮交换…

深度学习和OpenCV的对象检测(MobileNet SSD视频流实时识别)

上期文章,我们分享了如何使用opencv 与MobileNet SSD模型来检测给定的图片,有网友反馈能否提供一下视频流的实时检测代码,其实我们在分享人脸识别的时候,分享了如何使用cv2.videoCpature 类来从视频中实时提取视频中的图片,进行人脸的识别,视频流的对象检测跟opencv的人脸…

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报2

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…

【分布式应用】ELK企业级日志分析系统

目录 一、ELK 简介 1.1 ELK各组件介绍 ElasticSearch&#xff1a; Kiabana&#xff1a; Logstash&#xff1a; 1.2 可以添加的其它组件&#xff1a; Filebeat&#xff1a; 缓存/消息队列&#xff08;redis、kafka、RabbitMQ等&#xff09;&#xff1a; Fluentd&#xf…

【脚踢数据结构】链表(1)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言,Linux基础,ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的一句鸡汤&#x1f914;&…