第15 章 输入/输出

来源:互联网 发布:微软sql server 编辑:程序博客网 时间:2024/05/21 00:55

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与传统的InputStreamOutputStream(面向流输的处理)最大的区别是提供了一个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之前的版本如果程序需要监视文件的变化,则可以考虑启动一条后台线程,这条后台线程每隔一段时间去遍历一次指定目录的文件,如果和上次遍历结果不一样就认为发生了变化,但这种方式不仅十分繁琐,而且性能也不高

0 0
原创粉丝点击