标签搜索

java中的数据流

ATAO
2022-02-05 / 0 评论 / 17 阅读 / 正在检测是否收录...

1.数据流的基本概念

流,原英文:stream。在java中,流用来做输入输出操作,就是IO操作。

2.流的三种分法

1.按照方向分:输入流 和 输出流

  • 输入流,是把程序外的数据,读入到程序的内存中。
  • 输出流,是把程序内存中的数据,写到程序之外。

2.按照功能分:节点流 和 过滤流

  • 如果一个流直接连接了起点和终点,那么该流就是”节点流“
  • 如果一个流没有直接连接起点和终点,那么该流就是一个”过滤流“(也叫做 包装流)

3.按照是否使用码表进行编码或解码分:字节流 和 字符流

  • 字节流:不使用码表
  • 字符流:使用码表

3.流的继承体系

Java中的流,是一个继承体系。在该继承体系中,所有的流,都是以下四个流其中一个子类:

Reader字符输入流
Writer字符输出流
InputStream字节输入流
OutputStream字节输出流

Java中的流,大概有60多个,可能现在还更多。这60多个流,我们不可能都学习,我们只学习最常用的,不常用的用到再查也不迟。这60多个流,就是上面4个流其中一个的子类。

所以,我们先学习以上四个抽象类,把这4个抽象类的方法学习了,就等价于把Java中所有流的共性一并学习了。

4.字符流

Writer in = new FileWriter("D:\\aa.txt");//创建文件,覆盖原文件
Writer in = new FileWriter("D:\\aa.txt",true);//在原文件后续写

Writer是Java中所有字符串输出流的父类。

从方向上分,FileWriter是输出流

从功能上分,FileWriter是一个节点流

从是否使用编码表分:FileWriter是一个字符流

write(int n),把一个数字,写入到流(的缓冲区)中。反正不是直接输出到目的地。缓冲区中的数据,会在缓冲区被放满或者强制刷新时,才送到目的地。

write(char[] c),把字符数组中的所有元素都写入流中。

write(String),直接把一个字符串写入流中。

write(String s, int offset, int len),把一个字符串,按照指定的偏移量,和指定的长度写入流中。

write(char[] c, int offset, int len),同上一个。

close()关闭流,关闭流之后,就无法再向流中写入数据了。close除了可以把流关闭,还能在关闭流之前把缓冲区中的数据,刷新到目的地。

flush(),强制刷新字符流中的缓冲区中的数据到目的地,不会关闭流。

skip(int n)跳过指定的字符数

Reader in = new FileReader("D:\\aa.txt");

Reader时Java中所有的字符输入流的父类

因为Reader是一个抽象类,不能实例化,所以我们使用它的一个子类来实例化

从方向上分:FileReader是一个输入流

从功能上分:FileReader是一个节点流

从是否使用码表分:FileReader是一个字符流

read()一次读取一个字符,如果读到字符了就返回这个字符,还会将文件指针向后移动一个字符。如果读不到,则返回-1。

read(char[] c),读取几个字符放到字符数组中,返回值为读取到的长度

read(char[] c, int offset, int len),先偏移指定长度,再从头读取指定长度

练习:复制一个文件——略

5.字符过滤流

BufferedReader

从方向上分:BufferedReader是一个输入流

从功能上分:BufferedReader是一个过滤流(包装流)

从是否使用码表上分:BufferedReader是一个字符流

BufferedReader是Reader的子类,也就是说,上面讲解的Reader的方法,BufferedReader都有。我们使用BufferedReader独有的一个方法:readLine(),顾名思义,readLine一次可以读取一行。

BufferedWriter

从方向上分:BufferedWriter是一个输出流

从功能上分:BufferedWriter是一个过滤流(包装流)

从是否使用码表上分:BufferedWriter是一个字符流

BufferedWriter独有的方法:newLine(),作用是向流中写入换行,windows操作系统和linux操作系统中的换行符是不一样的,但newLine()方法可以根据不同系统写入对应的换行符,实现跨平台运行。

6.编码和解码(重点)

编码就是根据一个字符,找出该字符所对应的数字。

String str = "abc";
byte[] bb = str.getBytes();
System.out.println(Arrays.toString(bb));

解码就是根据数字,找出所对应的字符。

byte[] bb = {97,98,99};
String s = new String(bb);
System.out.println(s);

如果没有指定码表,默认会使用GBK码表,还可以手动指定码表:

String str = "abc";
byte[] bb = str.getBytes("utf-8");
System.out.println(Arrays.toString(bb));

如果编码和解码使用的码表不一样就会造成乱码,但是如果是前128个字符的话,不会乱码,因为所有码表的前128位都一样。

String str = "测试代码";
byte[] bb = str.getBytes("utf-8");
System.out.println(Arrays.toString(bb));
String str2 = new String(bb, "gbk");//码表不一样,会乱码
System.out.println(str2);

其实,每个码表都有自己独有的特征,比如:

使用unicode码表进行编码,得出的编码总是以-2 -1开头。(unicode码表中所有字符都是2个字节)

GBK码表中所有数字和所有字母都是一个字节,最高位是0,汉字都是2个字节,且汉字的两个字节的最高位都是1。

utf-8码表中,有的字符占1个字节,有的字符占2个字节,有的字符占3个字节,有的字符占4个字节。其中汉字占3个字节。

utf-8码表最大的特点是,按如下方式存储数据:

存储的字节内容读几个字节
10xxxxxx读1个字节,然后进行解码
110xxxxx 10xxxxxx读2个字节,然后进行解码
1110xxxx 10xxxxxx 10xxxxxx读3个字节,然后进行解码
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx读4个字节,然后进行解码

思考:

在使用Reader读取的时候,程序是如何知道,我们这一次是应该读取1个字节还是读取2个字节呢?

Reader默认使用的是本地编码表,也就是GBK,那么当使用in.read()读取1个字符是,in.read()内部,会先判断当前读取的字节最高位是不是1,如果是,就连续读取2个字节,然后返回,如果最高位是0,则只读取1个字节,再返回.

7.字节流

我们已经知道,字符流的特点是在读写数据的时候,一定会使用码表进行编码或解码。而字节流无论是读还是写,绝对不会使用码表。

OutputStream是所有字节输出流的父类

InputStream是所有字节输入流的父类

available()方法:获取当前文件指针到文件末尾的距离。

skip()方法:字节流中,skip()方法可以向前跳也可以向后跳,在字符流中,只能向后跳

字节流输入输出不进行任何编码解码,所以可以复制任何文件,包括图片、视频。

8.字节过滤流

只要是一个流,没有直接连接起点和终点,那么该流就是一个过滤流,使用过滤流就是为了使用它独有的一些方法。

BufferedOutputStream 和 BufferedInputStream

  • 从方向上分:BufferedOutputStream是输出流,BufferedInputStream是输入流
  • 从功能上分:BufferedOutputStream和BufferedInputStream都是过滤流
  • 从是否使用码表分:BufferedOutputStream和BufferedInputStream都是字节流

我们已经知道,所有的字符流天生都有缓冲区,而字节流都没有缓冲区,为了给字节流添加缓冲区,就使用BufferedOutputStream 和 BufferedInputStream

DataOutputStream 和 DataInputStream

DataOutputStream 和 DataInputStream可以一次读或写多个字节,就是把多个字节合在一起,成为一个完整的数字来进行操作。使用这两个流的时候,务必保证如何写的,就要如何让读。

0

评论 (0)

取消