第15 章 输入/输出

15 章 输入/输出

使用输入机制 允许程序记录运行时读取外部数据,(磁盘,关盘等存储介质),用户输入

使用输出允许程序记录运行状态,将程序数据输出到磁盘、关盘等介质

java io流使用了一种装饰设计模式,它将IO流分成底层字节流和上层处理流,其中节点流和底层物理存储节点直接关联,程序在将处理流包装成处理流,从而包装程序使用输入输出流来访问不同节点

15.1 File

15.1.1 访问文件和目录

File类可以使用文件路径字符串来创建File实例,该路径可是相对路径也可以是绝对路径

UNIX/Linux/BSD等系统上,如果路径是( / ) 则表明是绝对路径

 

15.2 理解javaIO

15.2.1 流的分类

1. 输入流和输出流(以内存来划分, 以服务器来划分)

输入流 :只能从中读取数据不能写入数据

输出流 :只能想其中写入数据,而不能读取数据

java的输入流主要由InputStreamReader作为基类

输出流主要是用OutputStreamWriter作为基类

 

2. 字节流和字符流

字符流主要有InputStreamOutputStream作为基类

字符流主要是Reader Writer 作为基类

 

3. 节点流和处理流

按照流的角色来分,可以分为节点流和处理流

可以从一个特定的IO设备(磁盘、网络)读/写数据的流,称为节点流(低级流)

处理流则用用对一个已经存在的流进行连接或者封装。通过封装后的流来实现数据的读写功能(高级流)

15.2.2 流的概念模型

java把所有的有序数据抽象成流模型,简化了输入输出处理,理解了流的概念模型也就了解两类javaIO

 

JAVA 的处理流模型则体现java输入/输出流的灵活,处理流的功能主要体现在以下两个方面

1.功能的提高:主要增加缓冲的方式来提高输入/输出的效率

2.操作的便捷:处理流可能提供了一系列的便捷方法来一次输入/输出大量流

通过处理流java无需理会输入输出的是磁盘还是网络

 

15.3 字节流和字符流

15.3.1 InputStream Reader

InputStream Reader是所有输入流的抽象基类,本身不能创建实例来执行输入,但他们分别有一个读取文件的输入流 FileInputStream FileReader

 

15.3.2 OutputStreamWrite

使用javaIO流执行输出是,不要忘记关闭输出流

1.关闭输出流可以保证流的物理资源被关闭

2.可能还可以将输出流缓存区中的数据flush到物理节点里

 

15.4 输入/输出流体系

15.4.1 处理流的用法

处理流可以隐藏底层设备撒上节点流的差距,并对外提供更加方便的输入/输出方法,让程序员只需关心高级流的操作

使用处理流是的典型思路是  使用处理流来包装节点流,程序通过处理流来执行输入输出的功能,让节点流与底层的I/0设备,文件交换

 

只要流的构造器参数不是一个物理节点而是一个已将存在的流,那么这种流就一定是处理流

 

PrintStream类的输出功能非常强大,通常如果需要输出文本内容,都应该讲输出流包装成PrintStream

使用处理流包装底层的节点流后,只要关闭最上层的处理流即可,系统会自动关闭节点流

 

15.4.2 输入/输出流体系

管道流主要是用来实现线程之间的通信问题

缓存流 可以提高输入输出效率,增加缓冲流功能后需要使用flush()才可以将缓冲区的内容写入实际的物理节点

15.4.3 转换流

输入输出流体系中还提供了两个转换流,这两个转换流用于实现将字节流转换成字符流

InputStreamReader将字节输入流转换为字符输入流,

OutputStreamWriter 将字节输出流转换成字符输出流

 

java 使用 System.in 代表标准输入,这个标准输入流式 InputStream类的实例,键盘输入的内容都是文本内容可以使用 InputStreamReader将其转换成字符流,普通的Reader读取输入内容时依然不太方便,可以将Reader再次包装成BufferedReader 利用BufferedReader可以一次读取一行内容

 

