Java笔记——IO流分类

时间:2022-04-11 19:36:13

转自该博客

参考该博客

流是什么?
  流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以“流”的方式进行。设备可以是文件,网络,内存等。

Java笔记——IO流分类

  流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。

  可以将流想象成一个“水流管道”,水流就在这管道中形成了,自然就出现了方向的概念。

Java笔记——IO流分类

  当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件、内存或网络等等。相反地,需要写出数据到某个数据源目的地的时候,也会开启一个输出流,这个数据源目的地也可以是文件、内存或网络等等。

1.分类

1.1、按流向分类:

输入流: 程序可以从中读取数据的流。
输出流: 程序能向其中写入数据的流。

1.2、按数据传输单位分类:

字节流:以字节(8位二进制)为单位进行处理。主要用于读写诸如图像或声音的二进制数据。
字符流:以字符(16位二进制)为单位进行处理。
都是通过字节流的方式实现的。字符流是对字节流进行了封装,方便操作。在最底层,所有的输入输出都是字节形式的。

1.3、按功能分类:

节点流:从特定的地方读写的流类,如磁盘或者一块内存区域。
Java笔记——IO流分类
过滤流:使用节点流作为输入或输出。过滤流是使用一个已经存在的输入流或者输出流连接创建的。
Java笔记——IO流分类

2、字节流

2.1、输入流:

2.1.1、字节输入流抽象类及其关键的方法:

java.io
类 InputStream
java.lang.Object
java.io.InputStream
abstract int read()
从输入流中读取数据的下一个字节。
int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入 byte 数组。

2.1.2、输入流的操作:

打开一个输入流
循环读取
关闭输入流

这里使用InputStream的子类FileInputStream读入文件:

public static void main(String[] args) throws IOException {

//创建文件输入流
InputStream is = new FileInputStream("D:/itzhai/arthinking.txt");
//创建字节缓冲
byte[] buffer = new byte[100];
int length = 0;
//以字节形式循环读取文件
while((length = is.read(buffer, 0, buffer.length)) != -1){
//把字节转换成字符并输出
String str =new String(buffer, 0, length);
System.out.println(str);
}
}

2.1.3、抽象类InputStream的类层次:

Java笔记——IO流分类

2.1.4、字节数组输入流ByteArrayInputStream

把字节数组作为源的输入流。
相关例子:
字节数组输入流:
public static void main(String[] args) {

//创建读取数据源
String input = "arthinking";
//获取字节数组
byte[] b = input.getBytes();
//创建字节数组输出流
ByteArrayInputStream bis = new ByteArrayInputStream(b);
//循环逐个读取
for(int i = 0; i < input.length(); i++){
int c;
//读取下一个字节
while((c = bis.read()) != -1){
System.out.print((char)c);
}
}
//将缓冲区的位置重置为标记位置
bis.reset();
}

字节数组输出流:

public static void main(String[] args) throws IOException {

//创建字节输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String output = "arthinking";
//创建需要输出的字节数组
byte[] buffer = output.getBytes();
//把字节数组写到输出流
bos.write(buffer);
//创建文件输出流
OutputStream os = new FileOutputStream("D:/itzhai/arthinking.txt");
//把字节输出流写到文件输出流
bos.writeTo(os);

}

2.2、输出流:

2.2.1、字节输出流的抽象类及其最关键的方法:

java.io
类 OutputStream
java.lang.Object
java.io.OutputStream
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。

  由此可以看出,只有最后一个方法才是抽象的,原因是前面两个都调用了第三个抽象方法,这样继承这个抽象类的子类都必须提供抽象的write(int b)的实现,从而使得每个子类的实现都不一样。

2.2.2、输出流的操作:

打开输出流
循环写入
关闭输入流

这里使用了OutputStream的子类FileOutputStream输出到文件:

public static void main(String[] args) throws IOException {
//创建一个输出流
OutputStream os = new FileOutputStream("D:/itzhai/arthinking.txt", true);
String output = "http://www.itzhai.com";
//从字符串中获取字节数组
byte[] buffer = output.getBytes();
//写出到输出流
os.write(buffer);
//关闭输出流
os.close();
}

