IO即数据读写。数据是应用的中心要素,而数据读写的能力和可扩展性是编程平台的基础支撑。

概念框架

  • 方式: 字节流 Byte 和 字符流 Char

  • 方向: 输入 Input 和 输出 Output ; 读 Reader 和 写 Writer

  • 源: 字符串 String, 数组 Array, 对象 Object, 文件 File, 通道 Channel, 管道 Pipe, 过滤器 Filter,控制台 Console, 网络 Network Link ; 一切可产生/接收数据的 Data

  • 性能: 带缓冲和不带缓冲的

  • 行为: Readable, Appendable, Closable, Flushable, Serializable/Externalizable

概念组合

  • 方式、方向、源、行为四个维度可以组合出各种特性的IO流。JavaIO框架采用装饰器模式,可以在现有IO流的基础上添加新的IO特性,生成更强功能的IO流,体现了读写能力的可扩展性。

  • 比如要构造一个带缓冲的文本文件读取流: File -> FileInputStream -> InputStreamReader -> FileReader -> BufferedFileReader . 【文本:字符流, 文件:源, 读:输入方向, 带缓冲的:性能】

  • 读写耗时的字符输入输出流可以使用缓冲来提升性能,比如文件读写、网络流读写等。这是由于每个待读写的字符都要经过【读写字节、字节/字符转换】两个操作。缓冲实际上是将大量单个的读写/转换或多个小粒度的读写/转换操作,转换为一个大的单个的批量读写/转换操作。

字节流

  • 字节输入流:InputStream, BufferedInputStream, FileInputStream, ByteArrayInputStream, PipedInputStream, FilterInputStream, DataInputStream, ObjectInputStream;

  • 字节输出流: OuputStream, BufferedOuputStream, FileOuputStream, ByteArrayOuputStream, PipedOuputStream, FilterOuputStream, DataOuputStream, ObjectOuputStream, PrintStream;

字符流

  • 字符输入流: Reader, BufferedReader, FileReader, CharArrayReader, PipedReader, FilterReader, StringReader, LineNumberReader;

  • 字符输出流: Writer, BufferedWriter, FileWriter, CharArrayWriter, PipedWriter, FilterWriter, StringWriter, PrintWriter;

字节流与字符流之间的转化

  • InputStreamReader:输入字节流转化为输入字符流

  • OutStreamWriter: 输出字符流转化为输出字节流

其它

  • Bits: 原子类型 boolean, short, int, long, float, double 到其字节数组表示之间的转化(大端字节序);将原子类型转化为字节数组使用 >>>8 运算符,高位字节在数组低索引; 将字节数组转化为原子类型则采用 [&0xFF, <<8, +] 运算符 。

  • 装饰器模式:多个类实现同一个接口,并且这些实现类均持有一个该接口的引用,通过构造器传入,从而可以在这些实现类的基础上任意动态地组合叠加,构造出所需特性的实现来。装饰器模式可以实现数学公式/逻辑公式运算函数。