经常把读取文本内容的输入流包装成BufferedReader,用来方便的读取输入流的文本内容

 

15.4.3 推回输入流

PushbackInputStream PushbackReader 流 这两个推回输入流都带有一个推回缓冲区,当程序调用者两个流的 unread()方法时,系统会把指定数组的内容推回到该缓冲区 ,而推回输入流每次调用read()方法总是先从缓冲区读取,只有完全读取,但read()还没装满时,才会从原输入流读取

默认缓冲区的长度为1

15.5 重定向标准输入输出流

不在使用键盘作为输入,不在使用屏幕作为输入

System类里提供了三个重定向方法

15.6 JAVA 虚拟机读写其他进程的数据

使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生一个Process对象, Process对象代表由该java程序启动的java的子进程

子进程读取程序中的数据 使用的是输出流

15.7 RandomAccessFile

RandomAccessFilejava输入输出体系中最丰富的文件内容访问类,它提供了众多方法来访问文件内容,也可以向文件输出数据,与普通的输入输出流不同的是,RandomAccessFile支持随机访问,程序可以直接跳到文件任意地方来读

如果要想文件后追加内容可以使用RandomAccessFile

RandomAccessFile只能读取文件,不能读取其他的io节点

RandomAccessFile对象包含了一个记录指针,用于标记当前读写的位置。

RandomAccessFile不能像文件指定位置插入内容,如果需要插入则可以通过把插入点后的内容输入缓冲区,插入完毕后再将缓冲区内的内容写到文件后面

 

多线程断点的网络下载工具就可以通过RandomAccessFile类来实现

 

15.8 对象系列化

对象系列化的目标是将对象保存在磁盘中,或允许在网络中直接传输对象。对象系列化机制允许吧内存中的java对象转换成平台无关的二进制流 通过网络将这种二进制流传输到另一个网络节点。其他程序一旦获得了这种二进制流,都可以将这种二进制流恢复成原来的java对象

15.8.1 系列化的含义和意义

系列化机制允许将实现系列化的java对象转换为字节系列,这些字节系列可以保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象,系列化机制使得对象可以脱离程序的运行而独立存在,

所有可能在网络上传播的对象的类都应该是可系列化的

15.8.2 使用对象流实现系列化

通过实现Serializable接口来实现对象系列化,程序可以通过如下两个步骤来实现对象系列化

1.创建一个ObjectOutputStream 这个输出流是一个处理流

2.调用 ObjectOutputStream对象的writeObject()方法输出可系列化的对象

 

反系列化步骤

1.创建一个ObjectInputStream输入流,这个输入流是一个处理流

2.调用ObjectInputStream对象的readObject() 方法读取流中的对象

 

反序列化读取的仅仅是java对象的数据,不是java类,因此采用反序列化恢复java对象时,必须提供对象所属于的class对象,

 

反系列化机制无需通过构造器来初始化java对象

 

如果使用系列化机制想文件中写入多个java对象,使用反系列机制恢复对象时,必须按实际写的顺序读取

 

当一个可系列化对象有多个父类时,这些父类要么有无参数的构造器要么也是可系列化的

 

15.8.3 对象引用的系列化

如果系列化类引用的对象没有系列化,那么该类也无法系列化

程序系列化算法 如下

1.所有保存在磁盘中的对象都有一个系列化编号

2.当程序试图系列化一个对象时,程序将先检查对象是否已经被系列化过,如果该对象从未被系列化过,系统才会将对象转化为字节系列并输出

3.如果对象已经系列化过,程序将只是直接输出一个系列化编号

 

15.9 NIO

15.9.1  java IO

IO采用内存映射文件的方式来处理输入输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了,模拟了操作系统上的虚拟内存的概念,

 

