JavaIO流04

4.常用的类03

4.4节点流和处理流02

4.4.5对象处理流-ObjectInputStream和ObjectOutputStream

1.序列化和反序列化

例子1:

看一个需求

  1. int num= 100这个int 类型的数据保存到文件中,注意不是100 数字,而是int 100,并且能够从文件中直接恢复 int 100
  2. Dog dog = new Dog("小黄",3)这个Dog对象保存到文件中,并且能够从文件恢复。

上面的要求,就是能够将 基本数据类型 或者 对象 进行 序列化反序列化操作

  • 序列化和反序列化

    • 序列化就是在保存数据时,保存数据的值和数据类型
    • 反序列化就是在恢复数据时,恢复数据的值和数据类型
    • 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让这个类是可序列化,该类必须实现以下两个接口之一
      • Serializable //这是一个标记接口,没有方法(推荐)
      • Externalizable //该接口有方法需要实现,因此推荐实现上面的Serializable接口
2. 对象处理流基本介绍与应用
  1. 功能:提供了对基本类型或对象类型的序列化和反序列化的方法
  2. ObjectOutputStream提供序列化功能
  3. ObjectInputStream 提供反序列化功能


例子1:序列化应用

使用ObjectOutputStream序列化 基本数据类型 和一个 Dog对象(name,age),并保存到data.dat文件中。

package li.io.outputstream_;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; //演示ObjectOutputStream的使用,完成数据的序列化
public class ObjectOutputStream_ {
public static void main(String[] args) throws Exception {
//序列化后,保存的文件格式不是纯文本格式,而是按照它自己的格式来保存
String filePath = "d:\\data.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); //存储
//序列化数据到文件d:\data.dat
oos.writeInt(100);//100在底层实现了自动装箱,int ->Integer (Integer实现了Serializable接口)
oos.writeBoolean(true);//boolean ->Boolean (Boolean实现了Serializable接口)
oos.writeChar('a');// char ->Char (实现了Serializable接口)
oos.writeDouble(9.5);//double ->Double (实现了Serializable接口)
oos.writeUTF("你好火星");//String(实现了Serializable接口)
//保存一个Dog对象
oos.writeObject(new Dog("旺财", 10)); //关闭外层流
oos.close();
System.out.println("数据保存完毕(序列化形式)"); }
} //如果需要序列化某个对象,必须实现接口Serializable或者 Externalizable接口
class Dog implements Serializable {
private String name;
private int age; public Dog(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public String getName() {
return name;
}
}

例子2:反序列化应用

使用ObjectInputStream读取data.dat并且反序列化恢复数据

Dog:

package li.io.outputstream_;

import java.io.Serializable;

//如果需要序列化某个对象,必须实现接口Serializable或者 Externalizable接口
public class Dog implements Serializable {
private String name;
private int age; public Dog(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public String getName() {
return name;
}
}

ObjectInputStream_:

package li.io.inputstream_;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream; import li.io.outputstream_.Dog; public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException { //指定反序列化的文件
String filePath = "d:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); //读取
//1.读取(反序列化)的顺序要和你保存数据(序列化)的顺序一致,否则会出现异常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF()); //dog的编译类型是Object ,dou的运行类型是 Dog
Object dog = ois.readObject();
System.out.println("运行类型=" + dog.getClass());//Dog
System.out.println("dog信息=" + dog);//底层 Object->Dog //一些重要细节:
//1. 如果我们希望调用Dog的方法,需要向下转型
//2. 需要我们将Dog类的定义拷贝到可以引用的位置然后导入类(将Dog类创建为公共类,重新序列化,再反序列化)
Dog dog1 = (Dog) dog;
System.out.println(dog1.getName()); //关闭外层流
ois.close();
}
}

3.对象处理流使用细节
  1. 读写顺序要一致
  2. 要求实现序列化或者反序列化的对象的类,需要实现接口Serializable或者Externalizable
  3. 序列化的类中建议添加SerialVersionUID,是为了提高版本的兼容性

在完成序列化操作后,如果对序列化对象进行了修改,比如增加某个字段,那么我们再进行反序列化就会抛出InvalidClassException异常,这种情况叫不兼容问题。

解决的方法是:在对象中手动添加一个 serialVersionUID 字段,用来声明一个序列化版本号,之后再怎么添加属性也能进行反序列化,凡是实现Serializable接口的类都应该有一个表示序列化版本标识符的静态变量。

  1. 序列化对象时,默认将里面的所有属性都进行序列化,但除了statictransient修饰的成员

  2. 序列化对象时,要求里面属性的类型也需要实现序列化接口

例如:在(实现了Serializable接口的)Dog类中创建一个Master类型的属性master,而Master类没有实现Serializable接口,所以在序列化时,Dog类中的master属性无法序列化,也不能反序列化

