一、IO流是什么
1.1 IO流概述
IO流就是数据的流通,input和output,输入和输出,那是输入哪里?又是输出哪里?其实就是内存和硬盘之间数据的传输。
内存:特点是临时存储,无法持久化保存;我们在写代码时,使用到的一些数据,对象,都是在内存中临时存储着,如果你想要长期存储,那我们就要将数据放在硬盘中,就像你电脑里的一些word文档和txt文本一样。
IO流操作也就是执行这些输入和输出操作。
1.2 IO流的分类
按方向分类:输入流和输出流
- 输入流:将硬盘中中的数据读入到内存中。
- 输出流:将内存中的数据写出到硬盘中。
按单位分类:字节流和字符流
- 字节流:以字节为单位,可以读写任意类型的文件,例如:.jpg .mp3等等。
- 字符流:以字符为单位,只能读写文本类型的文件。
(文本类文件:不知道怎么描述了,其实就是可以使用记事本打开且不会乱码的)
按功能分类:节点流和过滤流
- 节点流:只具备基本的读写功能。
- 过滤流:除了基本的读写功能外,还具备其他功能。
需要注意一点,这些流之间并不是互斥的,可以根据不同的分类有不同的叫法,比如一个流是输出流,但是他又是以字节为单位,所以它还是字节流,又比如他又只具备基本的读写功能,所以就是字节节点输出流。
我们这一篇文章里面不会对每一种单独举例介绍,主要是学会使用IO流如何进行输入和输出操作。下面的话就着重使用一下字节节点输出流和字节节点输入流。
1.3 字节流父类
字节流的父类是一个抽象类。
字节输入流父类:InputStream
字节输出流父类:OutputStream
二、字节节点输出流
FileOutputStream,字节节点输出流,是一个输出流,也就是我们将内存中的数据输出存储到硬盘中。
2.1 构造方法
java
FileOutputStream(String name):
//name: 文件路径 不在原有内容上追加,新内容会覆盖旧内容
FileOutputStream(String name,boolean append):
//append: 是否在原有内容上追加 设置为true代表在原有内容上进行追加
默认情况下,字节节点输出流在将数据存储到文件时,相当于将这次输出的数据直接覆盖在原文件的内容之上,比如原文件中有个"1",我使用第一个构造方法去输出"2",那么结束后文件里是"2",而不是拼接的"12"。
想要实现拼接就要使用第二个构造器,将append改为true,就可以在原文件内容的基础上进行输出了。
2.2 了解文件路径
在上边我们可以知道,在创建流的字节节点输出流对象时,需要传一个name的文件路径。
文件路径分为绝对路径和相对路径,如下:
- 绝对路径:盘符/文件夹/.../文件名
- 相对路径:文件夹/.../文件名(这里的"文件夹"是相对于项目路径而言的,也就是在项目目录下。)
这个文件路径也就是我们输出的数据要存放的地方,当然如果这个路径指向的地址不存在怎么办?
当文件不存在时,会自动的创建文件;当文件夹不存在时,不会创建文件夹,只会报错。
2.3 方法使用
输出的方法主要是:
java
write(int c);//写出单个字符到文件中
比如我们要输入一个"HelloWorld!"在一个文件中,我们可以像下面这样写:
java
//创建输出流对象,并规定append为true
FileOutputStream out = new FileOutputStream("files/test.txt",true);
String str = "Hello World";
//利用循环将上边那个字符串逐一输出
for (int i = 0; i < str.length(); i++) {
out.write(str.charAt(i));
}
//释放资源
out.close();
要记得,在使用完毕后记得调用close方法释放资源。
三、字节节点输入流
FileInputStream,字节节点输入流,也就是将硬盘中的数据读取到内存中来。
3.1 构造方法
java
FileInputStream(String name):
//name:文件路径
3.2 方法使用
输入的方法主要是:
java
int read();
//一次读一个字节的内容到内存中,当文件读取到末尾时返回-1
我们读取一个文件时使用这个方法是一个字节一个字节读入的,所以我们要一直调用,直到字节读取完毕,所以我们应该使用循环来进行read操作:
java
//创建 字节节点输入流
FileInputStream in = new FileInputStream("files/b.txt");
//读操作
while(true){
int read = in.read();
if(read==-1)break;//当文件读取到末尾时 返回-1 跳出循环
System.out.println((char)read);
}
//关流
in.close();
四、拷贝文件
学会了输入和输出,我们就可以将内存当作一个中转站,将一个文件中的数据转移到另外一个文件中去。
具体的操作就是我们可以声明输出和输入资源,将数据一边输入到内存中,一边输出到另一个文件,如下所示:
java
//1. 创建文件输入流
FileInputStream in = new FileInputStream("D:/.../xx");
//3 创建文件输出流
FileOutputStream out = new FileOutputStream("D:/.../zz");
//2. 读取数据
while(true){
int read = in.read();
if(read==-1)break;
//4. 写出到硬盘中
out.write(read);
}
//5 关流
in.close();
out.close();
因为我们这种是字节节点流,虽然可以拷贝任意类型的文件,但是是一个字节一个字节进行的转移,所以效率低下,如果文件较大会很慢。
五、流中对异常的处理
因为在输入流和输出流创建时都会抛出异常,以及关流也会上抛异常,所以我们要对这些异常进行一个处理,如下所示:
java
try{
//1. 创建文件输入流
FileInputStream in = new FileInputStream("D:/.../xx");
//2. 读取数据
while(true){
int read = in.read();
if(read==-1)break;
}
}catch (IOException e){
e.printStackTrace();
}finally{
//3 关流
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
但是我们会发现处理这几个异常,要多写好几行代码。
其实在Java 7引入的try-with-resources(自动资源管理)特性,基于AutoCloseable接口的规定中,我们可以将资源声明,也就是相应的对象写在try后,这样资源可以自动关闭。
但是也有一定的要求,就是资源必须实现AutoCloseable或Closeable接口,因为FileInputStream实现了Closeable接口,而Closeable继承了AutoCloseable,在try块执行完毕后,无论是否发生异常,都会自动调用资源的close()方法。
如下所示:
java
try(FileInputStream in = new FileInputStream("D:/.../xx")){//1. 创建文件输入流
//2. 读取数据
while(true){
int read = in.read();
if(read==-1)break;
}
}catch (IOException e){
e.printStackTrace();
}