[关闭]
@lemonguge 2015-07-01T08:14:55.000000Z 字数 7094 阅读 376

Java I/O系统(五)

I/O


Java I/O系统的流大部分都已经介绍完毕了,本篇将介绍一些很有趣但不简单的I/O类。


随机访问文件

java.io包中有一个名为RandomAccessFile的类,根据类的名称可以大概明白这个类的功能,即对随机访问文件的读取写入。而之前介绍的流要么只是读取,要么是写入,因此这个类还是很有特点的。可以发现这个类并不是以四个基础流的名称作为后缀,而是以File作为后缀,查看API发现这个类并不继承于任何流类,而是直接继承自Object并实现了DataInputDataOuput这两个接口。(DataInputStreamDataOutputStream也分别实现了这两个接口,所以大部分的方法都已经介绍过了)但是要注意:数据必须有规律。

RandomAccessFile类的构造函数需要指明两个参数:文件对象(或虚拟文件路径)和访问模式。

"rws"和"rwd"对确保在系统崩溃时不会丢失重要信息特别有用。如果该文件不在本地设备上,则无法提供这样的保证。

随机访问文件内部维护了一个巨大的byte数组,并通过指针可以操作数组中的元素,可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。

  1. import java.io.IOException;
  2. import java.io.RandomAccessFile;
  3. public class RandomAccess {
  4. public static void main(String[] args) throws IOException {
  5. writeFile();
  6. readFile();
  7. modifyFile();
  8. }
  9. public static void modifyFile() throws IOException {
  10. RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw"); // 不同与创建File对象,它不会覆盖已存在的文件
  11. raf.seek(7);
  12. raf.writeInt(17); // 对数据修改
  13. System.out.println("pos:" + raf.getFilePointer());
  14. raf.seek(0);
  15. byte[] name = new byte[4];
  16. raf.read(name);
  17. raf.seek(7);
  18. System.out.println(new String(name) + "=" + raf.readInt());
  19. raf.close();
  20. }
  21. public static void readFile() throws IOException {
  22. RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r");
  23. raf.seek(5);
  24. System.out.println("读入字符:" + raf.readChar());
  25. raf.seek(19);
  26. System.out.println("Pi为:" + raf.readFloat());
  27. raf.close();
  28. }
  29. public static void writeFile() throws IOException {
  30. RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw");
  31. raf.write("小雨".getBytes()); // 4-byte
  32. // 10-0110-0001
  33. raf.writeByte(609); // 1-byte
  34. // 0110-0001
  35. raf.writeChar(97); // 2-byte
  36. raf.writeInt(97); // 4-byte
  37. raf.writeDouble(3.3333); // 8-byte
  38. raf.writeFloat(3.1415F); // 4-byte
  39. raf.writeLong(123456789L);
  40. raf.close();
  41. }
  42. } /*
  43. 读入字符:a
  44. Pi为:3.1415
  45. pos:11
  46. 小雨=17
  47. */// :~

管道流

对于管道流,常用于任务间的通信,一般与多线程结合使用。管道流有四个类,分别为:PipedInputStream管道输入字节流、PipedOutputStream管道输出字节流、PipedReader管道输入字符流和PipedWriter管道输出字符流。记住管道输入流应该连接到管道输出流,我们可以在构造时流对象时指定,也可以通过connect方法将管道输入输出流连接起来。

