流与文件
流
- 在JavaAPI中,可以从其中读入一个字节序列的对象称为输入流,而可以向其中写入一个字节序列的对象称做输出流
- 这些字节序列的来源地和目的地可以是文件,而且通常都是文件,但是也可以是网络连接,甚至是内存块
- 抽象类inputStream和OutputStream构成了有层次结构的输入/输出(I/O)类的基础
- 面向字节的流不便于处理以Unicode形式存储的信息,所以从抽象类reader和writer中继承出来的专门用于处理Unicode字符的类构成了一个单独的层次结构
- 这些拥有的读入和写出操作都是基于两字节的Unicode码元的,而不是基于单字节的字符
读写字节
- inputStream类有一个抽象方法:abstract int read();这个方法将读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1.
- 在设计具体输入流类时,必须覆盖这个方法以提供适用的功能,在FileInputStream类中,这个方法将从某个文件中读入一个字节,而system.in却是从键盘读入信息的
- InputStream类还有若干个非抽象方法,它们可以读入一个字节数组,或者跳过大量的字节。
- read和write方法在执行时都将阻塞,直至字节确实被读入或写出。
- 这就意味着如果流不能被立即访问,那么当前的线程将被阻塞。
- 这使得在这个方法等待指定的流变为可用的这段时间里,其他的线程就有机会去执行有用的工作。
- 当你完成对流的读写时,应该通过调用close方法来关闭它,这个方法会释放掉十分有限的操作系统资源。
- 如果一个应用程序打开了过多的流而没有关闭它,那么系统资源将被耗尽。
- 关闭一个输出流的同时也就是在清空用于该输出流的缓冲区;所有被临时置于缓冲区中,以便用更大的包的形式传递的字符在关闭输出流时都将被送出。如果不关闭文件,那么写出字节的最后一个包可能将永远也得不到传递;我们可以用flush方法来人为地清空这些输出。
- 即使某个流类提供了使用原生的read和write功能来工作的某些具体的方法,应用系统的程序员还是很少使用它们,因为大家感兴趣的数据可能包含数字、字符串和对象,而不是原生字节
- Java提供了众多从基本的inputStream和outputStream类导出的类,这些类使我们可以处理那些以常用格式表示的数据,而不只是在字节级别上表示的数据。
完整的流家族
- Java拥有一个包含各种流类型的流家族,其数量超过60个
- 让我们把流类家族中的成员按照它们的使用方法来进行划分,这样就形成了字节和字符的两个单独的层次结构
- InputStream和outPutStream类可以读写单个的字节或字节数组,这些类构成了层次结构的基础。要想读写字符串和数字,就需要功能更强大的子类。
- DataInputStream和DataOutputStream可以二进制格式读写所有基本Java类型。
- 最后,还包含了多个很有用的流,例如,ZipInputStream和ZipOutputStream可以以常见的Zip压缩格式读写文件
- 对于Unicode,可以使用抽象类Reader和Writer的子类。Reader和Writer类的基本方法与InputStream和OutputStream中的方法类似。
- ZipInputStream和ZipOutputStream可以以常见的Zip压缩格式读写文件
- Java SE 5.0引入了4个附加接口:Closeable,Flushable,Readable和Appendable
- InputStream,outputStream,Reader和Writer都实现了Closeable接口,而OutputStream和Writer还实现了Flushable接口
- Readable接口只有一个方法:int read(CharBuffer cb)
- CharBuffer类拥有按顺序和随机地进行读写访问的方法,它表示一个内存中的缓冲区或者一个内存映像的文件。
- Appendable接口有两个用于添加单个字符和字符序列的方法:Appendable append(char c) Appendable append(CharSequence s)
- CharSequence接口描述了一个char值序列的基本属性,它是用String,CharBuffer,StringBuilder和StringBuffer来实现的。
- 在流类家族中,只有Writer实现了Appendable。
- java.io.Closeable5.0; void close(),关闭这个Closeable,可能抛出IOException。
- java.io.Flushable5.0; void flush(),清空这个Flushable
- java.lang.Readable5.0; int read(CharBuffer cb),尝试着读入cb可以持有的数量的char值。返回读入的char值的数量,或者在从这个Readable中无法再获得更多的值时返回-1
- java.lang.Appendable5.0;Appendable append(char c),Appendable append(CharSequence cs)向这个Appendable中追加给定的码元或者给定的序列中的所有码元,返回this。
组合流过滤器
- FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流,而你只需向其构造器提供文件名或文件的完整路径名。
- 与抽象类InputStream和OutputStream一样,这些类支持在字节级别上的读写。
- 为了从文件中读入数字,首先需要创建一个FileInputStream,然后将其传递给DataInputStream的构造器
- 当多个流连接在一起时,需要跟踪各个中介流
- 可以通过嵌套过滤器来添加多重功能。
文本输入与输出
- 在存储文本字符串时,需要考虑字符编码方式问题。
- OutputStreamWriter类将使用选定的字符编码方式,把Unicode字符流转换为字节流。
- InputStreamReader类将包含字节的输入流转换为可以产生Unicode字符的读入器
如何写出文本输出
- 以二进制格式写出数据,需要使用DataOutputStream
- 以文本格式写出数据,需要使用PrintWriter
以文本格式存储对象
- 对nextInt的调用读入的是数组长度,而不是坠在行尾的换行字符。
字符集
- 可以通过调用静态的forName方法来获得一个Charset,只需向这个方法传递一个官方名字或者是它的某个别名:Charset cset = Charset.forName(“ISO-8859-1”);
- 字符集的名字是大小写不敏感的
- 一旦有了字符集,就可以使用它将Unicode字符串转换成编码而成的字节序列,下面是如何编码Unicode字符串的代码:
- 要想了解字节序列,需要有字节缓冲区。使用byteBuffer数组的静态方法wrap可以将字节数组转为字节缓冲区
- java.nio.charset.Charset 1.4
- static SortedMap availableCharsets() 获取所有字符集,返回映射表,键是字符集的名字,值是字符集
- static Charset forNmae(String name) 获取给定名字的字符集
- Set aliases() 返回这个字符集的别名集
- ByteBuffer encode(String str) 将给定的字符串编码为字节序列
- CharBuffer decode(ByteBuffer buffer) 解码给定的字节序列,无法识别的输入将被转换成Unicode的“替代字符”('\uFFFD')
- java.nio.ByteBuffer 1.4
- byte[] array() 返回这个缓冲区所管理的字节数组
- static ByteBuffer wrap(byte[] bytes)
- static ByteBuffer wrap(byte[] bytes,int offset, int length) 返回挂你给定的字节数组或给定字节数组的某个范围的字节缓冲区
- java.nio.CharBuffet
- char[] array() 返回这个缓冲区所管理的码元数组。
- char charAt(int index) 返回给定索引处的码元
- String toString() 返回由这个缓冲区所管理的码元构成的字符串
读写二进制数据
- writeInt总是将一个整数写出为4字节的二进制数量值,而不管它有多少位,writeDouble总是将一个double值写出为8字节的二进制数量值。
- 这样的产生的结果并非人可阅读,对于给定类型的每个值,所需的空间都是相同的,而且将其读回也比解析文本要更快
- Java所有值都按照高位在前的模式写出,这使得Java数据文件可以独立于平台
- DataInputStream类实现了DataInput接口,要想从文件中读入二进制数据,需要将DataInputStream与某个字节源相组合
- 随机访问文件
- RandomAccessFile类可以在文件中的任何位置查找或写入数据。磁盘文件都是随机访问的,但是从网络而来的数据流却不是。
- 当你将已有文件打开成RandomAccessFile时,这个文件并不会被删除。
- RandomAccessFile类同时实现了DataInput和DataOutput接口。