  1. 序列化具有可继承性,也就是说如果某类已经实现序列化,则它的所有子类也已经默认实现了序列化

4.4.6标准输入输出流

不懂这些,你敢说自己知道Java标准输入输出流? - 简书 (jianshu.com)

介绍:

类型 默认设备
System.in 标准输入 InputStream 键盘
System.out标准输出 PrintStream 显示器

例子1:

package li.io.standard;

public class InputAndOutput {
public static void main(String[] args) {
// System 类 的 public final static InputStream in = null;
// System.in 编译类型 InputStream
// System.in 运行类型 BufferedInputStream
// 表示的是标准输入--用来读取键盘录入的数据 
System.out.println(System.in.getClass());//class java.io.BufferedInputStream-字节处理输入流 // 1. System.out public final static PrintStream out = null;
// 2.编译类型 PrintStream
// 3.运行类型 PrintStream
// 4.表示标准输出显示器--将数据输出到命令行
System.out.println(System.out.getClass());//class java.io.PrintStream - 字节输出流
}
}

应用案例1:

传统方法: System.out.println(" ");是使用out对象将数据输出到显示器

应用案例2:

传统方法,Scanner是从标准输入 键盘接收数据

        // 给Scanner扫描器传入的就是BufferedInputStream,
// 表示的是标准输入--用来读取键盘录入的数据
// 因此 scanner会到键盘去 获取输入的数据
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
//会去键盘去得到输入流
String next = scanner.next();
System.out.println(next);
scanner.close();

4.4.7转换流-InputStreamReader和OutputStreamWriter

1.文件乱码问题

先来看一个例子:

在d:\盘的根目录下有一个a.txt文件,内容如下:

现在想把该文件读取到程序中:

package li.io.transformation;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException; //一个中文乱码问题
public class CodeQuestion {
public static void main(String[] args) throws IOException {
//读取a.txt文件到程序中 //思路:
// 1.创建一个字符输入流 这里用 BufferedRead[处理流]
// 2.使用 BufferedRead 对象读取 a.txt
// 3.在默认情况下,读取文件是按照 UTF-8 编码
String filePath = "d:\\a.txt";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String s = br.readLine();
System.out.println("读取到的内容=" + s);
br.close();
}
}

在a.txt文件编码为uft-8的时候,输出:

在a.txt文件编码为ANSI的时候,输出:

出现乱码的根本问题是:没有指定文件的编码方式。

2.基本介绍与应用

转换流也是一种处理流,它提供了字节流和字符流之间的转换。

在Java IO流中提供了两个转换流:InputStreamReader 和 OutputStreamWriter,这两个类都属于字符流。


转换流的原理是:字符流 = 字节流 + 编码表

我们需要明确的是字符编码和字符集是两个不同层面的概念。

  • encoding是charset encoding的简写,即字符集编码,简称编码
  • charset是character set的简写,即字符集

编码是依赖于字符集的,一个字符集可以有多个编码实现,就像代码中的接口实现依赖于接口一样。

在转换流中选择正确的编码非常的重要,因为指定了编码,它所对应的字符集自然就指定了,否则很容易出现乱码,所以编码才是我们最终要关心的。

转换流的特点:其是字符流和字节流之间的桥梁。

​ 可对读取到的字节数据经过指定编码转换成字符

​ 可对读取到的字符数据经过指定编码转换成字节

那么何时使用转换流?

​ 当字节和字符之间有转换动作时

​ 流操作的数据需要编码或解码时

Java IO流详解(六)----转换流(字节流和字符流之间的转换) - 唐浩荣 - 博客园 (cnblogs.com)


  • InputStreamReader:

    如下图,InputStreamReader有一个重要的构造器InputStreamReader(InputStream, Charset):也就是说,我们可以通过这个方法,将传入的字节流转为一个字符流并指定处理的编码方式

  • OutputStreamWriter

    如下图,OutputStreamWriter也有一个重要的构造器:

    OutputStreamWriter(OutputStream, Charset):即我们也可以通过这个方法,将写出的字节流转为一个字符流并指定处理的编码方式