Channel(通道)和Buffer(缓存) 是NIO的两个核心对象,Channel是对传统的输入输出系统的模拟,在NIO系统中国所有数据都需要通过通道传输,与传统的输入输出系统模拟,Channel与传统的InputStream OutputStream(面向流输的处理)最大的区别是提供了一个map()方法,通过该方法可以直接将一块数据映射到内存中(面向块的处理)

 

Buffer可以理解为一个容器,本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,从Channel读取数据也必须先放到Buffer

 

NIO还提供了一个用于将Unicode字符串映射成字节系列以及逆映射操作的Charset类,也提供了用于支持非阻塞式输入输出的Selector

 

Buffer中有三个重要的概念 容量 界限 和位置

 

Buffer中有两个重要方法 flip() 将limit设置为position所在位置,并将position设为0 为从Buffer中取出数据做好准备

clear()  position置为0,将limit置为capacity

 

put get来访问Buffer中的数据时,分为相对和绝对两种

相对 从buffer中的当前position出开始读写,然后将position的值按处理元素的个数增加

绝对 直接根据索引执行Buffer中读取或写入数据 不影响position的值

 

通过allocate() 方法创建的Buffer对象是普通的Buffer

ByteBuffer还提供了一个allocateDirect()方法来创建直接Buffer

直接创建Buffer的成本很高,所以只适用于长生存期的buffer

 

15.9.3 使用Channel

Channel类似于传统的流对象,但与传统的流对象有两个主要区别

1. Channel 可以直接将指定文件的部分或者全部直接映射成Buffer

2. 程序不能直接访问Channel中的数据,只能通过Buffer进行交互

 

所有的Channel都不应该直接通过构造器直接创建,而是通过传统的节点InputStream...getChannel()方法来获取,

 

Channel中最常用的三类方法是map() read() write() 其中map()方法用于将Channel对应

的部分或者全部数据映射成ByteBuffer  read() write() 用来读取数据

 

15.9.4 字符集和Charset

newDecoder() 代表该Charset 解码器

newEncoder() 代表该Charset 编码器

 

15.9.5 文件锁

文件锁可以有效的阻止多个进程并发修改同一个文件

NIOjava提供了FileLock来支持文件锁定功能,在FileChannel中提供的lock()/tryLock()方法可以获得文件锁FileLock对象  

lock() 当试图锁定某一个文件时,如果无法得到文件锁,程序将一直阻塞,

try lock() 是尝试锁定文件,它将直接返回而不是阻塞,如果获得文件锁,则返回文件锁,否则返回null

 

文件锁虽然可以用于控制并发访问,但是对于高并发访问的情景,还是推荐使用数据库来保存程序信息

 

文件锁还需要指出的几点

1. 在某一些平台上,文件锁仅是建议性的(即使一个文件不能获得文件锁,它也可以读写)

2. 在某一些平台上,不能同步的锁定一个文件并把它映射到内存中

3. 文件锁是由java虚拟机所持有的,如果两个java程序使用同一个java虚拟机允许,则他们不能对同时同一个文件进行加锁

4. 在某一些平台上关闭FileChannel时,会释放jvm 在该文件中的所有锁

 

15.10 java 7 NIO.2

java 7对原有的NIO进行了重大的改进,主要有

1. 提供了全面的文件IO和文件系统访问支持

2. 基于异步的Channel IO

15.10.1 Path  Paths  Files核心API

15.10.2 使用FileVisitor遍历文件和目录

在以前的java版本中,如果要遍历指定目录下的所有文件和子类目录,只能使用递归进行遍历,但这种方式不仅复杂,而且性能也不高

Files 工具类提供了更方便的方法遍历文件和子目录

 

15.10.3 使用WatchService 监控文件的变化

java之前的版本如果程序需要监视文件的变化,则可以考虑启动一条后台线程,这条后台线程每隔一段时间去遍历一次指定目录的文件,如果和上次遍历结果不一样就认为发生了变化,但这种方式不仅十分繁琐,而且性能也不高

你可能感兴趣的:(java,基础)