JAVA知识专题总结之输入输出

来源:互联网 发布:淘宝钻石展位和直通车 编辑:程序博客网 时间:2024/05/10 07:44
主题 概要 JAVA基础 JAVA的I/O部分概括、总结 编辑 时间 新建 20160730 序号 参考资料 1 JAVA编程思想 2 http://docs.oracle.com/javase/8/docs/api/

编程语言的I/O类库中常用流这个抽象概念,它代表任何有能力产生数据的数据源对象或者是有能力接收数据的接收端对象。“流”屏蔽了实际的I/O设备中处理数据的细节。
JAVA类库中的I/O分成输入和输出两部分,可以在JDK文档里的类层次结构中查看到。通过继承,任何自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取当个字节或者字节数组。同样,任何自Outputstream或Writer派生而来的类都含有write()的基本方法,用于写单个字节或字节数组。我们很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所期望的功能。实际上,Java中“流”类库让人迷惑的主要原因就在于:创建单一的结果流,却需要创建多个对象。

InputStream与OutputStream

在JDK1.0中,类库的设计者首先限定与输入有关的所有类都应该从InputStream继承,而与输出有关的所有类都由OutputStream继承。

InputStream类型

InputStream的作用是用来表示那些从不同数据源产生输入的类,如下图所示,这些数据源包括:
1)字节数组
2)String对象
3)文件
4)“管道”,工作方式与实际管道相似,即从一端输入,从另一端输出
5)一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内。
6)其他数据源,如Internet连接等。
另外,FilterInputStream也属于一种InputStream,为“装饰器”(decorator)类提供基类,其中“装饰器”类可以把属性或有用的接口与输入流连接在一起。
这里写图片描述

OutputStream类型

如下表所示,该类型的类决定了输出所要去往的目标:字节数组(但不是String,不过可以用字节数组自己创建),文件或管道。
另外,FilterOutputStream也属于一种OutputStream,为“装饰器”(decorator)类提供基类,其中“装饰器”类可以把属性或有用的接口与输出流连接在一起。
这里写图片描述

添加属性和有用的接口

JAVA I/O类库需要多种不同功能的组合,这正是使用装饰器模式的理由所在。在编写程序时,它给我们提供了相当多的灵活性(因为我们可以很容易的混合和匹配属性),但是它同时也增加了代码的复杂性。JAVA I/O类库操作不便的原因在于:我们必须创建许多类—“核心”I/O类型加上所有装饰器,才能得到我们所希望的单个I/O对象。

通过FilterInputStream从InputStream读取数据

FilterInputStream,抽象类,作为“装饰器”接口。主要使用派生类来完成两件完全不同的事情,DataInputStream允许我们读取不同的基本类型数据以及String对象。
其他FilterInputStream类则在内部修改InputStream的行为方式,是否缓冲,是否保留它所读过的行等等。
我们几乎每次都要对输入进行缓冲,不管连接的是什么I/O设备。
这里写图片描述

通过FilterOutputStream向OutputStream写入

同样,使用FilterOutputStream的派生类,来改变输出的行为。
DataOutputStream可以将各种数据类型以及String对象格式化输出到“流”中,所有方法都以write开头,如writeByte(),writeFloat()等等。
PrintStream是为了可以格式化打印所有的基本数据类型及String对象。
BufferedOutputStream对数据流使用缓冲技术,当每次向流写入时,不必每次都进行实际的物理写操作。
这里写图片描述

Reader与Writer

JDK1.1对基本的I/O流类库进行了重大的修改,当我们初次看到Reader和Writer类时,可能会以为这是两个用来替代InputStream和OutputStream的类,尽管一些原始的“流”类库不再被使用(如果使用它们,则会收到编译器的警告信息),使用InputStream和OutputStream在以面向字节形式的I/O中仍可提供极有价值的功能,Reader和Writer则提供兼容Unicode与面向字符的I/O功能。
有时候我们必须把来自于“字节”层次结构中的类与“字符”层次结构中的类结合起来使用,为了实现这个目的,要用到“适配器”(adapter)类,InputStreamReader可以把InputStream转换成Reader, OutputStreamWriter可以把OutputStream转换成Writer。

数据的来源和去处

几乎所有原始的Java I/O流类都有相应的Reader类和Writer类来提供天然的Unicode操作。
这里写图片描述

更改流的行为

对于InputStream和OutputStream来说,会使用FilterInputStream和FilterOutputStream的装饰器子类来修改“流”以满足特殊需求。Reader和Writer的类继承层次结构继续沿用相同的思想,但是并不完全相同。
这里写图片描述
有一点很清楚,无论我们何时使用readLine(),都不应该使用DataInputStream(这会遭到编译器的强烈反对),而应该使用BufferedReader。除了这一点外,DataInputStream仍是I/O类库的首选成员。

未发生变化的类

这里写图片描述
特别是DataOutputStream,在使用时没有任何变化;因此如果想以“可传输的”格式存储和检索数据,可以使用InputStream和OutputStream继承层次结构。

自我独立的类:RandomAccessFile

RandomAccessFile适用于大小已知的记录组成的文件,所以我们可以使用seek()将记录从一处转移到另一处,然后读取或者修改记录。
RandomAccessFile是一个完全独立的类,从头开始编写其所有的方法,与InputStream和OutputStream继承结构没有任何的关联。在任何情况下,它都是自我独立的,直接从Object对象派生而来。

典型使用方式

缓冲输入文件

打开一个文件作用于字符输入,可以使用String或File对象作为文件名的FileInputReader,并用BufferedReader进行文件缓冲,应用BufferedReader的readline()进行读取。当readline()返回为NULL时,就达到了文件的末尾。

