Java I/O在Android中应用(一)

Java I/O在Android中应用(一)


前言(废话)

本来想周末拉一拉进度的,结果跑完10KM马拉松之后,发现自己已经完全没有力气再去做任何事情了。

讲一些日常的事情吧,最近家里人说要给我介绍对象认识,还和我说很漂亮,过年的时候见面。然后我就开始各种幻想,对方到底是什么样的大家闺秀呢?说实话,为什么我这样平时完全不运动的人能跑完10KM呢?因为我妈妈和我说,要不要考虑一下邻家的XXX,此处可以脑补一下周星驰电影里的如花。所以你完全可以想象,我在内心里不断呻吟,不断呐喊。我告诉自己,为了我下半生(身)的幸福,我不能半途而废,我不能倒下,当我倒下时,一切都结束了。所以下半程的时候,我的速度没有下降,甚至下半程的速度要高于上半程的速度,一切都是为了那么一个美好的愿景。

而在今天,我一个很要好的朋友和我说了一件事,将我的世界再一次倒转。他说他的一个朋友,最近结婚了,而且结婚的对象十分漂亮,并且他的这个朋友很老实,和这个对象也是通过相亲认识的。

他说,这个朋友是个老实人,是个老实人。还和我说现在一些漂亮的女孩嫁不出去只能通过相亲的方式来找对象,但是你想想,要是女孩子没有问题,为什么找不到对象呢?哦,不对,这是另外一个朋友和我说的。他的原话是,相亲时遇到漂亮的女孩,转手很多次的概率会很高。

换一句话说,如果换做是我,我作为男方,他们所谓的转手过很多次的女孩,如果我遇到了,并且我有权力选择,我到底会怎么选择呢?我不知道。我真的不知道。我本来认为我在很多情况下是绝对理性的,但是对于这个问题,我却不知道从何回答。


装饰者模式

要说到I/O,那么首先必定先说说装饰者模式,至于为什么,后续我会解释。

对于装饰模式,我感觉日常生活中它被提到的次数,远远高于实际上使用或者接触到的次数。之前我也仅仅是对其一知半解,以为自己对其已经掌握了,在别人面前夸夸其谈,自以为是。所以,为了自己和寥寥无几的观众,今天就来好好讲讲吧。

装饰者模式,首先何为装饰?大多数情况下是女性待在身上用来提升自己魅力的物件。那么何为装饰者,装饰者就是提供装饰方式的人。

有句话是这么说的,大多数情况下,能用聚合就不要用继承。首先,Java是基于单继承模型的语言,因此聚合在灵活性的角度上就已经优于继承。其次,我们常常说到,编程的时候应该要低耦合。那么问题来了,什么是低耦合,我换一个问题,同样是希望使用一个类中的一个方法,以下哪种方式的耦合度最低?

  1. 继承该类
  2. 实例化该类作为成员变量
  3. 定义一个方法,将该类作为参数,在方法体内调用目标方法
  4. 定义一个方法,在方法体内实例化该类并调用目标方法
  5. 将方式3定义在一个接口内,实现该接口
  6. 运行时注解

再卖一个关子,为什么我们要追求低耦合呢?耦合是什么?耦合是类之间的相互引用,无论是通过继承,聚合还是方法。这的确为我们带来的了遍历,但是程序在面对不断变化的需求时,是需要不断进行完善和改变的。而当我们面对这种变化时,根据我们对于项目的设计的不同,其实,实际的人力成本也不同。

举个例子,我们实现了一种以字节为单位通过流的方式来进行数据传输的IO操作类InputStream。我在类里面定义了读取数据相关的一系列方法:
Java I/O在Android中应用(一)

然后我发现,很多时候我都是一口气读取一串数据,然后对数据进行处理,然后再读取一串数据。那能不能我一口气读取多串数据缓存起来,并将这个过程封装起来呢?毕竟不爱偷懒的程序员不是一个好的程序员。所以就诞生了BufferedInputStream,该类读取数据的思想就是大多时候,我们都是希望尽数读取目标文件的,所以通过将数据单次读取的粒度变大的方式,我们可以减少对硬盘磁头的请求次数。我们可以看到,Java官方文档里,BufferedInputStream内有一个成员变量buf,它起到的作用就是对大粒度数据的缓存容器。
Java I/O在Android中应用(一)

但是BufferedInputStream用多了,发现很多时候我们其实并不是通过字节byte的方式来传输数据的,Java是面向对象的语言,实际上是Java程序员对于基本类型更加熟悉。我们每次传输数据的时候还需要将数据转换成byte数据再通过BufferedInputStream传输,实在是太麻烦了。那怎么办啊?再封装一层呗,也就是DataInputStream。
Java I/O在Android中应用(一)

在DataInputStream里,我们定义并封装了将字节数据转换成基本类型的方法,就比如说readBoolean()方法,我们读取一个字节的数据,当数据为非0时,判定结果为true,否则为false。
Java I/O在Android中应用(一)

好了,这样一来对于InputStream,BufferedInputStream以及DataInputStream三者的作用我已经理清楚了。但是怎么实现呢?难道是通过如下的继承关系吗?看起来的确可以对吧?
Java I/O在Android中应用(一)

试着思考如下情况,就拿上图中的DataInputStream来说,现在的继承关系中他是带有缓存的性质的,那么如果我有需求不想要带缓存的DataInputStream,该怎么办?如下,再创建一个不带缓存的DataInputStream继承自InputStream。发现了吗?我们迫切地需要一种设计模式来避免因为这种情况而可能出现的类爆炸问题!
Java I/O在Android中应用(一)

所以才需要装饰者模式。装饰者模式的思路就是将这种拓展性关系进行封装,从而将类之间的关系从继承弱化成(构造)方法引用。然后这里FIlterInputStream的作用与其说是抽象父类,更像是一个接口。
Java I/O在Android中应用(一)

如图,FilterInputStream就是作为这样一个提供了装饰方法的装饰者而存在。任何希望在已有的类上新增上新的性质的类只需要继承自FilterInputStream然后在实例化的时候将希望装饰的目标对象作为构造参数就行了。
Java I/O在Android中应用(一)


InputStreamReader和InputStream

抓住今天时间的小尾巴,最后的时间就聊聊这两个类吧。为什么在已经存在InputStream的前提下还会出现InputStreamReader呢?那还是需要从编码开始聊起,我们尽量长话短说,我很累了= =,想看详细点的,请移步我所理解的UTF-8和GBK_Ciruy B.Heimerdinger的博客-****博客

计算机是洋鬼子发明的,最初他们浅薄的见识也不会意识到这玩意能普及到全世界,所以仅设计了他们常用的128个字符,也就是我们常说的ASCII码。但是到后来,计算机普及到世界了,就面对了一个难题,我们总不能要求全世界都使用英语吧。毕竟和中文相比,英语什么的,怎么能表达完全人类的丰富的情感和意境呢?所以就诞生了各种编码,既然有各种编码,那我们通过InputStream传递的字节流就没有意义了,因为,在不同的环境下,解码出来的结果是不一样的!因此我们将InputStream和编码规则结合起来,摇身一变,诞生了InputStreamReader。

从文档里我们可以看到,构造方法中指定了编码方式。
Java I/O在Android中应用(一)