实现

  • 在IO流的实现类中,通常有一个 【Char[],Byte[], CharBuffer, StringBuffer, ByteBuffer】的成员数组或成员对象。将成员数组/对象里的数据写到方法里给定的参数数组或返回值,即为读实现;将方法里给定的参数数组写到成员数组或成员对象里,即为写实现。读写数据本质上就是数据在不同源之间的拷贝实现。
  • IO流通常是会实现读写单个字节/字符、读写字节数组/字符数组、跳过若干字节/字符的功能。成员 【next,pos,mark,count】 用于标识数据读写位置;mark 与 reset 方法组合可实现重复读写。
  • 要实现一个自定义的 Reader, 可参考 StringReader 实现;StringReader 可以将字符串作为字符流来读取,内部成员为String;而 StringWriter 内部成员为线程安全的 StringBuffer。两者均要考虑并发读写的问题,使用一个 Object 锁进行互斥访问。
  • FileReader 是文本文件读取流,通过继承 InputStreamReader, 转化 FileInputStream 而得。因文件是对磁盘字节数据的抽象,因此要获得人类可读的文本,必然存在从字节流到字符流的转换。
  • InputStreamReader 使用 StreamDecoder 将字节流转换为指定字符编码的字符流,后续读操作直接委托 StreamDecoder 读取;StreamDecoder 是一个字节解码器, 是 Reader 实现类。OutputStreamWriter 使用 StreamEncoder 将字符流转换为指定字符编码的字节流,后续写操作直接委托 StreamEncoder 写入底层 OutputStream;StreamEncoder 是一个 Writer 实现类。
  • FilterReader, FilterWriter, FilterInputStream, FilterOutputStream: IO流的抽象类,分别持有一个【Reader,Writer,InputStream, OutputStream】, 自定义 IO 流可以通过继承 FilterXXX 来实现。CommonIO库里 ProxyReader, ProxyWriter 分别提供了 Reader, Writer 的一个示例实现, java.io.DataInputStream 提供了 FilterInputStream 的一个示例实现,java.io.PrintStream 提供了 FilterOutputStream 的一个示例实现。
  • BufferedReader: 提供了缓冲读及读行的功能。BufferedReader 主要成员为 【Reader in, char[defaultsize=8192] cb, int nextChar, int nChars, int markedChar】; nextChar 指示将要读取的字符位置, nChars 指示可以读取字符的终止位置, markedChar 表示被标记的将要读取字符的位置; BufferedReader 从字符流 in 中读取字符预存入缓冲字符数组 cb 中,然后在需要的时候从 cb 中读取字符。BufferedReader 的实现由 fill 和 read, readLine 共同完成。当缓冲字符数组的字符数不足于填充要读取的参数数组时,就会使用 fill 方法从输入流 in 中读取数据再进行填充;使用 System.arraycopy 方法

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 拷贝字符。

  • PipedReader: 管道输入流,必须从 PipedWriter 中读取。底层实现有两个线程、两个位置索引以及一个字符数组。两个线程分别是读线程 readSide 和 写线程 writeSide, 两个位置索引分别用于指示字符数组中将要写的位置和将要读的位置。字符数组是一个循环队列,默认大小为 1024 chars 。初始化时必须将 PipedReader 的输入连接至 PipedWriter 。读实现方法中,即是将底层字符数组拷贝到方法中的参数数组。PipedWriter 的实现相对简单,其输出连接至 PipedReader; PipedWriter 的写就是 PipedReader 的读,因此 PipedWriter 的方法实现委托给 PipedReader 引用的方法。

文件

  • 文件相关的类: File, RandomAccessFile, FileDescriptor, FileChannel, FilePermission, FilePermissionCollection, FileFilter, FileSystem, UnixFileSystem ;

  • 文件 File: 唯一性路径标识【目录路径/文件名[.文件扩展名]】、文件元信息【描述符、类型、大小、创建时间、最近访问时间、节点信息】、文件访问权限【读/写/执行组合】等; File 的操作会先使用 SecurityManager 检查访问权限,然后通过 FileSystem 判断和操作。

  • 构造可以实际读写的文件输入输出流需要使用 FileInputStream, FileOutputStream, FileReader, FileWriter, BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream 来包装 File ,比如 new BufferedReader(new FileReader(new File(filename))) 。

  • FileChannel: 操作文件内容的通道;FileChannel 是线程安全的,使用 java.nio 引入的高性能机制来实现。

  • 文件描述符 FileDescriptor : 用于操作打开的文件、网络socket、字节流的唯一标识句柄;其成员是一个整型数 fd 和一个原子计数器 AtomicInteger useCount ; 标准输入流 System.in.fd = 0 , 标准输出流 System.out.fd = 1 , 标准错误流 System.err.fd = 2

  • 文件访问权限 FilePermission: 提供了【文件可进行的操作 actions 与位掩码表示 mask 】之间的转化方法。文件可进行的操作 actions 在类 SecurityConstants 的常量中定义; mask 是一个整型数,表示文件的执行(0x01)/写(0x02)/读(0x04)/删(0x08)/读链接(0x10)/无(0x00) 等权限组合。

  • FileSystem:所使用的操作系统平台的文件系统。指明该文件系统所使用的【文件分隔符、路径分隔符、路径标准化、路径解析、文件访问安全控制、文件元信息访问、文件操作等】。文件分割符是指路径名中分割目录的符号,linux = '/', windows = '\' ; 路径分割符是环境变量中分割多个路径的符号,linux = ':' , windows = ';' ; UnixFileSystem 提供了 FileSystem 的一个实现示例。

  • 枚举: 存在 BA_EXISTS = 0x01; 普通文件 BA_REGULAR = 0x02; 目录 BA_DIRECTORY = 0x04; 隐藏 BA_HIDDEN = 0x08; 可读 ACCESS_READ = 0x04; 可写 ACCESS_WRITE = 0x02; 可执行 ACCESS_EXECUTE = 0x01; 判断文件属性及访问权限采用与或非位运算实现, 优点是可以灵活组合。