  • 当处理纯文本数据时,若使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
  • 可以在使用时指定编码格式,比如:uft-8,gbk,gb2312,ISO8859-1等

应用案例1:

编程将字节流FileInputStream转换成(包装成)字符流InputStreamReader,对文件进行读取(按照UTFF-8格式),进而再包装成 BufferedReader

package li.io.transformation;

import java.io.BufferedReader;
import java.io.FileInputStream; import java.io.InputStreamReader;
import java.io.IOException; //演示使用 InputStreamReader 转换流解决中文乱码问题
//将字节流转 FileInputStream 换成字符流 InputStreamReader,并指定编码 gbk/utf-8
public class InputStreamReader_ {
public static void main(String[] args) throws IOException { String filePath = "d:\\a.txt";
// 1.将 FileInputStream流(字节流),转成了 InputStreamReader流(字符流)
// 2.指定了gbk编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
// 3. 把 InputStreamReader 传入 BufferedReader
BufferedReader br = new BufferedReader(isr);
// 4. 读取
String s = br.readLine();
System.out.println("读取内容=" + s); // 5.关闭外层流
br.close(); }
}

应用案例2:

编程将字节流FileOutputStream 包装(转换)成字符流OutputStreamWriter,对文件进行写入(按照GBK格式,可以指定其他,比如UTF-8)

package li.io.transformation;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException; /**
* 演示使用 OutputStreamWriter
* 将字节流转 FileOutputStream 换成字符流 OutputStreamWriter
* 指定处理的编码 gbk/utf-8/utf8
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "d:\\a.txt";
String charSet = "GBK"; //创建对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet); //写入
osw.write("我只是一只小猫咪,我不懂你在说什么"); //关闭外层流
osw.close(); System.out.println("按照 " + charSet + " 保存文件成功~");
}
}

day37-IO流04的更多相关文章

  1. 04 IO流(二)——IO类的记忆方法、使用场景

    关于IO流以前写的PPT式笔记请跳转:https://blog.csdn.net/SCORPICAT/article/details/87975094#262___1451 IO流的主要结构 记忆方法 ...

  2. IO流01--毕向东JAVA基础教程视频学习笔记

    提要 01 IO流(BufferedWriter)02 IO流(BufferedReader)03 IO流(通过缓冲区复制文本文件)04 IO流(readLine的原理)05 IO流(MyBuffer ...

  3. JavaSE_ IO流 总目录(19~22)

    JavaSE学习总结第19天_IO流119.01 集合的特点和数据结构总结19.02 如何选择使用哪种集合19.03 集合常见功能和遍历方式总结19.04 异常的概述和分类19.05 JVM默认处理异 ...

  4. Java基础17:Java IO流总结

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  5. Java IO流学习

    Java IO流学习 Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是 ...

  6. C++ STL IO流 与 Unicode (UTF-16 UTF-8) 的协同工作

    09年研究技术的大神真的好多,本文测试有很多错误,更正后发布下(可能与编辑器相关). file.imbue(locale(file.getloc(), new codecvt_utf8<wcha ...

  7. File类的特点?如何创建File类对象?Java中如何操作文件内容,什么是Io流Io流如何读取和写入文件?字节缓冲流使用原则?

    重难点提示 学习目标 1.能够了解File类的特点(存在的意义,构造方法,常见方法) 2.能够了解什么是IO流以及分类(IO流的概述以及分类) 3.能够掌握字节输出流的使用(继承体系结构介绍以及常见的 ...

  8. Java基础之IO流

    很长时间都没有更新了,最近在补充JavaSE的一些细节部分 关于IO流的一些总结 首先要介绍的是File类,File类用于对文件和目录的一些操作 1.创建文件CreateNewFile() 2.对文件 ...

  9. Java中IO流,输入输出流概述与总结

    总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都是抽象类InputStream(字节输入 ...

随机推荐

  1. 学习笔记-JDBC连接数据库操作的步骤

    前言 这里我就以JDBC连接数据库操作查询的步骤作以演示,有不到之处敬请批评指正! 一.jdbc连接简要步骤 1.加载驱动器. 2.创建connection对象. 3.创建Statement对象. 4 ...

  2. Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制

    本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋 前文回顾 在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网 ...

  3. 写了个 Markdown 命令行小工具,希望能提高园友们发文的效率!

    写了个 Markdown 命令行小工具,希望能提高园友们发文的效率! 前言 笔者使用 Typora 来编写 Markdown 格式的博文,图片采用的是本地相对路径存储(太懒了不想折腾图床). 时间久了 ...

  4. P1494 小Z的袜子 莫队

    题干 就是将$add$和$del$函数里的$ans$变化变成组合数嘛, 先预处理出$x$只相同袜子一共有$f[x] = 1+2+...+$$(x-1)$种组合, 要注意,由于$f[x]$是一直加到$x ...

  5. 攻防世界MISC—进阶区32—37

    32.normal_png 得到一张png,扔进kali中binwalk 和 pngcheck一下,发现CRC报错 尝试修改图片高度,我是把height的2改为4,得到flag 33.很普通的数独 得 ...

  6. 适用于MES、WMS、ERP等管理系统的实体下拉框设计

    场景 该设计多适用于MES,ERP,WMS 等管理类型的项目. 在做管理类型项目的时候,前端经常会使用到下拉框,比如:设备,工厂等等.下拉组件中一般只需要他们的ID,Name属性,而这些数据都创建于其 ...

  7. Python中使用 for 循环来拿遍历 List 的值

    常规版本 简单的 for 循环遍历 x_n = ["x1","x2","x3"] for x in x_n: print(x) >&g ...

  8. 002 Redis使用及API

    Redis的使用及相关API 1.作用: 提高查询效率 一定程度上可以减轻数据库服务器的冲击压力,从而保护了数据库 //1.是否包含key redisTemplate.hasKey(key) //2. ...

  9. 我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景

    欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 写在前面..... 本文是笔者肉眼盯 Bug 系列的第三弹,前 ...

  10. 通过Nginx(OpenResty)修改UserAgent

    通过OpenResty修改UserAgent,非常简单,Demo里做了多次反向代理是为了日志输出显示效果.实际应用中不必这么麻烦. 浏览器访问如下地址即可 http://127.0.0.1:10090 ...