JavaIO学习笔记

前提

此读书笔记是基于OReilly的《Java I/O》第二版,可惜这本书相对比较古老,2006年出版的书,但是Java的IO包基本没有太大改动,所以这本书还是值得一读的,加深对IO和IO相关的API使用的理解。这本书没有翻译,只能硬啃英文版,不过内容相对比较简单。下面主要抽取几个重要的章节做基于翻译和总结的笔记。本文稍长,换个舒服的姿势阅读。

BIO

BIO在此书称为Basic I/O(基础IO),有时候我们更多地叫做Blocking I/O(阻塞IO),或者叫Old I/O(OIO),三个叫法其实是指同一个东西。

第一章 IO简介

输入和输出,简称I/O,是任何计算机操作系统或编程语言的基础。Java包含一个丰富的I/O类集合,大部分集中在java.io和java.nio两个包中。这两个包分别支持不同类型的IO。主要区别是老式的基于流的I/O和新型的基于通道和缓冲的I/O。Java的I/O库以一种抽象的方式设计,使你能够从外部数据源中读取数据
然后写到外部的目标,不管你写什么或读什么。你使用写入文件的方法与写入字节数组或串行端口设备的方法相同,也就是你不需要关系数据来自哪里。除此之外,它还允许你定义特定的I/O流,这些流可以自动压缩、加密、并从一种数据格式转换到另一种格式。一旦有了这些工具,程序就可以无感知地发送加密的数据或进行压缩文件写入,加密或者压缩可以被隔离。

什么是流

流就是一个不定长度、有序的字节序列

流的来源:

  • 1、最常见的流的输入源就是System.in
  • 2、文件(磁盘文件)是另一种常见的输入源和输出目标源。
  • 3、网络连接也能提供流。
  • 4、Java程序内部也能产生流,主要是通过字节数组或者管道(Pipe)。

流相关的类:
绝大多数操作流的相关类在java.io包中,两个主要的抽象类是java.io.InputStreamjava.io.OutputStream,它们的子类包括:

  • BufferedInputStream
  • BufferedOutputStream
  • ByteArrayInputStream
  • ByteArrayOutputStream
  • DataInputStream
  • DataOutputStream
  • FileInputStream
  • FileOutputStream
  • FilterInputStream
  • FilterOutputStream
  • ObjectInputStream
  • ObjectOutputStream
  • PipedInputStream
  • PipedOutputStream
  • PrintStream
  • PushbackInputStream
  • SequenceInputStream

java.util.zip包主要包括四个用于读取压缩格式数据并且返回非压缩格式数据的输入流相关类和四个用于输入压缩格式数据并且写入(输出)压缩格式数据的输出流:

  • CheckedInputStream
  • CheckedOutputStream
  • DeflaterOutputStream
  • InflaterInputStream
  • GZIPInputStream
  • GZIPOutputStream
  • ZipInputStream
  • ZipOutputStream

java.util.jar包提供两个流相关的类用于操作JAR归档:

  • JarInputStream
  • JarOutputStream

java.security包提供了一对流相关的类用于计算消息摘要:

  • DigestInputStream
  • DigestOutputStream

JCE(Java Cryptography Extension)提供两个类用于加密和解密:

  • CipherInputStream
  • CipherOutputStream

最后,还有一些隐藏在sun包下的流相关的类,如sun.net.TelnetInputStream和sun.net.TelnetOutputStream等等,它们也是java.io.InputStream或者java.io.OutputStream的子类。

数字类型数据

输入流读取字节,输出流写入字节。Reader读取字符,Writer写入字符。为了理解输入和输出,首先需要理解Java是如何处理字节(byte)、整数(integer)、字符(character)和其他基础数据类型的。

整型(Integer)数据:
(书中这里提到的Integer并不是Java中的int的包装类型Integer,而是指整数类型,包括int、byte、short、long等)。

Java中int类型是用4字节(4byte=32bit)、大端字节序(big-endian)表示的。它能够表示的范围是[-2147483648,2147483647],其实也就是-2^31到2^31-1。

Java中long类型是8字节(8byte=64bit)、大端字节序(big-endian)表示的。它能够表示的范围是[-9223372036854775,9223372036854775807]。数字的末尾要用小写l或者大写L标记,例如10086L。

Java中short类型是2字节(2byte=16bit)、大端字节序(big-endian)表示的。它能够表示的范围是[-32768,32767]。

byte在Java中使用广泛,因为它就是使用在I/O中。byte类型是1字节(1byte=8bit),它能够表示的范围是[-128,127]。Java中的byte类型是有符号的,最大值为127。

Java中是不存在short或者byte类型的字面量(这里特指编译之后)。例如当你写了表达式byte b = 42;short s = 24000;,编译后变量b和s都被编译器读取为int类型。

java.io.InputStream的read方法中:

public abstract int read( ) throws IOException

读取到的整型的取值范围是[0,255],类似于无符号的byte(实际上不存在无符号的byte),而不是byte的表示范围[-128,127]。当读取到的值为-1的时候,说明已经到达流的尾部。

java.io.OutputStream的write方法中:

public abstract void write(int b) throws IOException

