本章内容:

1.IO概述
2.字节流
3.字符流
4.文件异常的处理

1.IO概述

  1. 什么是IO

生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了ctrl+s ,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。

我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input输出output ,即流向内存是输入流,流出内存的输出流。

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。

  1. IO的分类

根据数据的流向分为:输入流输出流

  • 输入流*:把数据从其他设备上读取到内存中的流。
  • 输出流*:把数据从内存 中写出到其他设备上的流。

根据数据的类型分为:字节流字符流

  • 字节流:以字节为单位,读写数据的流
  • 字符流:以字符为单位,读写数据的流
  1. 顶级父类们
输入流 输出流
字节流 字节输入流
InputStream
字节输出流
OutputStream
字符流 字符输入流
Reader
字符输出流
Writer

2.字节流

一切皆为字节(一切皆字节,让我联想到了在python和js中,一切皆对象;在资本博弈中,一切皆营销)
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

字节流包含InputStream和OutputStream,这两类又是所有字节流的父类。

InputStream类

字节输入流InputStream又包含类诸多子类字节流:FileInputStream(写入文件的输入流)、PipedInputStream(输入管道)、FilterInputStream(过滤输出流)。。。

FileInputStream

java.io.FileInputStream 类是文件输入流,从文件中读取字节到内存中。

方法创建

  • FileInputStream(File file):通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的 File对象 file命名
  • FileInputStream(String name):通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的路径名 name命名

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出 FileNotFoundException 。

1
2
3
4
5
6
// 使用File对象创建流对象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);

// 使用文件名称创建流对象
FileInputStream fos = new FileInputStream("b.txt");

常用方法

方法 描述
void close() 关闭输入流
void mark() 标记输入流的当前位置
void reset() 将读取位置返回到标记处
int read() 从输入流中当前位置读入一个字节的二进制数据,以此数据为低位字节,补足16位的整型量(0~255)后返回,若输入流中当前位置没有数据,则返回-1
int read(byte b[]) 从输入流中的当前位置连续读入多个字节保存在数组中,并返回所读取的字节数
int read(byte b[], int off, int len) 从输入流中当前位置连续读len长的字节,从数组第off+1个元素位置处开始存放,并返回所读取的字节数
int available() 返回输入流中可以读取的字节数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// read()类
int b;
// 循环读取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}

// read(byte[] b)类
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}

// read(byte b[], int off, int len)类
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b, 0, len));// len 每次读取的有效字节个数
}

// 关闭资源
fis.close();
}
}

PS:使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。

OutputStream类

java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地,它定义了字节输出流的基本共性功能方法。FileOutputStream、PipedOutputStream、FilterOutputStream

  • void close():关闭此输出流并释放与此流相关联的任何系统资源
  • void flush():刷新此输出流并强制任何缓冲的输出字节被写出
  • void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流
  • void write(byte[] b, int offset, int len):从指定的字节数组写入 len字节,从偏移量 offset开始输出到此输出流
  • abstract void write(int b):将指定的字节输出流

注:close方法,当完成流的操作时,必须调用此方法,释放系统资源

FileOutputStream

OutputStream超类有很多的子类,FileOutputStream类也是超类中的的一个简单子类,表示文件输出流,用于将数据写出到文件

创建FileOutputStream

  • FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。
  • FileOutputStream(String name):创建文件输出流以指定的名称写入文件(推荐使用相对路径)。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会自动创建该文件;如果文件存在,则会清空这个文件的数据

常用方法

  • close():关闭输出流
  • flush():强制清空缓冲区并执行向外设输出数据
  • write(int b):b表示为一个ASCII码值的字节,每次写入一个字节数据(a-97 A-65)
  • write(byte[] b):b表示为一个字节型数组,每次写入数组中的数据
  • write(byte[] b, int offset, int len):写入指定长度的字节数组,起始索引为offset(默认为0),长度为len字节
  • 调用string.getBytes()方法将字符串转换为字节数组 byte[] b = “hello world”.getBytes();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("c.txt");
// 文件写入方式一:
fileOutputStream.write(65);
// 文件写入方式二:
byte[] bytes = {65,69,68,97};
fileOutputStream.write(bytes);
// 文件写入方式三:
byte[] by = {98,99,105,107};
fileOutputStream.write(by,2,2);
// 文件写入方式四:字符串
fileOutputStream.write(" hello world;hello china!!!".getBytes());
fileOutputStream.close();
}
}
// 输出结果:AAEDaik hello world;hello china!!!

数据追加续写:

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?

1
2
public FileOutputStream(File file, boolean append):创建文件输出流以写入由指定的 File对象表示的文件
public FileOutputStream(String name, boolean append):创建文件输出流以指定的名称写入文件

