一个屌丝程序猿的人生(三十七)

2年前 (2022) 程序员胖胖胖虎阿
267 0 0

  对于林萧来说,学习Java基础的过程,比想象当中的要顺利许多。但尽管如此,在这个过程当中,还是偶尔会碰到比较难搞的奇葩知识点。

  这不,林萧看了没多久视频,就碰到了一个把他搞得晕头转向的家伙——文件IO。

  说到这个文件IO,第一个让林萧觉得别扭的地方,就是这个输入输出流的叫法。

  明明是读取一个文件,却叫做文件输入流,也就是FileInputStream。而明明是写入一个文件,却叫做文件输出流,即FileOutputStream。

  好在林萧一直都知道类比的学习方式,拿标准的输入输出流,和文件的输入输出流一对比,就基本上能转过来弯儿了。

  标准的输入流,是为了接受控制台标准输入流输入的字符,因此便称作输入流。那么文件这里也是类似,只不过,与标准输入流不同的是,这里的输入是一个文件。

  同样的,输出流也是一样,标准输出流是将字符,输出到控制台的标准输出流上面去,而文件的输出流,则是将字符输出到文件中去。

  而一开始林萧之所以觉得别扭,其实是断词断错了地方的缘故。

  就拿文件输入流FileInputStream来说吧,林萧在一开始,很自然的便将这个类,按照中文的翻译进行了断词,分成了“文件输入”和“流”两个单词。这么分下来,文件输入流传递给林萧的字面意思,就变成了用于给文件输入内容的流,也就是写文件的意思,与文件输入流的实际作用恰恰相反。

  但其实人家正确的断词方式,是应该分为“文件”和“输入流”两个词。这样一分下来,文件输入流的字面意思,就变成了一个文件作为输入的流,这就和文件输入流的作用一致了。

  实际上,这里如果按照类名的英文去看的话,则更容易理解。也就是说,FileInputStream应该看作是File和InputStream的结合,而不是FileInput和Stream的结合。

  不过叫法这个事,也只是让林萧一开始觉得别扭而已,真正让人抓狂的,是文件读取的那段代码。

  逐行读取一个文件的内容,最终都要用到一个叫做BufferedReader的类。而得到这个类的实例有两种不同的方式,一种是先new一个FileInputStream,然后再new一个InputStreamReader,最后再new一个BufferedReader。还有一种方式是,先new一个FileReader,然后再new一个BufferedReader。

  这么一大堆完全不知道干啥的类突然出现,林萧直接就被整懵逼了。

  FileInputStream、FileReader、InputStreamReader和BufferedReader到底有啥区别?

  为毛每次都要new出来好几个对象才能读取?而且这些对象还特么一个套一个的,搞基呢?

  诸如此类,各种得不到答案的疑问,开始在林萧脑海中盘旋,那种感觉,别提多痛苦了,就好像脑袋要爆炸了一般。

  虽然关于这些疑问,在度娘上也可以搜到一些解释,然而那并没有什么卵用。

  当初的林萧,水平差到以为只有得到了BufferedReader,才能逐行进行文件内容的读取,就这种初级的水准,怎么可能消化的了度娘上的那些解释?

  要知道,哪怕使用最原始的FileInputStream,也是完全可以逐行读取出文件内容的,只不过比较麻烦而已。不仅需要自己去对字节进行转码,而且要自己去解析转码后的内容,才能分析出每行的内容。

  但就是这么基础的用法,林萧都还丝毫不知,就更别提搞清楚FileInputStream、FileReader、InputStreamReader和BufferedReader这四个类的关系了。

  实际上,这四个类的关系,要解释起来倒算不上多么复杂,但却需要一定的基础,才能真正的看懂这四个类到底什么关系。

  首先,在解释这四个类的关系之前,要先解释一下InputStream和Reader这两个类,它们都是Java整个IO类库中,最顶级的抽象类。

  什么叫最顶级?

  这里的最顶级并不是指得传统意义上的,高端大气上档次的意思,而是指它们都处于继承链的最上层。

  而这两个最顶级的抽象类,则分别代表了两种流的读取方式,InputStream代表的是字节流的读取,而Reader代表的,则是字符流的读取。

  通俗的解释,就是InputStream读出来的都是字节,而Reader读出来的都是字符。至于FileInputStream、FileReader、InputStreamReader和BufferedReader这四个类,其实都是这两个类的子类而已。

  其中FileInputStream是InputStream的子类,而其余三个,则都是Reader的子类。但是,在Reader的这三个子类中,有一个子类是非常特殊的一个,那就是InputStreamReader。

  这个类为什么会这么特殊呢?它有什么作用呢?

  其实它的作用很简单,InputStreamReader这个类,就是一个连接InputStream和Reader的桥梁,它可以将一个InputStream,转化为Reader类型。

  那为什么需要这种转化呢?难道不可以直接new一个Reader去使用吗?

  答案当然是肯定的,在有些时候,你完全可以直接new一个Reader直接去使用,根本不需要先new一个InputStream,然后再使用InputStreamReader去做转化。

  但是,这种直接new一个Reader去使用的方式,并不是万能的,在一些特定的情况下,这种方式是不适用的。而很不巧,文件的读取和写入操作,就恰巧属于这种特定的情况。

  这其实是因为,对于文件IO的底层操作,只能通过native方法来进行,而这些方法,则都是封装在了FileInputStream和FileOutputStream这两个字节流的类当中。

  因此,在读取一个文件的时候,只能先new一个FileInputStream,然后才能通过InputStreamReader,来产生一个Reader。

  虽然FileReader这个类,看似是可以直接通过文件或者文件路径,就new出来了一个Reader,但它本质上,依然是通过InputStreamReader对FileInputStream进行了一次转换,才产生了一个Reader。

  因为逐行读取文件内容这个需求,最终希望得到的是字符而不是字节,因此在得到Reader对象以后,整个事情就好办多了,只需要用Reader对象再new出来一个BufferedReader,就可以非常方便的逐行读取文件内容了。

  至于为什么有了Reader对象以后,还要再new出来一个BufferedReader,这就是设计模式思想的体现了。而在这里所体现的设计模式,则是被称为装饰器模式。

  简单的说,装饰器模式就是为了可以方便的,对某些类进行装饰,从而增强它的功能。而在逐行读取文件内容这个具体的例子当中,就使用到了BufferedReader,来对Reader的功能进行增强。

  这些增强的功能,主要就包括缓存的功能,以及一些更加方便读取的方法。就比如逐行读取时所用到的readLine方法,这个方法在Reader对象当中就是没有的,需要对Reader增强以后,获取到BufferedReader对象才有。

  其实分析到这里,上面四个类的关系,可以说已经基本搞清楚了。但从这个分析的过程也可以看出来,如果你对Java的IO包没有一个全貌的了解,或者对设计模式没有做到深入理解的话,是不可能彻底搞明白这些关系的。

  由此也可以看出,学习必须是递进式的,如果你在底子还不够的时候,就打算一口吃个胖子的话,是很容易碰一鼻子灰的。

  好在林萧一直对于自己学习的食量,把握的都还比较到位。该搞明白的,哪怕花再多时间,林萧也会弄清楚,但是不该搞明白的,或者说当前无法搞明白的,林萧也会很识趣的放弃。

  举一个不太恰当的例子,学习这件事,有时候特别像在皇上身边儿当太监,有些事你应该知道的,你一定要不择手段的探听到,但是你不该知道的,那就千万不要碰那条线,否则就很可能会掉脑袋。

  上位者身边的这条线,通常会被人们简称为高压线,但是大部分人都忽略了,其实学习也是有高压线的。

  只是这条线到底在哪,就需要自己去衡量和把握了。

  ......

  对于林萧来说,Java基础的学习,虽然也给他造成了一定的困扰,但总的来说,还是非常顺利的。

  文件IO的难关过去以后,林萧看了下接下来要学习的目录,终于忍不住长舒一口气,声音中带着一丝兴奋的自言自语道:“终于要学完Java基础了......”

版权声明:程序员胖胖胖虎阿 发表于 2022年9月9日 上午1:56。
转载请注明:一个屌丝程序猿的人生(三十七) | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...