这里允许写入的int类型的有效取值范围是[0,255]。当然,我们无法阻止一些粗心大意的程序员写入了超出这个范围的int值,如果出现这种非法情况,int输入值的高位24bit会被舍弃,只写入低位的8bit,相当于做了下面的操作:

b = b & 0x000000FF;

可以使用下面这个公式来转换[-128,127]的值为流写入的有效的整数值:

int unsignedByte = signedByte >= 0 ? signedByte : 256 + signedByte;

可以使用下面这个公式转换超过256的int类型的值为byte类型的值:

int temp = intValue % 256;
if ( intValue < 0) {
   byteValue = temp < -128 ? 256 + temp : temp;
}else {
   byteValue = temp > 127 ? temp - 256 : temp;
}

当然,java.io.InputStream提供通过字节数组读取数据的方法:

public int read(byte[] data) throws IOException
public int read(byte[] data, int offset, int length) throws IOException

字符类型数据

因为计算机只真正理解数字,字符是通过给给定脚本中的每个字符分配一个数字来编码的。也就是说,一个字符最终可能会映射为多个字节。先记住这两点,关于字符和字节的内容不参考本书做展开,后面有专门的文章来讲解一下字符和乱码等内容。

Reader和Writer

流不是用来专门读取和写入文本的,因此才出现了Reader和Writer。输入和输出流基本上是基于字节的。Reader和Writer都是以字符为基础的,面向的字符集可以有不同的宽度。例如,ASCII和Latin-1使用1字节的字符。UTF-32使用4字节的字符。UTF-8使用不同宽度的字符(在1到4个字节之间)。字符最终由字节组成,把这些按照指定的编码格式将字节传递到字符中,然后使用Reader从中读取。同样,Writer把在将它们(字符)写入一些底层流之前,根据指定的编码将字符转换成字节。[从本书的角度来看,并没有字符流的说法,目前也不知道字符流这个词语到底是怎么产生的。]

java.io.Reader和java.io.Writer以及它们的子类都是用于读取和写入字符数据的,这些子类用于转换不同来源或者不同目标的字符集。相关的API都在java.io包中:

  • BufferedReader
  • BufferedWriter
  • CharArrayReader
  • CharArrayWriter
  • FileReader
  • FileWriter
  • FilterReader
  • FilterWriter
  • InputStreamReader
  • LineNumberReader
  • OutputStreamWriter
  • PipedReader
  • PipedWriter
  • PrintWriter
  • PushbackReader
  • StringReader
  • StringWriter

java.io.Writer这个抽象父类主要提供下面的方法:

public void write(int i) throws IOException
public void write(char[] data) throws IOException
public abstract void write(char[] data, int offset, int length) throws IOException
public void write(String s) throws IOException
public void write(String s, int offset, int length) throws IOException

其中,write(int i)方法中,int的取值的上限是65536,这是因为char和byte的区别。

Buffer和Channel

这一节跳过,因为更详细的描述可以阅读《Java NIO》。

无处不在的IOException

在人为控制之外,有些时候会发生一些物理的故障。例如磁盘在读取文件时可能会损坏扇区,又例如连接广域网的电缆有可能被工人铲断等等。基于问题的多样性,几乎所有的输入和输出相关的方法都会抛出IOException。IOException是一个检查型的异常,也就是必须直接抛出该IOException或者把方法放在一个try/catch代码块里面执行(并且处理异常)。

控制台标准输入、输出和异常重定向

IO的安全管理

第二章 输出流

第三章 输入流

数据源(Data Sources)

第四章 文件流

第五章 网络流

装饰流

实际上这这一章叫"Filter Streams",翻译来说应该是"过滤流",但是实际上FilterInputStream和FilterOutputStream使用了装饰器模式,因此叫它们为装饰流。

第六章 装饰流简介

第七章 打印流

第八章 数据流(Data Streams)

第九章 内存中的流

第十章 压缩流

第十一章 JAR存档

第十二章 加密流

第十三章 对象序列化

NIO

NIO一般叫做New I/O或者Nonblocking I/O,两者指的都是同一个东西,就是非阻塞IO。这一个大章节的内容直接跳过,因为更详细的描述可以阅读《Java NIO》。

第十四章 缓冲区(Buffer)

第十五章 管道(Channel)

第十六章 非阻塞IO

文件系统

文本

小结

此书是理解Java IO一些基础概念和API使用的经典书籍,可以为IO编程和网络编程打下基础。至于编码和文件这两个比较大头的概念后面会单独各自写一篇东西详细分析一下。读完此书之后,接下去的书单顺序是:

  • 1、《Java NIO》,同一个系列的书,封面也是一只兔子。
  • 2、《Java网络编程》(第四版),这本书加深一下IO、NIO和网络编程相关的API使用。
  • 3、《Unix网络编程》,这本书主要是深化一下网络编程的相关基础知识,例如十分重要的IO线程模型、套接字(Socket)的底层原理等。
  • 4、《TCP/IP详解-协议》,TCP/IP的经典书,不过看了一下,比较枯燥,晚点试下啃下来。

(未完待续)

猜你喜欢

转载自www.cnblogs.com/throwable/p/9188400.html