这两个构造方法,参数中都需要传入一个boolean类型的append值,true 表示追加数据,false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了

写出换行:

  • 回车符\r和换行符\n
    • 回车符:回到一行的开头(return)
    • 换行符:下一行(newline)
  • 系统中的换行:
    • Windows系统里,每行结尾是 回车+换行 ,即\r\n
    • Unix系统里,每行结尾只有 换行 ,即\n
    • Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一
1
2
fos.write(97);
fos.write("\r\n".getBytes());

3.字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

字符流包含Reader和Writer,这两类又是所有字符流的父类。

Reader

java.io.Reader字符输入流,是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

FileReader

java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

创建方式

  • FileReader(File file):创建一个新的FileReader,给定要读取的File对象
  • FileReader(String fileName):创建一个新的FileReader,给定要读取的文件的名称

当你创建一个流对象时,必须传入一个文件路径,类似于FileInputStream,如果文件不存在则会报错。

1
2
3
4
5
6
// 使用File对象创建流对象
File file = new File("a.txt");
FileReader fr = new FileReader(file);

// 使用文件名称创建流对象
FileReader fr = new FileReader("b.txt");

常用方法

  • void close():关闭此流并释放与此流相关联的任何系统资源
  • int read():从输入流读取一个字符
  • int read(char[] ch):从输入流中读取字符数组

读取字符数据:

  1. read()方法读取字符:每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取,代码使用演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("c.txt");
int b;
while((b = fileReader.read())!= -1){
System.out.print((char)b + " ");
}
fileReader.close();
}
}
// 结果为:欢 迎 来 到 英 雄 联 盟 , 敌 军 还 有 三 十 秒 到 达 战 场 !
  1. read(char[] ch)使用字符数组读取文件,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 ,代码使用演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class FISRead {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("read.txt");
// 定义变量,保存有效字符个数
int len ;
// 定义字符数组,作为装字符数据的容器
char[] cbuf = new char[2];
// 循环读取
while ((len = fr.read(cbuf))!=-1) {
System.out.println(new String(cbuf,0,len));
}
// 关闭资源
fr.close();
}
}
输出结果:
黑马
程序

Writer

java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

FileWrite类

Java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

创建方式

1
2
FileWriter(File file):创建一个新的 FileWriter,给定要读取的File对象
FileWriter(String fileName):创建一个新的 FileWriter,给定要读取的文件的名称

当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。构造举例,代码如下:

1
2
3
4
5
6
// 使用File对象创建流对象
File file = new File("a.txt");
FileWriter fw = new FileWriter(file);

// 使用文件名称创建流对象
FileWriter fw = new FileWriter("b.txt");

常用方法

  • void write(int c):写入单个字符
  • void write(char[] ch):写入字符数组
  • void write(char[] ch, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数
  • void write(String str):写入字符串
  • void write(String str, int off, int len):写入字符串的某一部分,off字符串的开始索引,len写的字符个数
  • void flush():刷新该流的缓冲
  • void close():关闭此流,但要先刷新它

数据写入:

  • write(int b):每次可以写出一个字符数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象(追加模式)
FileWriter fw = new FileWriter("fw.txt", true);
// 法一:单字符写入数据
fw.write(97);
fw.write('b');

// 法二:字符数组写入数据
char[] chars = "黑马程序员".toCharArray();
fw.write(chars);

// 法三:字符串写入数据
fw.write("china!");
/*
【注意】关闭资源时,与FileOutputStream不同。
如果不关闭,数据只是保存到缓冲区,并未保存到文件。
*/
fw.close();
}
}

小结:

  1. 字符流,只能操作文本文件,不能操作图片,视频等非文本文件。
  2. 当我们单纯读或者写文本文件时使用字符流,其他情况使用字节流
  3. java的字节字符流调用close方法会清空缓存区的内容,也就是flush一下,不过为了保险起见,应该先调用flush()再close()
    • flush():刷新缓冲区,流对象可以继续使用,还可以继续读写
    • close():先刷新缓存区,告知系统关闭资源,流对象不再使用

4.文件异常的处理

之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用try...catch...finally 代码块,处理异常部分,代码使用演示:
java的io异常一定要用try…catch…finally,因为直接throws抛出异常是交给jvm,jvm直接中断程序运行,这会导致流对象无法关闭,会占用资源,这是要避免的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HandleException1 {
public static void main(String[] args) {
// 声明变量
FileWriter fw = null;
try {
//创建流对象
fw = new FileWriter("fw.txt");
// 写出数据
fw.write("黑马程序员"); //黑马程序员
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

 评论

联系我 | Contact with me

Copyright © 2019-2020 谁知你知我,我知你知深。此恨经年深,比情度日久

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议