序列化

  • 序列化是实现内存数据持久化的机制。可以将 Java 对象数据存储到文件中,或者将文件中的数据恢复成Java对象。Java RPC, Dubbo,ORM 等都依赖于序列化机制。

  • 序列化相关的类及方法: Serializable/Externalizable, ObjectInputStream, ObjectOutputStream, readObject, writeObject 。

  • 使用 ObjectInputStream.readObject, ObjectOutputStream.writeObject 方法进行对象序列化: JDK提供的标准对象序列化方式;对象必须实现 Serializable 接口;适用于任何 Java 对象图的序列化,通用的序列化方案;输出为可读性差的二进制文件格式,很难为人所读懂,无法进行编辑;对某些依赖于底层实现的组件,存在版本兼容和可移植性问题;只有基于 Java 的应用程序可以访问序列化数据;
  • 使用 XMLDecoder.readObject, XMLEncoder.writeObject 方法进行对象序列化: 对象是普通的 javaBean,无需实现 Serializable 接口;输出为可读性良好的XML文件,容易编辑和二次处理;其它平台亦可访问序列化数据;可以修改数据成员的内部结构的实现,只要保留原有bean属性及setter/getter签名,就不影响序列化和反序列化;可以解决版本兼容的问题,提供长期持久化的能力;每个需要持久化的数据成员都必须有一个 Java bean 属性。
  • 实现 Externalizable 接口来进行对象序列化。需在对象中实现 readObject, writeObject 方法,提供更高的序列化灵活性,由开发者自行决定实例化哪些字段、何种顺序、输出格式等细节。

NewIO

  • java.nio 为 javaIO 体系引入了更好的性能改善,主要是 Buffer, Channel 和 file 相关的支撑类。由于 java.io 类已经引用了 nio 里的类,因此直接使用 java.io 里的类就可以获得 nio 带来的好处了。

  • nio后续再完善。

代码阅读小记

  • 理解框架的关键在于理清其设计思路及概念结构(概念及其关联)。

  • 阅读 JDK 代码可以获得很多面向对象的软件框架设计的有益启示。

