Java 输入输出流学习笔记
文件 File 类
创建 File 实例
首先在进行输入输出处理之前,我们来看看数据的载体——文件。
因为我们在内存中存储的数据都是暂时的,一旦断电,都会消失,所以通常我们会把数据以文件的形式存储到永久存储的硬盘中,可以说,数据的操作单位就是文件。而 Java 中的 File 类就是专门对文件进行概括的一个类,它能很好的控制一个类。
1 | File file=new File("Xorex.txt"); |
上面我们通过给构造方法传入一个文件的路径,成功实例化了 File 类。这个实例就是 Xorex.txt
这个文件,上面我们传入了 Xorex.txt
这个文件的相对路径,可以唯一确定这个文件,当然绝对路径也是可以的:E:\\Java\\Xorex.txt
。
需要注意的是,在不同的操作系统中,路径的表示是不同的,Windows 里面使用 \
表示路径分隔符,而 Linux 里面使用 /
表示路径分隔符。又因为在字符串中 \
表示转义,所以在写绝对路径或者相对路径的时候,需要用 \\
来分隔路径。
得到文件路径
得到文件路径中,File 类里面有三种不同的方法,分别是:
1 | getPath(); // 返回构造方法传入的的路径 |
获取 File 实例的信息
对于一个通过路径得到的 File 实例,如果路径结尾是一个文件名,那么 File 实例就是这个文件,如果是一个文件夹的名字,那么 File 实例就是这个文件夹。两者都可以被 File 类表示。
对于确定了一个文件/文件夹的 File 实例,我们可以通过方法获取一些这个文件/文件夹的信息。
1 | boolean isFile() 是否为文件 |
如果确定了这个实例是一个文件夹,那么还可以使用下面的方法来获取文件夹里面的东西。
1 | String[] list() // 以字符串列出所有内容 |
操作文件
如果我们给的路径源文件不存在,可以直接创建,如果存在了,可以直接删除:
1 | boolean createNewFile() // 文件路径的所有文件夹必须全部都存在才能创建成功 |
我们还可以用 File 的静态方法创建一个临时文件,这个临时文件默认保存在 C 盘用户文件夹下面等等等的一个叫作 Temp 的文件夹。我们可以自定义这个临时文件的前缀和后缀,甚至是保存地点,这个临时文件的名字会在前缀和后缀之间加上一串随机数,且不会自动删除,所以最好获得临时文件的实例之后直接调用 deleteOnExit()
。
1 | File static createTempFile(prefix,suffix,[File]) |
可选参数 File ,为是否改变默认的保存地址为 File 的地址(需要传入的 File 实例是一个文件夹,然后临时文件创建到这个文件夹中)。
文件夹操作
1 | boolean mkdir() //创建文件夹,必须前面路径的文件夹存在才能创建成功。 |
字节 IO 流
try(resource)-catch 语句
InputStrean 和 OutputStream 这种流 IO 在运行的时候会占用各种资源,所以在结束流之后关闭流来释放资源是很有必要的。但是对于这种操作,往往可能关闭的不太顺利,可能出现流正在被使用无法关闭的情景,所以 try-catch
语句也就常常伴随着 close() 的调用而出现,来解决流关闭失败后的处理。
但是用一次 close() 就写一个 try-catch
有点麻烦了,Java 就给了一个小语法糖:try(resource)-catch
语句。这个语句和 try-catch
的区别就是对于在(resource)
这里开启的资源,结束运行之后,会自动调用资源的 close() 方法。因此能使用 try(resource)-catch
的语句必须拥有 close() 方法,也就是实现 closeable 接口。
正巧 IOStream 都实现了这个接口,就可以使用上面的语句是申请 IO 流资源而不用写关闭代码,因为编译器会自动帮忙写,所以也是一个语法糖。
1 | try(new InputStream(File)) { |
看源代码发现,这个 InputStream 和 OutputStream 抽象类实现了 Closeable 接口。
1 | public abstract class InputStream implements Closeable |
而这个 Colseable 接口只有一个方法,就是 close() 方法, 也是我们对 IOStream 类操作的时候,最后需要释放资源时使用的 close() 方法。只要这个类实现了 Closeable 接口,那么就可以应用在 try(resource)-catch
语句里面,运行结束之后自动关闭流。
InputStream:FileInputStream
对于从文件中读取,使用 InputStream 的子类 FileInputStream,在构造方法传入 File 实例或者一个文件路径的字符串。
1 | public FileInputStream(String name) //自己根据路径找文件 |
确认文件之后就可以使用内置的 read() 方法读取文件内容了。
1 | public int read() //一次读取一个字节 |
OutputStream:FileOutputStram
对于向文件写入,使用 OutputStream 的子类 FileOutputStream,在构造方法传入 File 实例或者一个文件路径的字符串。
1 | public FileOutputStream(String name) //传入文件路径覆盖写入 |
确认文件之后就可以使用内置的 write() 方法向文件中写入内容了。
1 | public void write(int b) //读取一个字节 |
在 Java 中为了效率,有了输入输出的缓冲区,只有缓冲区内数据到达一定程度,才会真正的写入到文件中,但是有些情况我们需要即使写入,那么就可以调用 flush() 来将缓冲区的数据直接写入到文件中。
Filter 设计模式
因为对于 IO 的输入输出来说,有的时候我们想要在输入输出的同时添加一些额外的工作,比如加密,解码等等。在通常情况下,解决方法就是单独写一个类继承于 IOStream 然后添加新功能,但当各种小功能需要排列组合使用的时候,就会引起功能类数量爆炸的情况,所以这里引入了 Filter 的设计模式,来对功能类进行排列组合。
首先要实现的功能需要继承于 FilterInputStream/FilterOutputStream ,然后功能类的构造方法参数为 IOStream 并用 super() 传给 Filter 类的构造方法,最后就可以重写 read() 和 write() 方法,使用原输入输出就用 in.read() 或者 out.write() ,只需要前后添加新功能即可,类似于普通代理。
有了功能类之后,就可以利用在构造方法中不停套娃其他的功能类,实现功能的排列组合,一个简单的对输出数据进心 base64 加密的功能类:
1 | package Java; |
Filter 父类里面的构造方法其实就是将传入的输入流实例赋值给 in 或者 out,然后这个可以被子类重写方法的时候调用,从而实现功能的组合。
剩下的东西不太想写了,鸽了。以后直接上传思维导图好了。
主要 IO 框架的思维导图:
IO 相关类的继承关系的思维导图: