JAVA IO的那些事

话题一:编码/转码网络

咱们知道,在网络传输过程当中,说到底,是要传输字节流的,字符流(Writer/Reader)不过是在字节流(InputStream/OutputStream)基础上作了一下封装而已,是JAVA在语法层面上给咱们作的一个东西。ide


下面咱们来先看看一段代码:编码

wKioL1YrPTqCuY0dAAHxAGvG_sU621.jpg


运行结果:spa

get info : 涓栫晫锛屼綘濂?设计

get info : 世界,你??代理



分析:对象


首先来讲,S1是乱码,这个是好理解的,但是为何S2也是乱码呢?blog


第一,S经过UTF-8编码造成字节数据B接口


第二,接收方拿到B,经过GBK来进行转码,获得S1,很显然会乱码。ip


更加剧要的一点是:极可能B在GBK字符集中根本没有任何映射,那么此时GBK会指定一些特殊符号代替,好比?


第三,利用S1想还原原先的字节流的想法可能失败!


由于在上面的第二中,原来有些字节数据经过GBK的转码,可能被弄成?,而?在GBK中的字节数据和原先的字节数据就极可能不同了!因而S2就这样被乱码了。



这说明,在编码的转换过程当中,是极可能转不回来的!



话题二:网络交换数据的IO过程

wKioL1Z-erijYrS_AAA2tUp6-xI359.png

从上面的图能够看出,JAVA自己不参与数据在网络上的传输,JAVA仅仅作的是,若是要发送数据,那么把字节流交给KERNEL;若是须要读取数据,那么从KERNEL中读取字节流。


发送与接收,若是采起一致的编码,那么就不会乱码!


在网络中传输的,应该是一种协议,不过是用字节流表达而已,好比,字节流的编码,大小,字节内容等。


话题三:JAVA BIO

Java BIO,即传统的IO,阻塞的IO,也是最经常使用,最基本的IO了,下面就来剖析下它!


  • Input/Output

Input:输入,把数据从磁盘、键盘、网络,内存中输入到程序中;

Output:输出,把程序中的数据输出到磁盘、显示器、网络、内存中;

能够说,没有IO,就没有结果,就没有价值!


  • 字节流 VS 字符流

字节流的源头是:InputStream/OutputStream

字符流的源头是:Reader/Writer


首先来讲,咱们必定得忘记字符流这回事,而是从字节流的角度进行理解IO。


之因此,存在字符流,是由于毕竟字节不是那么可视化,所以提供了把字节流转化成字符流的方式。


咱们能够关注下Reader/Writer的子类:

wKiom1Z_RsmiB2eiAAATKDisQrQ181.png


wKioL1Z_R0Cw-8_GAAASUbJR9Z0058.png


能够清楚的看到,在InputStreamReader/OutputStreamWriter提供的构造方法中,接受一个字节流以及编码。其实,这就是字节流转化成字符流的桥梁。咱们须要注意的是,在这个转化过程当中,须要特别注意编码问题,最好就是手动的、明确的指定编码,而不是走默认的环境编码什么的。


  • DataInputStream/DataOutputStream

这种数据流有个特色,就是提供了各类readXXX/writeXXX方法,以下所示:

wKiom1Z_SsDgL4XJAAA4eM9PR64233.png

试想下,若是咱们想经过网络,给对方发送个int,咱们难道得本身将int转成4个字节来进行发送?这也太麻烦了吧,而DataInputStream已经提供好了众多数据格式的操做了。在Socket通讯时,当咱们须要封装Socket的IO操做时,能够考虑用这个数据流。


  • ByteArrayInputStream/ByteArrayOutputStream

内存字节流,这种流,会将内存中的字节包装造成流,延伸来看,String也能够先获取byte[],而后在转化成这种内存字节流。

wKiom1Z_TZ2ipBPcAAAG7R8NSHg480.png


  • ObjectInputStream/ObjectOutputStream

对象字节流,类比的思想,就是将对象包装造成字节流,而后,能够写入磁盘(持久化),能够经过网络发送出去(对方接受后,将字节流反序列化获得对象)。


  • 压缩流

ZipInputStream/GZinputStream/JarInputStream...

这些压缩流,能够套在文件字节流上,也能够套在网络字节流上(节省网络通讯量)。


  • BufferedInputStream/BufferedOutputStream

若是咱们想要Buffer功能,能够套一层Buffer流,Buffer每每会快点,这是为何呢?


下面咱们简单来看看BufferedOutputStream的实现:

wKioL1Z_UaTBpxWVAAAEfYc6N48524.png

wKioL1Z_UcOh8gWUAAAKdQDFdAc565.png

wKioL1Z_USKRssWaAAAWEDZ_MJQ518.png

wKioL1Z_UUyypRrQAAAR3By3SyA028.png

能够看到在BufferedOutputStream内部维护了一个byte[],默认构造的状况下,大小是8192。

若是写,没有超出byte[]大小,那么写入byte[],这个过程固然至关快;若是超出了byte[]大小,那么flushBuffer,而flushBuffer才是真正写入的地方,并且是一次性的写入byte[]。

实际上,利用Buffer功能,将随机写变成了顺序写,固然快了!


  • PrintStream

打印字节流,咱们常用到的System.out,System.err就是PrintStream对象,提供了众多数据格式的print方法。在重定向中,利用System.setOut/setErr,其实都是set到一个PrintStream上。


话题四:IO中的装饰思想

IO流涉及的类那么多,每种流都有本身的特色,若是但愿多种功能/特色都汇集在一块儿,那么就要进行包装和装饰功能了。咱们能够以最简单的BUFFER功能,来分析下这个装饰思想。

wKioL1Z_f6yBMgFSAAAbLj3oWMg281.png


经过阅读FilterOutputStream类的源码,发现FilterOutputStream类持有OutputStream类的引用,它的每一个方法,都没有干啥,就是对持有的OutputStream的引用进行调用对应方法罢了。


BufferedOutputStream extends FilterOutputStream,它内部经过本身的byte[],实现BUFFER功能。


到这里,咱们好像发现点端倪了,开始有了些猜想:

那些有特色的IO类,是否是经过extends FilterXXXStream,而后在本身内部“耍些手段”来实现特定功能的呢?


wKioL1Z_go3iS-kwAABlEmJUvlg281.png


经过上图,能够发现,的确有些有特色的IO类extends FilterXXXStream,好比DataXXXStream/BufferedXXXStream/PrintStream等。


FilterXXXStream就好像一层代理,虽然它什么都没有作,仅仅是经过引用去调用对应方法而已;这样看似无用,其实否则,好比想实现buffer功能,只须要extends FilterXXXStream,而后仅仅重写read/write方法便可,这样不少代码不须要再写,由于从FilterXXXStream上已经得到。Java这样的设计,巧妙!


话题五:flush() / close() 

流用完了,要关闭流时,咱们常常想到要flush,要close,下面咱们来分析分析它们!


wKiom1Z_h2zxiv0cAAAJbD4eLlk375.png

2个标示接口,Closeable须要close,Flushable须要flush。


在OutputStream中,实际上它们是空的方法:

wKioL1Z_iLPwCRfyAAAIEyZFZSw969.png

wKiom1Z_iKbxJcA3AAAG_JwvX44604.png


很显然,这是但愿子类本身去flush,close。若是用到了Buffer机制,特别是经过修饰的功能拥有了BufferedXXXStream的特色,那么必定得注意flush/close,其实是将byte[]中未处理的数据处理掉。


注意到FileXXXStream并无去重写flush方法,若是extends FileXXXStream类,又没有提供flush的实现,那么将调用的是OutputStream中一个空的flush方法!好比SocketOutputStream extends FileOutputStream,却没有提供重写的flush方法,说明在SOCKET通讯中,并不须要咱们去调用flush方法,它是在内核层面上作的缓冲,而不是JAVA层面的。


所以,对一个IO类,要不要flush,要不要close,咱们得看看它extends who?有buffer机制吗?


话题六:Java BIO vs NIO vs AIO

关于Java BIO/NIO,在前面的博客中已经有所介绍,你们能够参考下面的:

http://zhangfengzhe.blog.51cto.com/8855103/1715530

http://zhangfengzhe.blog.51cto.com/8855103/1726488

http://zhangfengzhe.blog.51cto.com/8855103/1712845


Java BIO:小张同窗有一份快递,因而他去物流中转站去拿货,他在那里等着,一直等到快递来,这段期间,他哪里也不去。


Java NIO:小张同窗,天天去一趟物流中转站,检测下是否是快递的货到了,若是到了,就带回来,若是没有则返回,明天继续。


Java AIO:小张同窗并不去物流中转站,若是货到了,那么快递员“送货上门”。


上面的例子,形象的说明了BIO/NIO/AIO的特色和区别,胖哥说的好,存在就是有价值的,AIO是最新出来的东西,想法是好的,可是不必定是适合的。好比JDBC程序中,咱们发了一个SQL,可是这是个AIO,那么麻烦了,发出这个SQL后,程序马上向下执行,一段时间后SQL结果送来了,这个时候,咱们要拿出保存有发出SQL那个时刻的一些信息。想想,若是这样的AIO不少的话,程序会复杂不少,保存不少信息,回调不少方法。


这也再次说明了一个道理,若是它简单,那么它内在确定很复杂,真是由于它内在的复杂,致使了外在的简单!


到这里,JAVA IO那些事就结束了,你学到了吗?

相关文章
相关标签/搜索