@lemonguge
2015-07-01T08:14:55.000000Z
字数 7094
阅读 376
I/O
Java I/O系统的流大部分都已经介绍完毕了,本篇将介绍一些很有趣但不简单的I/O类。
在java.io包中有一个名为RandomAccessFile的类,根据类的名称可以大概明白这个类的功能,即对随机访问文件的读取和写入。而之前介绍的流要么只是读取,要么是写入,因此这个类还是很有特点的。可以发现这个类并不是以四个基础流的名称作为后缀,而是以File作为后缀,查看API发现这个类并不继承于任何流类,而是直接继承自Object并实现了DataInput和DataOuput这两个接口。(DataInputStream和DataOutputStream也分别实现了这两个接口,所以大部分的方法都已经介绍过了)但是要注意:数据必须有规律。
RandomAccessFile类的构造函数需要指明两个参数:文件对象(或虚拟文件路径)和访问模式。
write方法都将导致抛出IOException。"rws"和"rwd"对确保在系统崩溃时不会丢失重要信息特别有用。如果该文件不在本地设备上,则无法提供这样的保证。
随机访问文件内部维护了一个巨大的byte数组,并通过指针可以操作数组中的元素,可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。
import java.io.IOException;import java.io.RandomAccessFile;public class RandomAccess {public static void main(String[] args) throws IOException {writeFile();readFile();modifyFile();}public static void modifyFile() throws IOException {RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw"); // 不同与创建File对象,它不会覆盖已存在的文件raf.seek(7);raf.writeInt(17); // 对数据修改System.out.println("pos:" + raf.getFilePointer());raf.seek(0);byte[] name = new byte[4];raf.read(name);raf.seek(7);System.out.println(new String(name) + "=" + raf.readInt());raf.close();}public static void readFile() throws IOException {RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r");raf.seek(5);System.out.println("读入字符:" + raf.readChar());raf.seek(19);System.out.println("Pi为:" + raf.readFloat());raf.close();}public static void writeFile() throws IOException {RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw");raf.write("小雨".getBytes()); // 4-byte// 10-0110-0001raf.writeByte(609); // 1-byte// 0110-0001raf.writeChar(97); // 2-byteraf.writeInt(97); // 4-byteraf.writeDouble(3.3333); // 8-byteraf.writeFloat(3.1415F); // 4-byteraf.writeLong(123456789L);raf.close();}} /*读入字符:aPi为:3.1415pos:11小雨=17*/// :~
对于管道流,常用于任务间的通信,一般与多线程结合使用。管道流有四个类,分别为:PipedInputStream管道输入字节流、PipedOutputStream管道输出字节流、PipedReader管道输入字符流和PipedWriter管道输出字符流。记住管道输入流应该连接到管道输出流,我们可以在构造时流对象时指定,也可以通过connect方法将管道输入输出流连接起来。
执行流程应该是,先写内容到管道中,之后从管道读入所写的内容。对内存而言,写内容到管道中即为输出,从管道读入所写的内容即为输入。简单地说“先管道输出,后管道输入”。
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PipedInputStream;import java.io.PipedOutputStream;import java.io.PrintWriter;// 管道输入封装成线程任务class Input implements Runnable {private PipedInputStream pis;public Input(PipedInputStream pis) {this.pis = pis;}@Overridepublic void run() {BufferedReader br = null;br = new BufferedReader(new InputStreamReader(pis));String line = null;try {// 输出管道所读入的内容while ((line = br.readLine()) != null) {System.out.println("Input:" + line);}} catch (IOException e) {e.printStackTrace();}}}// 管道输出封装成线程任务class Output implements Runnable {private PipedOutputStream pos;public Output(PipedOutputStream pos) {this.pos = pos;}@Overridepublic void run() {BufferedReader br = null;PrintWriter pw = null;try {br = new BufferedReader(new FileReader("file.txt"));pw = new PrintWriter(pos, true);String line = null;System.out.println("Output start..");// 每读一行便打印输出一行while ((line = br.readLine()) != null) {pw.println(line);}System.out.println("Output end..");} catch (IOException e) {e.printStackTrace();} finally {if (br != null)try {br.close();} catch (IOException e) {e.printStackTrace();}if (pw != null)pw.close();}}}public class PipedIO {public static void main(String[] args) throws IOException {PipedInputStream pis = new PipedInputStream();PipedOutputStream pos = new PipedOutputStream();pis.connect(pos);new Thread(new Input(pis)).start(); // 开启一个新的线程,执行输入任务new Thread(new Output(pos)).start(); // 开启一个新的线程,执行输出任务}} /* Output: // 后四行的输出顺序可能不同Output start..Output end..Input:abcInput:你好!Hello WorldInput:你好,世界。*///:~
Java提供了System.in、System.out和System.err。System.err也已经被包装成PrintStream对象,System.in是一个没有被包装过的为经加工的InputStream。这意味我们可以直接使用System.out和System.err,而在使用System.in之前必须对其进行包装。
在这里,笔者想向大家介绍三个静态方法:
void setIn(InputStream in)重定向输入输出流void setOut(PrintStream out)重定向标准输出流void setErr(PrintStream err)重定向错误I/O流应该注意到上面方法所操纵的是字节流,而不是字符流。
import java.io.BufferedOutputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintStream;public class RedirIO {public static void main(String[] args) throws IOException {BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io_log.txt", true)); // 续写文件System.setOut(new PrintStream(bos, true));System.out.println("标准输出流重定向");}} ///~
执行以上代码会发现,在主控台没有任何输出,可以在当前项目下的"io_log.txt"文件中看到输出内容。
曾经在《容器》中,并没有对Properties类进行讲解,因为该类最具特点的应用是需要和Java I/O结合使用。不过在此之前,我们先来了解一下该类的基本使用。
import java.util.Properties;import java.util.Set;public class Props {public static Properties getProps() {Properties pp = new Properties();pp.setProperty("小雨", "17");pp.setProperty("lemon", "20");pp.setProperty("Java", "Thinking in Java");pp.setProperty("lemon", "22"); // 修改属性return pp;}public static void showProps(Properties pp) {Set<String> names = pp.stringPropertyNames(); // 获取所有的键for (String name : names) {String value = pp.getProperty(name);System.out.println(name + ":" + value);}}public static void main(String[] args) {showProps(getProps());}} /* Output:Java:Thinking in Java小雨:17lemon:22*///:~
Properties集合中的键和值都是字符串类型Properties集合中的数据可以保存到流中,或者从流获取。对于Properties集合的第二个特点,因为有几个方法可以来实现。
void load(Reader reader)方法:从输入字符流中读取属性列表(键和元素对)。void load(InputStream inStream)方法:输入流按load(Reader)中所指定的、简单的面向行的格式,并假定使用ISO 8859-1字符编码void list(PrintWriter out)方法:将属性列表输出到指定的打印输出字符流,此方法对调试很有用。void list(PrintStream out)方法:将属性列表输出到指定的打印输出字节流,此方法对调试很有用。void store(Writer writer, String comments)方法:将此Properties表中的属性列表(键和元素对)和注释写入输出字符流。void store(OutputStream out, String comments)方法:将此Properties表中的属性列表(键和元素对)和注释写入输出字节流。
import java.util.Properties;public class Props {public static Properties getProps() {Properties pp = new Properties();pp.setProperty("小雨", "17");pp.setProperty("lemon", "20");pp.setProperty("Java", "Thinking in Java");pp.setProperty("lemon", "22"); // 修改属性return pp;}public static void main(String[] args) {Properties pp = getProps();pp.list(System.out); // 将属性列表输出到主控台查看内容}} /* Output:-- listing properties --Java=Thinking in Java小雨=17lemon=22*///:~
Java支持后缀以".properties"结尾的文件,里面其实就应该存储Properties对象属性列表。
import java.io.BufferedOutputStream;import java.io.IOException;import java.io.PrintStream;import java.util.Properties;public class Props {public static Properties getProps() {Properties pp = new Properties();pp.setProperty("小雨", "17");pp.setProperty("lemon", "20");pp.setProperty("Java", "Thinking in Java");return pp;}public static void main(String[] args) throws IOException {store();}public static void store() throws IOException {Properties pp = getProps();BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("prop.properties"));pp.store(bos, "这是Comments"); // 将集合中的字符串键值信息持久化存储到文件中bos.close();}} ///~
从输入流中读取属性列表
import java.io.BufferedInputStream;import java.io.FileInputStream;import java.io.IOException;import java.util.Properties;public class Props {public static void main(String[] args) throws IOException {load();}public static void load() throws IOException {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("prop.properties"));Properties pp = new Properties();pp.load(bis);pp.list(System.out);}} /* Output:-- listing properties --Java=Thinking in Javalemon=20小雨=17*///:~
到现在为止,笔者对Java I/O系统已经全部结束。
后记:对于Java I/O系统的知识,一直都很想写,但是由于这一部分的知识比较乱而且实在太多,所以构思了很久,写于2015年6月16日。