Java IO 总结
IO 是什么?其实就是Java中的一种输入和输出功能,也可以理解为对文件的写入和读出的操作。Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出源抽象表述为”流”。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。
在Java中有输入、输出两种IO流,每种输入、输出流又分为字节流和字符流两大类。
在介绍IO流之前,我们先明白Java中字节和字符的关系。
1.字节与字符
1.1 概念
- 字节:每个字节(byte)有8bit组成
- 字符:一个汉字或者英文字母
1.2 两者关系
Java采用unicode编码,2个字节来表示一个字符,而一个字符表示一个汉字或英文字母,具体字符与字节之间的大小转换视编码情况而定,这些不是我们需要关心的事情,我们只需要知道不同的编码对应的转换规则不一样。有时候读取的数据是乱码,就是因为编码方式不一致,需要进行转换,然后再按照unicode进行编码。
getBytes(String charsetName)使用指定的编码方式将此String编码为 byte 序列,并将结果存储到一个新的 byte 数组中。如果不指定将使用操作系统默认的编码方式,我的电脑默认的是GBK编码。
String str = "hello world 你好哦";
int byte_len = str.getBytes().length;
int len = str.length();
System.out.println("字节长度为:" + byte_len);
System.out.println("字符长度为:" + len);
// 编译工具为IDEA,输出结果如下
字节长度为:21
字符长度为:15
系统默认编码方式:UTF-8
2.IO流的分类
2.1 输入流和输出流
根据数据流向不同分为:输入流和输出流。
输入流:从流中读取数据
输出流:将数据写入到流中
2.2输入流和输出流
字节流和字符流和用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同。
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
2.3节点流和处理流
按照流的角色来分,可以分为节点流和处理流。
可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被成为低级流。
处理流是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流。
//节点流,直接传入的参数是IO设备
FileInputStream fis = new FileInputStream("test.txt");
//处理流,直接传入的参数是流对象
BufferedInputStream bis = new BufferedInputStream(fis);
- 节点流
类型 | 字符流 | 字节流 |
---|---|---|
File(文件) | FileReader FileWriter |
FileInputStream FileOutputStream |
Memory Array | CharArrayReader CharArrayWriter |
ByteArrayInputStream ByteArrayOutputStream |
Memory Array | StringReader StringWriter |
|
Pipe(管道) | PipedReader PipedWriter |
PipedInputStream PipedOutputStream |
- 处理流 ,用来包装节点流
类型 | 字符流 | 字节流 |
---|---|---|
Buffering | BufferedReader BufferedWriter |
BufferedInputStream BufferedOutputStream |
Filtering | FilterReader FilterWriter |
FilterInputStream FilterOutputStream |
Converting between Bytes and Characters | InputStreamReader OutputStreamWriter |
|
Object Serialization | ObjectInputStream ObjectOutputStream |
|
DataConversion | DataInputStream DataOutputStream |
|
Counting | LineNumberReader | LineNumberInputStream |
Peeking Ahead | PushbackReader | PushbackInputStream |
Printing | PrintWriter | PrintStream |
(1)Buffering缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader与BufferedWriter、BufferedInputStream与BufferedOutputStream。
(2)Filtering 滤流:在数据进行读或写时进行过滤:FilterReader与FilterWriter、FilterInputStream与FilterOutputStream。
(3)Converting between Bytes and Characters 转换流:按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader):InputStreamReader、OutputStreamWriter。
(4)Object Serialization 对象流 :ObjectInputStream、ObjectOutputStream。
(5)DataConversion数据流: 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 。
(6)Counting计数流: 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream。
(7)Peeking Ahead预读流: 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream。
(8)Printing打印流: 包含方便的打印方法 :PrintWriter、PrintStream。
3.IO流的四大基类
3.1 字符流 Reader和Writer
字符流原理:
Reader是字符输入流的父类
Writer是字符输出流的父类
字符流是以字符(char)为单位读写数据的,一次处理一个unicode
字符流的底层仍然是基本的字节流
Reader常用方法:
int read():读取一个字符,返回的int值“低16位”有效
int read(char[] chs):从该流中读取一个字符数组的length个字符并存入该数组,返回值为实际读取到的字符量
Writer的常用方法:
void write(int c):写出一个字符,写出给定int值“低16位”表示的字符
void write(char[] chs):将给定字符数组中所有字符写出
void write(String str):将给定的字符串写出
void write(char[] chs,int offset,int len):将给定的字符数组中从offset处开始连续的len个字符写出
3.2 字符输入流
InputStream是所有字节输入流的父类,其定义了基础的读取方法,如下:
int read():读取一个字节,以int形式返回,该int值的“低8位”有效,若返回值为-1则表示EOF
int read(byte[] d):尝试最多读取给定数组length个字节并存入该数组,返回值为实际读取到的字节量
OutputStream是所有字节输出流的父类,其定义了基础的写出方法,如下:
void write(int d):写出一个字节,写的是给定的int的“低8位”
void write(byte[] d):将给定的字节数组中的所有字节全部写出
4.常见用法
4.1 对象序列化
// 创建写出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 对象写出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 写对象
oos.writeObject(this);
// 关闭
oos.close();
// 创建写出流
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
// 转成对象流
ObjectInputStream ois = new ObjectInputStream(bis);
personClone = (Person) ois.readObject();
ois.close();
4.2 文件读写
// 字节流方式写出
FileOutputStream fos = new FileOutputStream("fos.txt");
String str = "Hello,World";
byte[] date = str.getBytes();
fos.write(date);
System.out.println("写出完毕");
fos.close();
// 字节流方式写入
FileInputStream fis = new FileInputStream("fos.txt");
int d = -1;
while((d=fis.read())!=-1){
System.out.print((char)d);
}
fis.close();
4.3 转换流读写
// 转换流写入
FileOutputStream fos = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
osw.write("测试");
osw.close();
// 转换流读取
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
int d = -1;
while((d=isr.read())!=-1){
System.out.print((char)d);
}
isr.close();
4.4 缓冲流读写
//创建缓冲字节输出流
FileOutputStream fos = new FileOutputStream("fos.txt");
//所有字节被存入缓冲区,等待一次性写出
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "Hello Java";
byte[] date = str.getBytes();
bos.write(date);
//关闭流之前,缓冲输出流会将缓冲区内容一次性写出
bos.close();
//创建缓冲字节输入流
FileInputStream fis = new FileInputStream("fos.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int d = -1;
//缓冲读入,实际上并非是一个字节一个字节从文件读取
while((d=bis.read())!=-1){
System.out.println(d);
}
bis.close();
5.总结
使用IO流通过四点来完成:
5.1 明确数据的来源和数据到达的目的地。
来源:输入流 [InputStream,Reader]。
目的:输出流 [OutputStream,Writer]。
5.2操作的数据是否是纯文本。
是:字符流,使用Reader与Writer;
否:字节流,使用InputStream与OutputStream。
5.3明确要使用哪个具体的对象。 通过设备来进行区分:
源设备:内存用数组,硬盘就加file,键盘用System.in;
目的设备:内存用数组,硬盘就加file,键盘用System.out。
5.4明确是否还需要其他额外功能:例如
(1)是否需要较高的效率,即是否需要使用缓冲区,是就加上Buffered;
(2)是否需要转换,是,就使用转换流,InputStreamReader 和 OutputStreamWriter。