2.2.3、抽象类OutputStream的类层次结构:
Java笔记——IO流分类

3.字符流

  字节流不能操作Unicode字符,由于Java采用16位的Unicode字符,即一个字符占16位,所以要使用基于字符的输入输出操作。所以创造了字符流,以提供直接的字符输入输出的支持。

3.1 字符流的层次结构

3.1.1 输入流的类层次结构:

Java笔记——IO流分类

3.1.2 输出流的类层次结构:

Java笔记——IO流分类

3.2 字符流的顶层抽象类:

Reader和Writer。
public abstract class Reader
extends Object
implements Readable, Closeable

  用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable

  写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

3.3 java.io包中用于处理字符流的基本类InputStreamReader和OutputStreamWriter:

  InputStreamReader是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

  OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

3.3.1 InputStreamReader主要构造方法:

InputStreamReader(InputStream in)
创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
InputStreamReader(InputStream in, CharsetDecoder dec)
创建使用给定字符集解码器的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader。

3.3.2 OutputStreamWriter主要构造方法:

OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, Charset cs)
创建使用给定字符集的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, CharsetEncoder enc)
创建使用给定字符集编码器的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集的 OutputStreamWriter。

3.4 字符流的使用例子:

3.4.1 下面使用了一些常用的字符流对象实现文件的输出和输入:

public static void main(String[] args) throws IOException {

//创建文件字节输出流
FileOutputStream fos = new FileOutputStream("D:/itzhai/arthinking.txt");
//把字节输出流转换成字符输出流,并使用了BufferedWriter提供缓冲功能
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(fos));
bw.write("arthinking");
bw.close();

//创建文件字节输入流
FileInputStream fis = new FileInputStream("D:/itzhai/arthinking.txt");
//把字节输入流转换成字符输入流,并使用了BufferedReader提供缓冲功能
BufferedReader br = new BufferedReader(
new InputStreamReader(fis));
String str = br.readLine();
while(null != str){
System.out.println(str);
str = br.readLine();
}
br.close();
}

3.4.2 下面把标准的输入流封装成字符流并添加缓冲功能:

标准的输出流和标准的输入流是字节流:

static InputStream
in
“标准”输入流。
static PrintStream
out
“标准”输出流。
public static void main(String[] args) throws IOException {

BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String str;
while(null != (str = br.readLine())){
System.out.println(str);
}
}

3.4.3 FileReader和FileWriter的使用:

public class FileReader
extends InputStreamReader

  用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
  FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。

public class FileWriter
extends OutputStreamWriter

  用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
  文件是否可用或是否可以被创建取决于底层平台。特别是某些平台一次只允许一个 FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
  FileWriter 用于写入字符流。要写入原始字节流,请考虑使用 FileOutputStream。
使用FileReader和FileWriter读取文件和写出文件:

public static void main(String[] args) throws IOException {

String str = "hello world!!!";
//创建字符数组并初始化
char[] buffer = new char[str.length()];
str.getChars(0, str.length(), buffer, 0);
//创建FileWriter
FileWriter fw = new FileWriter("D:/itzhai/arthinking.txt");
//逐个字符的输出到文件
for(int i=0; i<buffer.length; i++){
fw.write(buffer[i]);
}
fw.close();

//创建FileReader
BufferedReader br = new BufferedReader(
new FileReader("D:/itzhai/arthinking.txt"));
//使用BufferedReader提供的逐行读取函数读取文件
while(null != (str = br.readLine())){
System.out.println(str);
}
br.close();
}

3.4.4 CharArrayReader和CharArrayWriter的使用

public class CharArrayReader
extends Reader

此类实现一个可用作字符输入流的字符缓冲区。

public class CharArrayWriter
extends Writer

  此类实现一个可用作 Writer 的字符缓冲区。缓冲区会随向流中写入数据而自动增长。可使用 toCharArray() 和 toString() 获取数据。
  注:在此类上调用 close() 无效,并且在关闭该流后可以调用此类中的各个方法,而不会产生任何 IOException。
