导航
我们为什么需要IO流?
我大学主修的专业是电路硬件与通信,我觉得先从硬件上理解IO会更容易一些。
IO就是取了Input和Output的首字母,就是输入和输出的意思。在硬件上的IO接口,是分别用来接输出设备与输出设备的。因为电路与人类,毕竟不是同一种类,无法直接交流。那么,我们人类如何与单片机、电脑等硬件交流呢?这时就有了输入(Input)设备:鼠标、键盘、麦克风;输出(Output)设备:屏幕、扬声器、打印机等。
我们写的Java主程序,就相当于单片机、电脑,程序的运行需要数据,而数据的获取往往需要跟外部系统进行通信。Java 的 IO流,就是广泛应用于文件传输和网络编程中的数据通信处理技术。
IO流
Java中,IO流主要可以分为五个部分:数据源、输入流、主程序、输出流、目的数据源。
其中,数据源和目的数据源的表现形式有多种,可以为:文件、数据库、其他程序、内存、网络连接等。
而其中的输入输出流是相对于主程序来说的,也是我们在IO流技术中需要掌握的。
大致的传输过程,可以图形化表示如下:
IO流类体系
在Java IO流技术中,常用的抽象类与实现类的关系图如下:
IO流的分类
输入流与输出流
按流的方向分类,可以将流分为输入流与输出流:
- 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
- 输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
字节流与字符流
按流的数据单位分类,可以分为字节流与字符流:
- 字节流:以字节为单位获取数据,InputStream/OutputStream抽象类的子类为字节流。
- 字符流:以字符为单位获取数据,Reader/Writer抽象类的子类为字符流。
字节数组byte[]、字符数组char[]与字符串String
要理清字节流与字符流,就先要清楚字节数组byte[]、字符数组char[]与字符串String之间的关系。
byte与char都是基本数据类型。其中byte是数值型,占1字节空间,char字符型占2字节的空间。
char是可以转换为byte的,但需要有规定的字符集,而且不同字符集的转换规则不一样,常见的字符集转换时字节个数:
-
UTF-8编码中,一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
-
Unicode编码中,一个英文等于两个字节,一个中文(含繁体)等于两个字节。符号:英文标点占一个字节,中文标点占两个字节。
-
GBK编码方式是中文占两个字节,英文占1个字节。
关于字符集,就按字典、键值对的方式理解。如在UTF-8中:a对应97,‘我’对应{-2,-120,111},是固定且一一对应的。
字符串String类型,就是字符数组char[]的一种实现类。类中提供了与字节数组、字符数组相互转换的方法。所以我们可以通过String,快捷方便地实现字节数组byte[]与字符数组char[]的相互转换。
编码
我们把字符串转换为字节数组的过程,称为编码(由人可以读懂到不可读懂)。实现案例:
public class Test {
public static void main(String args[]) throws UnsupportedEncodingException{
String str = "你我他";
byte[] bytes = str.getBytes("UTF-8"); // String类提供的转byte方法,若不传入字符集,则选择Eclipse默认字符集
//若传入字符集与String的字符集不一致,则会乱码
for (byte b : bytes) {
System.out.print(b+",");
}
}
}
-------------------------------------------------------
输出结果为:
-28,-67,-96,-26,-120,-111,-28,-69,-106, // 一汉字 , 3字节
解码
我们把字节数组转换为字符串的过程,称为解码(由人不可读懂到可读懂)。实现案例:
public class Test {
public static void main(String args[]) throws UnsupportedEncodingException{
byte[] bytes = {-28,-67,-96,-26,-120,-111,-28,-69,-106};
String str = new String(bytes,0,bytes.length,"UTF-8"); // 通过String构造器,实现字节数组转String
System.out.println(str);
}
}
------------------------------------------------------
输出结果为:
你我他
String与字符数组char[]的转换
编码与解码就是String与字节数组byte[]的转换,String与字符数组char[]的转换与其类似,但不用涉及字符集:
public class Test {
public static void main(String args[]) throws UnsupportedEncodingException{
String str = "你我他";
char[] charArray = str.toCharArray();//String类的toCharArray方法,转数组
for (char c : charArray) {
System.out.print(c+",");
}
System.out.println();
String str2 = new String(charArray,0,charArray.length);//调用String构造器,不涉及字节,所以不涉及字符集
System.out.println(str2);
}
}
------------------------------------------------------
输出结果为:
你,我,他,
你我他
节点流与处理流
按处理的对象分类,可以将流分为节点流与处理流:
- 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
- 处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
处理流不可以单独直接使用,因为它需要有一个有数据的流,才能对流进行处理。
四大IO抽象类
InputStream/OutputStream和Reader/Writer类是所有IO流类的抽象父类,我们有必要简单了解一下这个四个抽象类的作用:
InputStream
此抽象类是表示字节输入流的所有类的父类。InputSteam是一个抽象类,它不可以实例化。 数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类 。
继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
常用方法:
-
int read():读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。如果未读出字节则返回-1(返回值为-1表示读取结束)。
-
void close():关闭输入流对象,释放相关系统资源。
OutputStream
此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。
常用方法:
-
void write(int n):向目的地中写入一个字节。
-
void close():关闭输出流对象,释放相关系统资源。
Reader
Reader用于读取的字符流抽象类,数据单位为字符。
-
int read(): 读取一个字符的数据,并将字符的值作为int类型返回(0-65535之间的一个值,即Unicode值)。如果未读出字符则返回-1(返回值为-1表示读取结束)。
-
void close() : 关闭流对象,释放相关系统资源。
Writer
Writer用于写入的字符流抽象类,数据单位为字符。
-
void write(int n): 向输出流中写入一个字符。
-
void close() : 关闭输出流对象,释放相关系统资源。