执行流程应该是,先写内容到管道中,之后从管道读入所写的内容。对内存而言,写内容到管道中即为输出,从管道读入所写的内容即为输入。简单地说“先管道输出,后管道输入”。

  1. import java.io.BufferedReader;
  2. import java.io.FileReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PipedInputStream;
  6. import java.io.PipedOutputStream;
  7. import java.io.PrintWriter;
  8. // 管道输入封装成线程任务
  9. class Input implements Runnable {
  10. private PipedInputStream pis;
  11. public Input(PipedInputStream pis) {
  12. this.pis = pis;
  13. }
  14. @Override
  15. public void run() {
  16. BufferedReader br = null;
  17. br = new BufferedReader(new InputStreamReader(pis));
  18. String line = null;
  19. try {
  20. // 输出管道所读入的内容
  21. while ((line = br.readLine()) != null) {
  22. System.out.println("Input:" + line);
  23. }
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  29. // 管道输出封装成线程任务
  30. class Output implements Runnable {
  31. private PipedOutputStream pos;
  32. public Output(PipedOutputStream pos) {
  33. this.pos = pos;
  34. }
  35. @Override
  36. public void run() {
  37. BufferedReader br = null;
  38. PrintWriter pw = null;
  39. try {
  40. br = new BufferedReader(new FileReader("file.txt"));
  41. pw = new PrintWriter(pos, true);
  42. String line = null;
  43. System.out.println("Output start..");
  44. // 每读一行便打印输出一行
  45. while ((line = br.readLine()) != null) {
  46. pw.println(line);
  47. }
  48. System.out.println("Output end..");
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. } finally {
  52. if (br != null)
  53. try {
  54. br.close();
  55. } catch (IOException e) {
  56. e.printStackTrace();
  57. }
  58. if (pw != null)
  59. pw.close();
  60. }
  61. }
  62. }
  63. public class PipedIO {
  64. public static void main(String[] args) throws IOException {
  65. PipedInputStream pis = new PipedInputStream();
  66. PipedOutputStream pos = new PipedOutputStream();
  67. pis.connect(pos);
  68. new Thread(new Input(pis)).start(); // 开启一个新的线程,执行输入任务
  69. new Thread(new Output(pos)).start(); // 开启一个新的线程,执行输出任务
  70. }
  71. } /* Output: // 后四行的输出顺序可能不同
  72. Output start..
  73. Output end..
  74. Input:abc
  75. Input:你好!Hello World
  76. Input:你好,世界。
  77. *///:~

标准I/O

Java提供了System.inSystem.outSystem.errSystem.err也已经被包装成PrintStream对象,System.in是一个没有被包装过的为经加工的InputStream。这意味我们可以直接使用System.outSystem.err,而在使用System.in之前必须对其进行包装。

在这里,笔者想向大家介绍三个静态方法:

应该注意到上面方法所操纵的是字节流,而不是字符流。

  1. import java.io.BufferedOutputStream;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.PrintStream;
  5. public class RedirIO {
  6. public static void main(String[] args) throws IOException {
  7. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io_log.txt", true)); // 续写文件
  8. System.setOut(new PrintStream(bos, true));
  9. System.out.println("标准输出流重定向");
  10. }
  11. } ///~

执行以上代码会发现,在主控台没有任何输出,可以在当前项目下的"io_log.txt"文件中看到输出内容。


与I/O技术结合的Properties类

曾经在《容器》中,并没有对Properties类进行讲解,因为该类最具特点的应用是需要和Java I/O结合使用。不过在此之前,我们先来了解一下该类的基本使用。

  1. import java.util.Properties;
  2. import java.util.Set;
  3. public class Props {
  4. public static Properties getProps() {
  5. Properties pp = new Properties();
  6. pp.setProperty("小雨", "17");
  7. pp.setProperty("lemon", "20");
  8. pp.setProperty("Java", "Thinking in Java");
  9. pp.setProperty("lemon", "22"); // 修改属性
  10. return pp;
  11. }
  12. public static void showProps(Properties pp) {
  13. Set<String> names = pp.stringPropertyNames(); // 获取所有的键
  14. for (String name : names) {
  15. String value = pp.getProperty(name);
  16. System.out.println(name + ":" + value);
  17. }
  18. }
  19. public static void main(String[] args) {
  20. showProps(getProps());
  21. }
  22. } /* Output:
  23. Java:Thinking in Java
  24. 小雨:17
  25. lemon:22
  26. *///:~
  1. Properties集合中的键和值都是字符串类型
  2. Properties集合中的数据可以保存到流中,或者从流获取。

对于Properties集合的第二个特点,因为有几个方法可以来实现。

  1. import java.util.Properties;
  2. public class Props {
  3. public static Properties getProps() {
  4. Properties pp = new Properties();
  5. pp.setProperty("小雨", "17");
  6. pp.setProperty("lemon", "20");
  7. pp.setProperty("Java", "Thinking in Java");
  8. pp.setProperty("lemon", "22"); // 修改属性
  9. return pp;
  10. }
  11. public static void main(String[] args) {
  12. Properties pp = getProps();
  13. pp.list(System.out); // 将属性列表输出到主控台查看内容
  14. }
  15. } /* Output:
  16. -- listing properties --
  17. Java=Thinking in Java
  18. 小雨=17
  19. lemon=22
  20. *///:~

Java支持后缀以".properties"结尾的文件,里面其实就应该存储Properties对象属性列表。

  1. import java.io.BufferedOutputStream;
  2. import java.io.IOException;
  3. import java.io.PrintStream;
  4. import java.util.Properties;
  5. public class Props {
  6. public static Properties getProps() {
  7. Properties pp = new Properties();
  8. pp.setProperty("小雨", "17");
  9. pp.setProperty("lemon", "20");
  10. pp.setProperty("Java", "Thinking in Java");
  11. return pp;
  12. }
  13. public static void main(String[] args) throws IOException {
  14. store();
  15. }
  16. public static void store() throws IOException {
  17. Properties pp = getProps();
  18. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("prop.properties"));
  19. pp.store(bos, "这是Comments"); // 将集合中的字符串键值信息持久化存储到文件中
  20. bos.close();
  21. }
  22. } ///~

从输入流中读取属性列表

  1. import java.io.BufferedInputStream;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.util.Properties;
  5. public class Props {
  6. public static void main(String[] args) throws IOException {
  7. load();
  8. }
  9. public static void load() throws IOException {
  10. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("prop.properties"));
  11. Properties pp = new Properties();
  12. pp.load(bis);
  13. pp.list(System.out);
  14. }
  15. } /* Output:
  16. -- listing properties --
  17. Java=Thinking in Java
  18. lemon=20
  19. 小雨=17
  20. *///:~

到现在为止,笔者对Java I/O系统已经全部结束。

后记:对于Java I/O系统的知识,一直都很想写,但是由于这一部分的知识比较乱而且实在太多,所以构思了很久,写于2015年6月16日。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注