概述
Java程序中,对于数据的输入/输出
操作以“流(stream)”
的方式进行。
流的分类
- 按操作数据单位的不同分为:
字节流(8bit)
,字符流(16 bit)
- 按数据流的流向不同分为:
输入流
,输出流
- 按流的角色不同分为:
节点流
,处理流
关于字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
- 字节流:一次读入或读出是8位二进制。
- 字符流:一次读入或读出是16位二进制。
输入流和输出流
- 输入流只能进行读操作
- 输出流只能进行写操作
节点流和处理流
节点流
:直接从数据源或者目的地读写数据
处理流
:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
Java IO流体系
Java中IO流体系如下表所示,最基本的四个抽象基类分别是:InputStream
、OutputStream
、Reader
、Writer
。表格每一列的类都是继承与第一行的抽象基类的。
常用的字符流
FileReader
通常一个流的使用由以下4个步骤组成:
- 提供File类的对象,指明要操作的文件
- 提供具体的流
- 根据不同流,进行不同的操作
- 流的关闭操作
比如以FileReader流为例,那么它的使用步骤是:
- 实例化File类的对象,指明要操作的文件
- 提供具体的流,FileReader流的实例化
- 进行数据的读入操作
- 流的关闭操作
虽然Java IO流体系有众多的类,但是所有的流都基本按照这几个步骤来使用的。下面记录一下FileReader的使用,数据的读入操作。
@Test
public void testFileReader() throws IOException { // 抛出异常
// 1. 实例化File类对象,指明要操作的文件
File file = new File("Hello.txt");
// 2. 提供具体的流
FileReader fr = new FileReader(file);
// 3. 数据的读入
// read():返回读入的一个字符,如果达到文件末尾,那么返回-1
int data = fr.read();
while(data != -1){
System.out.print((char)data);
data = fr.read();
}
// 4. 流的关闭操作
fr.close();
}
上面例子中,通过File类实例化了一个File对象,然后将其当做参数传入了FileReader类进行实例化,之后调用read()方法进行数据的读入,最后执行流的关闭。
关于上面代码有几个需要注意的点:
-
File file = new File("Hello.txt");
这段代码使用的是相对路径,如果是使用IDEA进行编写的,那么这个相对路径在单元测试方法和main方法中有所区别。在单元测试方法中使用相对路径,那么相对的是模块的路径,而在main方法中相对的是项目的路径。比如,这里模块名Day07,这个模块下有文件Hello.txt,而模块位于项目JavaSenior下,那么File类实例化后的实例文件路径如下代码所示:
public class FileReaderTest {
// 在main方法中,输出:D:\code\Java\JavaSenior\Hello.txt
public static void main(String[] args) {
File file = new File("Hello.txt");
System.out.println(file.getAbsolutePath());
}
// 在单元测试方法中,输出:D:\code\Java\JavaSenior\Day07\Hello.txt
@Test
public void test4(){
File file = new File("Hello.txt");
System.out.println(file.getAbsolutePath());
}
}
- 由于创建流不能被Java垃圾回收机制所处理,所以需要手动进行流的关闭操作,但是由于异常,在步骤1,2,3可能出现异常导致第4步(流的关闭)不能被执行,导致资源不能及时回收,所以为了保证资源一定可以执行关闭操作,需要使用try-catch-finally进行处理,而不建议使用抛出的方式。所以,比较规范的流使用代码应该如下:
@Test
public void test2(){
FileReader fr = null;
try{
File file = new File("Hello.txt");
fr = new FileReader(file);
int data;
while ((data = fr.read()) != -1){
System.out.println((char)data);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (fr != null){
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
read()
方法:读取单个字符。作为整数读取的字符,范围在0-65535之间(2个字节的Unicode码),如果已到达流的末尾,则返回-1。 -
read(char cbuf[])
:将字符存入数组,并且返回本次读取的字符数,如果到达流的末尾,则返回-1。
@Test
public void test3() {
FileReader fr = null;
try {
// 1. File类的实例化
File file = new File("Hello.txt");
// 2. FileReader流的实例化
fr = new FileReader(file);
// 3. 读入的操作
char[] cbuf = new char[5];
int len;
/*
假如Hello.txt里的文件内容是helloworld123,这里的read(cbuf)指的是读取5个字符,
即第一次读取hello,返回5,第二次读取world,返回5,
第三次读取123,因为只有123了,返回3,第四次返回-1。
所以3次循环,len变量的值为5,5,3,最后一次为-1。
*/
while ((len = fr.read(cbuf)) != -1) {
// 遍历操作错误的写法
// for (int i = 0; i < arr.length; i++) {
// System.out.println(arr[i]);
// }
// 正确的遍历操作一:
// for (int i = 0; i < len; i++) {
// System.out.print(arr[i]);
// }
// 错误的写法二:
// String str = new String(arr);
// System.out.println(str);
// 正确的写法二:
String str = new String(cbuf,0,len);
System.out.print(str);
}
}catch (IOException e){
e.printStackTrace();
}finally {
// 4. 流资源的关闭
try {
if (fr != null) {
fr.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
未完待续...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。