下面使用CharArrayReader读取字符数组:

public static void main(String[] args) throws IOException {

String str = "arthinking";
//创建并初始化字符数组
char[] ch = new char[str.length()];
str.getChars(0, str.length(), ch, 0);

//通过字符数组初始化字符数组输入流
CharArrayReader cr = new CharArrayReader(ch);

int c;
while(-1 != (c = cr.read())){
System.out.print((char)c);
}
}

3.4.5 RandomAccessFile的使用:

public class RandomAccessFile
extends Object
implements DataOutput, DataInput, Closeable

  此类的实例既可以支持对随机访问文件的读取也可以支持对其写入。
  随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。
  通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。需要特别指出的是,如果流已被关闭,则可能抛出 IOException。

public static void main(String[] args) throws IOException {

//创建随机访问文件
RandomAccessFile raf = new RandomAccessFile("D:/itzhai/arthinking.txt", "rw");
//写入数据
raf.writeInt(1);
raf.writeChar('a');
//文件指针复位
raf.seek(0);
//输出数据
System.out.println(raf.readInt() + "" + raf.readChar());
}

4. 过滤流

过滤流不能直接跟文件打交道,只能通过节点流进行相关的操作。可以从其构造方法中看出:

FilterOutputStream(OutputStream out)

  需要传入一个OutputStream。
  在InputStream和OutputStream的子类中,FilterInputStream和FilterOutputStream是过滤流,其又派生出子类DataInputStream和DataOutputStream数据输入流和数据输出流。
过滤流的主要特点是在输入输出数据同时对所传输的数据做指定类型或格式的转换。

4.1、缓冲输出流BufferedOutputStream

  该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
  当缓冲区写满或者关闭输出流时,一次性输出到流,或者调用flush()方法主动将缓冲区输出到流。

4.2、过滤流的使用例子:

  使用过滤流类BufferedOutputStream和DataOutputStream装饰FilterOutputStream的例子:

public static void main(String[] args) throws IOException {

//创建数据输出流
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("D:/itzhai/arthinking.txt")));
byte a = 1;
char b = 'a';
int c = 2;
//使用数据输出流对象的方法写出数据到输出流
dos.write(a);
dos.write(b);
dos.write(c);
//关闭数据输出流
dos.close();
//创建数据输入流
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream("D:/itzhai/arthinking.txt")));
//使用数据输入流的方法从输入流中读取数据
System.out.println(dis.readByte() + dis.readChar() + dis.readInt());
//关闭数据输入流
dis.close();
}

  使用DataInputStream和DataOutputStream数据文件流的一般步骤:
  ① 创建字节文件流对象
  ② 基于字节文件流对象建立数据文件流对象
  ③ 用数据文件流对象的方法对基本类型的数据进行输入/输出

5. IO流的链接

Input Stream Chain

FileInputStream --> BufferedInputStream --> DataInputStream --> 数据

Output Stream Chain

数据 --> DataOutputStream --> BufferedOutputStream --> FileOutputStream

6. 字符集编码介绍:

ASCII:
  它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO 646。可显示字符:英文大小写字符、阿拉伯数字和西文符号。
ISO-8859-1:
  是四方国家所使用的编码机,单字节的字符集。
GB2312:
  GB2312是中国国家标准的简体中文字符集。它所收录的汉字已经覆盖99.75%的使用频率,基本满足了汉字的计算机处理需要。在*和新加坡获广泛使用。
GBK:
  除了完全兼容GB2312之外,还对繁体中文,不常用汉字和特殊符号进行了编码。
UTF-8:
  UTF-8便于不同的计算机之间使用网络传输不同语言和编码的文字,使得双字节的Unicode能够在现存的处理单字节的系统上正确传输。
  UTF-8使用可变长度字节来储存 Unicode字符,例如ASCII字母继续使用1字节储存,重音文字、希腊字母或西里尔字母等使用2字节来储存,而常用的汉字就要使用3字节。辅助平面字符则使用4字节。
Unicode:
  一种通用字符集,每个字符都用2个字节来表示,对于英文字符采用前面补0的方法实现等长兼容。