您的位置:首页 > 编程语言 > Java开发

《Java 编程思想》--第十八章:Java I/O 系统

2013-05-31 23:49 239 查看
File类既能代表一个特定文件的名称,又能代表一个目录下一组文件的名称。File类不仅仅只代表存在的文件或目录。也可以用File对象来创建新的目录或尚不存在的整个目录路径。我们还可以查看文件的特性,检查某个File对象代表的是一个文件还是目录,并可以删除文件

file.getAbsolutePath():获得绝对路径
file.canRead():获得是否可读
file.canWrite():是否可写
file.getName():获得文件或文件夹的名字
file.getParent():获得文件或文件夹的父文件夹
file.getPath():获得路径
file.length():获得文件或目录大小
file.lastModified():最后修改时间
file.isFile():是否是文件
file.isDirectory():是否是文件夹
file.mkdirs():创建文件夹路径

流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。流屏蔽了实际的I/O设备中处理数据的细节

任何自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组
任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或者字节数组
Java中的流类库让人迷惑的主要原因就在于:创建单一的结果流,却需要创建多个对象
与输入有关的所有类都应该从InputStream继承,而与输出有关的所有类都应该从OutputStream继承

InputStream的源:

字节数组
String对象
文件
管道,工作方式与实际管道相似
一种由其他种类流组成的序列
其他数据源,如internet连接

InputStream类型:

ByteArrayInputStream:允许将内存的缓冲区当做inputstream来使用,输入缓冲区,字节从中取出
StringBufferInputStream:将String转换成InputStream,输入字符串,底层使用StringBuffer
FileInputStream:用于文件读取信息, 输入字符串表示文件名、文件或FileDescriptor对象
PipedInputStream:产生用于写入相关PipedOutputStream的数据实现管道化的概念,作为多线程中的数据源
SequenceInputStream:将两个或多个InputStream对象转换成单一的InputStream,两个InputStream对象或一个容纳InputStream对象的容器Enumeration
FilterInputStream:抽象类,作为装饰器的接口,其中装饰器为其他InputStream提供了有用功能

DataInputStream:与DataOutputStream搭配使用,因此我们可以按照可抑制方式从流读取基本数据类型,输入一个InputStream,包含用于读取基本类型数据的全部接口
BufferedInputStream:使用它可以防止每次读取时都得进行式机械操作,代表使用“缓冲区”。输入InputStream,可以指定缓冲区的大小(可选)本质上不提供接口,只不过是想进程中添加缓冲区所必需的。与接口对象搭配
LineNumberInputStream:跟踪输入流中的行号,可调用getLineNumber()和setLineNumber(int),输入InputStream,仅增加了行号,因此可能要与接口对象搭配使用
PushbackInputStream:具有能弹出一个字节的缓冲区,因此可以将读到的最后一个字符回退,输入InputStream,通常作为编译器的扫描器,之所以包含在内是因为java 编译器的需要,一般不会用到

OutputStream类型:

ByteArrayOutputStream:在内存中创建缓冲区,所有送往流的数据都要放置在此缓冲区,可输入缓冲区初始化尺寸(可选),用于指定数据的目的地
FileOutputStream:用于将信息写至文件,输入字符串表示文件名、文件或FileDescriptor对象
PipedOutputStream:任何写入其中的信息都会自动作为相关PipedInputStream的输出,实现管道化的概念,输入一个PipedInputStream,指定用于多线程的数据的目的地
FilterOutputStream:抽象类,作为装饰器的接口,其中装饰器为其他OutputStream提供有用的功能

DataOutputStream:与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本类型数据,输入一个OutputStream,包含用于写入基本类型数据的全部接口
用于产生格式化输出,其中DataOutputStream处理数据的存储,PrintStream处理显示,输入一个OutputStream,可以用boolean值只是是否在每次换行时清空缓冲区(可选),应该是对OutputStream对象的呃final封装,经常被使用
BufferedOutputStream:使用它以避免每次发送数据时都要进行实际的写操作,代表使用缓冲区,可以调用flush()清空缓冲区,输入OutputStream,可以指定缓冲区大小(可选)