//: io/BufferedInputFile.javaimport java.io.*;public class BufferedInputFile {  // Throw exceptions to console:  public static String  read(String filename) throws IOException {    // Reading input by lines:    BufferedReader in = new BufferedReader(      new FileReader(filename));    String s;    StringBuilder sb = new StringBuilder();    while((s = in.readLine())!= null)      sb.append(s + "\n");    in.close();    return sb.toString();  }  public static void main(String[] args)  throws IOException {    System.out.print(read("BufferedInputFile.java"));  }} /* (Execute to see output) *///:~

最后,需要调用 close()来显示关闭文件。

从内存输入

调用上面例子封装的类,把数据缓存入内存中,再用缓存中的String结果来创建一个StringReader,然后调用read()每次读取一个字符,并把它发送到控制台。

//: io/MemoryInput.javaimport java.io.*;public class MemoryInput {  public static void main(String[] args)  throws IOException {    StringReader in = new StringReader(      BufferedInputFile.read("MemoryInput.java"));    int c;    while((c = in.read()) != -1)      System.out.print((char)c);  }} /* (Execute to see output) *///:~

格式化的内存输入

要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类(不是面向字符的),因此必须使用InputStream类而不是reader类。

//: io/FormattedMemoryInput.javaimport java.io.*;public class FormattedMemoryInput {  public static void main(String[] args)  throws IOException {    try {      DataInputStream in = new DataInputStream(        new ByteArrayInputStream(         BufferedInputFile.read(          "FormattedMemoryInput.java").getBytes()));      while(true)        System.out.print((char)in.readByte());    } catch(EOFException e) {      System.err.println("End of stream");    }  }} /* (Execute to see output) *///:~

基本的文件输出

FileWriter可以向文件写入数据,只需创建一个与指定文件连接的FileWriter。通常会用BufferedWriter将其包装起来用以缓冲输出,另外,为了提供格式化机制,会将它装饰成PrintWriter。

//: io/BasicFileOutput.javaimport java.io.*;public class BasicFileOutput {  static String file = "BasicFileOutput.out";  public static void main(String[] args)  throws IOException {    BufferedReader in = new BufferedReader(      new StringReader(        BufferedInputFile.read("BasicFileOutput.java")));    PrintWriter out = new PrintWriter(      new BufferedWriter(new FileWriter(file)));    int lineCount = 1;    String s;    while((s = in.readLine()) != null )      out.println(lineCount++ + ": " + s);    out.close();    // Show the stored file:    System.out.println(BufferedInputFile.read(file));  }} /* (Execute to see output) *///:~

文本文件输出的快捷方式:Java SE5在PrintWriter中添加了一个辅助构造器,使得你不必在每次希望创建文本文件,并向其中写入时,都去执行所有的装饰操作。

//: io/FileOutputShortcut.javaimport java.io.*;public class FileOutputShortcut {  static String file = "FileOutputShortcut.out";  public static void main(String[] args)  throws IOException {    BufferedReader in = new BufferedReader(      new StringReader(       BufferedInputFile.read("FileOutputShortcut.java")));    // Here's the shortcut:    PrintWriter out = new PrintWriter(file);    int lineCount = 1;    String s;    while((s = in.readLine()) != null )      out.println(lineCount++ + ": " + s);    out.close();    // Show the stored file:    System.out.println(BufferedInputFile.read(file));  }} /* (Execute to see output) *///:~

存储和恢复数据

为了输出可供另一个“流”恢复的数据,我们需要用DataOutputStream写入数据,并用DataInputStream恢复数据,这些流可以是任何形式。需注意,DataInputStream和DataOutputStream是面向字节的,因此使用InputStream和OutputStream。

//: io/StoringAndRecoveringData.javaimport java.io.*;public class StoringAndRecoveringData {  public static void main(String[] args)  throws IOException {    DataOutputStream out = new DataOutputStream(      new BufferedOutputStream(        new FileOutputStream("Data.txt")));    out.writeDouble(3.14159);    out.writeUTF("That was pi");    out.writeDouble(1.41413);    out.writeUTF("Square root of 2");    out.close();    DataInputStream in = new DataInputStream(      new BufferedInputStream(        new FileInputStream("Data.txt")));    System.out.println(in.readDouble());    // Only readUTF() will recover the    // Java-UTF String properly:    System.out.println(in.readUTF());    System.out.println(in.readDouble());    System.out.println(in.readUTF());  }} /* Output:3.14159That was pi1.41413Square root of 2*///:~

要注意readUTF()和writeUTF()方法,UTF-8是一种多字节格式,其编码长度会根据实际使用的字符集会有所变化。如果我们使用的只是ASCII码或者几乎都是ASCII字符(只占7位),那么就显得浪费带宽,所以UTF-8将ASCII字符编码成单一字节的形式,而非ASCII字符则编码成两到三个字节的形式。

读写随机访问文件

使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream,另外,利用seek()可以在文件中到处移动,并修改文件中的某个值。

//: io/UsingRandomAccessFile.javaimport java.io.*;public class UsingRandomAccessFile {  static String file = "rtest.dat";  static void display() throws IOException {    RandomAccessFile rf = new RandomAccessFile(file, "r");    for(int i = 0; i < 7; i++)      System.out.println(        "Value " + i + ": " + rf.readDouble());    System.out.println(rf.readUTF());    rf.close();  }  public static void main(String[] args)  throws IOException {    RandomAccessFile rf = new RandomAccessFile(file, "rw");    for(int i = 0; i < 7; i++)      rf.writeDouble(i*1.414);    rf.writeUTF("The end of the file");    rf.close();    display();    rf = new RandomAccessFile(file, "rw");    rf.seek(5*8);    rf.writeDouble(47.0001);    rf.close();    display();  }} 

管道流

主要用于多线程中,进行任务之间的通信。

0 0
原创粉丝点击