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)