InputStream和OutputStream在以面向字节形式的I/O中提供极有价值的功能,Reader和Writer子提供兼容Unicode与面向字符的I/O功能
通过适配器:InputStreamReader可以吧InputStream转化为Reader,而OutputStreamWriter可以把OutputStream转换为Writer。增加Reader和Writer继承层次主要是为了国际化,在所有的I/O操作中都支持Unicode
最明智的做法是尽量尝试使用Reader和Writer,一旦程序无法成功编译,再去使用面向字节的类库
在java 1.1中的Reader和Writer:

InputStream:Reader
OutputStream:Writer
FileInputStream:FileReader
FileOutputStream:FileWriter
StringBufferInputStream:StringReader
无:StringWriter
ByteArrayInputStream:CharArrayReader
ByteArrayOutputStream:CharArrayWriter
PipedInputStream:PipedReader
PipedOutputStream:PipedWriter
FilterInputStream:FilterReader
FilterOutputStream:FilterWriter(抽象类,没有子类)
BufferedInputStream:BufferedReader(也有readLine())
BufferedOutputStream:BufferedWriter
DataInputStream:使用DataInputStream(除了当需要使用readLine()时意外,这时应该使用BufferedReader)
PrintStream:PrintWriter
LineNumberInputStream(已弃用):LineNumberReader
StreamTokenizer:StreamTokenizer(使用接受Reader的构造器)
PushbackInputStream:PushbackReader

无论何时我们使用readLine()都不应该使用DataINputStream,而应该使用BufferedReader除了这一点,DataInputStream仍是I/O类库的首选成员
没有对应Reader或者Writer的类:

DataOutputStream
File
RandomAccessFile
SequenceInputStream

自我独立的RandomAccessFile类:

适用于由大小已知的记录组成的文件,所以我们可以使用seek()将记录从一处转移到另一处,然后读取或修改记录。文件中的记录大小不一定都相同,只要我们能确定那些记录有多大以及他们在文件中的位置
在任何情况下,他都是自我独立的类,直接从Object派生而来
getFilePointer():查找当前所处的文件位置
seek()用于在文件内移至新的位置
length():用于判断文件的最大尺寸
reset():重新设定位置
支持随机读”r“和既读又写”rw“模式,并不支持只写文件

java.util.Scanner类:可以用于读取文件,而不能用于写入文件,并且这个工具存在于util包中而不再I/O包中,主要设计用来创建爱你成语言的扫描器或小语言的
标准I/O中的system流:

System.in:没有包装过的未经加工的InputStream,在使用前需要对其进行包装
System.out:经过包装的PrintStream
System.err:经过包装的PrintSteram

Java的System类提供了一些简单的静态方法,使我们可以对标准输入、输出和错误I/O流进行重定向:

setIn(InputStream)
setOut(PrintStream)
setErr(PrintStream)
需要注意的是I/O重定向操纵的是字节流,而不是字符流,因此我们使用的是InputStream和OutputStream,而不是Reader和Writer

JDK1.4中的java.nio.*包中引入了新的Java I/O,其目的在于提高速度。

实际上旧的I/O包已经使用nio重新实现过,以便充分利用这种速度提高。
速度的提高来自于所使用的结构更接近操作系统执行I/O的方式:通道和缓冲器。
我们并没有直接和通道交互,我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。
唯一直接与通道交互的缓冲器是ByteBuffer,也就是说,可以存储未加工字节的缓冲器。

旧的类库中,有三个类被修改了,用以产生FileChannel

FileInputStream
FileOutputStream
既读又写的RandomAccessFile

对于这些流,getChannel()将会产生一个FileChannel
通道可以向他传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式访问
使用put方法对ByteBuffer进行填充
如果是只读,必须显式地使用静态的allocate()方法来分配ByteBuffer

特殊方法transferTo()和transferFrom()允许我们将一个通道和另一个通道直接相连
视图缓冲器可以让我们通过某个特定的基本数据类型的视窗来查看其底层的ByteBuffer,ByteBuffer依然是实际存储的地方

如asCharBuffer()等方法获得该缓冲器上的视图,然后使用视图的put()方法插入数据,所有的基本数据类型都有自己的视图,注释使用ShortBuffer时需要类型转换

字节存放次序:不同的及其会使用不同的字节排序方法来存储数据,高位优先将最重要的字节存放在地址最低的存储器单元,而低位优先则将最重要的字节放在地址最高的存储器单元