javaIO框架小析的更多相关文章

  1. gtest运行小析

    Gtest是google推出的C++测试框架,本篇文档,从整体上对Gtest的运行过程中的关键路径进行分析和梳理. 分析入口 新建一个最简单的测试工程,取名为source_analyse_proj,建 ...

  2. Poco logger 日志使用小析

    Poco logger 日志使用小析 Poco logger 日志使用小析 日志 logger 库选择 Pocologger 架构简析 步骤一 生成消息 步骤二 写入logger 步骤三 导入chan ...

  3. PHP单一文件入口框架简析

    <?php /** * PHP单一文件框架设计简析 * 1.MVC架构实现 * 2.URL路由原理 */ //URL路由原理 /** * 路由作用 * 获取url中的c和a变量,执行c类对应的方 ...

  4. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  5. js回调函数,字符串,数组小析

    (一)回调函数:是指通过函数参数传递到其他代码的,某一块可执行代码的引用.这一设计允许了底层代码调用在高层定义的子程序.在抖动函数中,回调函数用于在实现一些功能之后采取的另外的措施,比如div,照片抖 ...

  6. ArcGIS Earth数据小析

    ArcGIS Earth,一款轻量级的三维地球应用.因为工作关系下载试用了半天,正好借这个机会简单研究一下ArcGIS Earth的大概思路,特别是地形数据的组成和影像数据的加载,在这总结整理一下.下 ...

  7. Android Hook Dexposed原理小析

    dexposed是阿里巴巴在xposed框架上面开发的hotpatch一套框架 当然hotpatch的方式有很多,这里先介绍下dexposed原理 Demo中有个test函数, 在调用hook之前正常 ...

  8. [Node.js] ECMAScript 6中的生成器及koa小析

    原文地址:http://www.moye.me/2014/11/10/ecmascript-6-generator/ 引子 老听人说 koa大法好,这两天我也赶了把时髦:用 n 安上了node 0.1 ...

  9. android动画小析

    这里所讲的动画,是android framework提供的动画框架里面的动画. 是view层级的动画.不涉及到底层opengl es相关的动画实现. 动画: 主要包括 Interpolation du ...

随机推荐

  1. JavaScript_Math函数

    JavaScript_Math函数与属性按功能分类 Math三角函数与属性 Math.sin() -- 返回数字的正弦值 Math.cos() -- 返回数字的余弦值 Math.tan() -- 返回 ...

  2. mac终端命令大全全全全全全全全全

    OSX 的文件系统 OSX 采用的Unix文件系统,所有文件都挂在跟目录 / 下面,所以不在要有Windows 下的盘符概念. 你在桌面上看到的硬盘都挂在 /Volumes 下. 比如接上个叫做 US ...

  3. sbt %%

    在依赖库选项中会看到其中有的是 %%,而有的是一个%. 这表示 :“要求sbt寻找用当前你配置的scala版本编译出来的jar包.” 因为scala不同版本编译出来的结果会不兼容.

  4. Hibernate和Mybatis的对比

    http://blog.csdn.net/jiuqiyuliang/article/details/45378065 Hibernate与Mybatis对比 1. 简介 Hibernate:Hiber ...

  5. Cookie工具类

    import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet ...

  6. MyEclipse自定义快捷键

    MyEclipse快捷键设置 分类: JAVA2011-06-30 09:35 11255人阅读 评论(2) 收藏 举报 myeclipseeclipsetriggersmicrosoftjavabi ...

  7. sqlserver集合操作

    SQLServer2005通过intersect,union,except和三个关键字对应交.并.差三种集合运算 详细如下 use tempdb go if (object_id ('t1' ) is ...

  8. Oracle的自动统计信息不收集直方图的信息

    Oracle的自动统计信息不收集直方图的信息 在oracle9i中,默认的统计信息收集是不收集直方图信息的,也就是说默认的MOTHOD_OPT模式为FOR ALL COLUMNS SIZE 1 在10 ...

  9. mysql组合索引顺序参考

    问题背景 : 当我们需要创建一个组合索引, 索引的顺序对于效率影响很大, 怎么确定索引的顺序; 解决方法 : 我们应该依据字段的全局基数和选择性, 而不是字段的某个具体的值来确定; 表结构 :  dc ...

  10. java notify和notifyAll的区别

    首先从名字可以了解,notify是通知一个线程获取锁,notifyAll是通知所有相关的线程去竞争锁. notify不能保证获得锁的线程,真正需要锁,并且可能产生死锁. 举例1: 所有人(消费者线程) ...