Java的IO
在学习 IO 流之前,我们写的程序都是在内存里面自己跟自己玩。比如你声明一个变量,创建一个数组,创建一个集合,写一个排序算法,模拟一个链表,使用一些常用的 API,其实都只是自己在内存里面玩一玩。
如果你是用 C 语言的话,除了基本数据类型的声明外,你都需要使用 malloc() 申请内存来存放数据。
如果你的程序需要与外部设备交互,那么就需要 IO 了。那么用什么交互呢?用的是比特流,也叫做字节流。显然不可能靠之前学习的那些数组,集合,常用类,String等等来完成。
既然要学习 IO 流,就得针对某个输入输出设备来学习。哪种输入输出设备最重要也最常见,是硬盘。硬盘在这里的含义也可以理解为文件系统。因此学习 IO 流的时候,基本上是学习 Java 如何操作文件系统。
顶层抽象类 InputStream 和 OutputStream
要使用 Java 操作文件系统,首先要学习的就是文件的表示,即 File 类。然后,要操作文件,虽然我们大部分操作都是操作文件系统,但是要明白 IO 流的概念不仅仅局限在操作文件上,编程语言是要操作所有的输入输出。于是,Java API 提供了两个顶层抽象类,用来表示操作所有的输入输出 InputStream,OutputStream。并且,这两个类表示字节的输入输出,因为输入输出的本质是字节流。这里注意体会一句话“字节流是最最基本的流”,这句话的由来就是因为计算机底层传递的就是字节。
操作文件系统 FileInputStream 和 FileOutputStream
当我们要操作文件的时候,就需要具体的对文件系统操作的 IO 实现类,于是我们需要学习 FileInputStream 和 FileOutputStream,它们是文件输入输出字节流。这里之所以 FileInputStream/OutputStream 作为子类出现,按照面向对象思想理解就是,将来还有别的字节流来操作别的设备(比如将来需要通过操作网络设备获取网络数据,再比如需要操作机器人,那么或许就会再来个 RobotInputStream 和RobotOutputStream,这些新的需求也就都可以继承这个体系)
(这里顺便提一句架构设计思想,其中有一种设计原则叫“开闭原则”,其核心是:一个对象对扩展开放,对修改关闭。就是说,一旦写好了某个类,就不要去轻易改动他,而是要保证它一直能运行下去,而面对新的功能需求时,只要在原有代码上增加即可,而不是修改原有代码。要做到开闭原则,就需要分清需求中未来哪些部分是稳定的,哪些是很可能变化的,而往往抽象的部分是最稳定的,把稳定的内容分离出来,就能满足开闭原则。这就是为什么 Java 的类设计的如此之琐碎,为什么我们要从继承关系角度去理解 JavaIO 流的设计)
增加缓存区 BufferedInputStream 和 BufferedOutputSteam
学了文件 IO 字节流之后,我们会发现原始的字节流对象用起来没那么高效,因为每个读或写请求都由底层操作系统处理,这些请求往往会触发磁盘访问、网络活动或其他一些相对昂贵的操作。不带缓冲区的流对象,只能一个字节一个字节的读,每次都调用底层的操作系统 API,非常低效,而带缓冲区的流对象,可以一次读一个缓冲区,缓冲区空了才去调用一次底层 API,这就能大大提高效率。所以又有了 BufferedInputStream 和 BufferedOutputSteam,他们的用法是把字节流对象传入后再使用,也相当于把它俩套在了字节流的外面,给字节流装了个“外挂”,让基本字节流如虎添翼。
FileInputStream / FileOutputStream 一般总是应该用 BufferedInputStream / BufferedOutputStream 装饰
public class BufferedInputStreamExample {
public static void main(String[] args) {
File file = new File("file.txt");
FileInputStream fileInputStream = null;
BufferedInputStream bufferedInputStream = null;
try {
fileInputStream = new FileInputStream(file);
bufferedInputStream = new BufferedInputStream(fileInputStream);
// Create buffer
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, bytesRead));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class BufferedOutputStreamExample {
public static void main(String[] args) {
File file=new File("outfile.txt");
FileOutputStream fileOutputStream=null;
BufferedOutputStream bufferedOutputStream=null;
try {
fileOutputStream=new FileOutputStream(file);
bufferedOutputStream=new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("This is an example of writing data to a file".getBytes());
bufferedOutputStream.write(" using BufferedOutputStream".getBytes());
bufferedOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fileOutputStream!=null){
fileOutputStream.close();
}
if(bufferedOutputStream!=null){
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}变成人类可读可写 InputStreamReader 和 OutputStreamWriter
说到操作文件,就不得不提到文件的分类和编码格式。文件分为二进制文件和文本文件,二进制文件是用记事本打开后看不懂的,他们的编码格式是特殊的,比如 pdf 文件,exe 文件。记事本打开后人能看懂的只有纯文本文件,我们处理文件(或者说处理任何的字节流),就免不了处理一些文本文件(或文本字节流)。如果是英语国家的人还好说,因为他们是用的常用字符用一张 ASCII 码表就能表示得出来,用一个字节就能表示一个字母。但是显然,对非英语国家的人来说,一个字节的大小无法表示他们所有的文字。因此,人们需要有能够处理字符的类,或者说这个类提供一个功能:就是把输入的字节转成字符,把要输出的字符转成计算机可以识别的字节。所以,你需要两个转换流:InputStreamReader 和 OutputStreamWriter。这两个类的作用分别是把字节流转成字符流,把字符流转成字节流。但是这两个流需要套在现成的字节流上才能使用,当中用到的设计模式也就是常说的装饰模式。当字节流被转成字符流之后,恭喜你,你可以不必操作字节流了,而是可以用人类的方式 read 和write 各种“文字”。
还是回到文件系统,我们最常见的是和文件系统打交道,那么针对如此常见的用途,读取文本文件能不能用一种方便的方式呢?当然,大牛们替你想到并提供了。FileReader 和 FileWriter 这两个流对象可以直接把文件转成读取、写入流。让你省去了创建字节流,再套上转换流的步骤。看看这类名起的,实际上很形象,xxxReader 和 xxxWriter ,明摆着告诉你“阅读和书写”都是“人可以做的”也就是他们表示的是字符流。同理上面的 InputStreamReader 和 OutputStreamWriter,表示的是把字节流转成人可读的,把字节流转成人可写的。因此他们的顶层抽象类:Reader 和 Writer,表示的是所有人类可读可写的字符流统称。
同上面说的缓冲区的作用,再把Reader和Writer做成高效的,就需要BufferedReader和BufferedWriter,把它们套在Reader和Writer上,就能实现高效的字符流。
本来来源:
作者:大概是条废咸鱼
链接:https://www.zhihu.com/question/67535292/answer/1248887503
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论已关闭