ByteBuffer以高位优先的形式存储数据,并且数据在网上传送时也常常使用高位优先的形式

使用ByteBuffer缓冲器操纵数据:

capacity():返回缓冲区容量
clear():清空缓冲区,将position设置为0,limit设置为容量
flip():将limit设置为position,position设置为0,此方法用于准备从缓冲区读取已经写入的数据
limit():返回limit值
limit(int lim):设置limit值
mark():将mark设置为position
position():获得position的值
position(int pos):设置position
remaining():返回(limit-position)
hasRemaining():若有介于position和limit之间的元素,返回true

JDK1.4中引入了文件加速机制,允许我们同步访问某个作为共享资源的文件

通过FileChannel可以调用tryLock()或lock(),就可以获得真个文件的FileLock
tryLock()是非阻塞式的,它设法获取锁,但是如果不能获得,它将直接从方法调用返回
lock()是阻塞式的,它要阻塞进程直至锁可以获得,或调用lock()的线程终端,或调用lock()的通道关闭
使用FileLock.release()可以释放锁
对于独占锁或者共享锁的支持必须由底层的操作系统提供。如果操作系统不支持共享锁并为每一个请求都创建一个锁,那么他就会使用独占锁。锁的类型(共享或独占)可以通过FileLock.isShared()进行查询

压缩:

Java I/O的压缩格式的数据流不是从Reader和Writer派生而来的,而是属于InputStream和OutputStream继承层次结构的一部分。因为压缩类库是按字节方式而不是字符方式处理的。
所有的压缩类:

CheckedInputStream:GetCheckSum()为任何InputStream产生校验和(不仅仅是解压缩)
CheckedOutputStream:GetCheckSum()为任何OutputStream产生校验和(不仅仅是压缩)
DeflaterOutputStream:压缩类的基类
ZipOutputStream:一个 DeflaterOutputStream,用于将数据压缩成Zip文件格式
GZIPOutputStream:一个DeflaterOutputStream,用于将数据压缩成GZIP文件格式
InflaterInputStream:解压缩类的基类
ZipInputStream:一个 InflaterInputStream,用于解压缩Zip文件格式的数据
GZIPInputStream:一个InflaterInputStream,用于解压缩GZIP文件格式的数据

GZIP使用实例:

Zip多文件保存实例:

zip格式也被应用于JAR文件格式中

对象序列化:

Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象
利用对象的序列化可以实现轻量级持久性。持久性意味着一个对象的生存周期并不取决于程序是否正在执行;它可以生存于程序的调用之间。通过将一个序列化对象写入磁盘文件,然后在重新调用程序时恢复该对象,就能够实现持久性的效果
对象序列化是为了支持两种主要特性:

Java的远程方法调用,它使存活于其他计算机上的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值
对Java Bean来说,对象的序列化也是必须的,使用一个Bean时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动时进行后期恢复;这种具体工作就是由对象序列化完成的

序列化需要实现Serializable接口,该接口仅仅是一个标记接口,不存在任何方法
必须保证Java虚拟机能找到相关的.class文件
通过实现Externalizable接口,代替实现Serializable接口,来对序列化过程进行控制。

这个Externalizable接口继承了Serializable接口,同时增添了两个方法,writeExernal()和readExternal()。这两个方法会在序列化合反序列化还原的过程中被自动调用
对于Serializable对象,对象完全以它存储的二进制位为基础来构造,而不调用构造器。而对于一个Externalizable对象,所有普通的默认构造器都会被调用,然后调用readExternal()。所有的默认的构造器都会被调用,才能使Externalizable对象产生正确的行为
如果从一个Externalizable对象继承,通常需要调用基类版本的writeExternal()和readExternal()来为基类组件提供恰当的存储和恢复功能

transient(瞬时)关键字:

如果需要防治对象的敏感部分被序列化,且操作的是一个Serializable对象,可以用transient(瞬时)关键字逐个字段地关闭序列化

Externalizable的替代方法:

可以实现Serializable接口,并添加writeObjct()和readObject()方法,这样一旦对象被序列化或者被反序列化还原,就会自动的分别调用这两个方法

深度复制:复制整个对象网而不仅仅是基本对象的引用

XML

使用XOM能将对象序列化为xml文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: