Java的输入输出流

来源:互联网 发布:unity3d vr 编辑:程序博客网 时间:2024/05/09 05:17

System.in是inputStream的子类

decorator的目的是在不改变任何原有的类的基础下,添加新的功能(你可以理解为书上说的灵活性)。
其中Myclass是你要扩展的类,DecoratorA跟DecoratorB封装了你要扩展的功能,并保存有一个MyInterface的引用。


       装饰器模式
  MyInterface
      |
_______|_____________
|                    | 
Myclass             Decorator
                  _____|________
                  |             | 
               DecoratorA DecoratorB
                
              IO流组织模式
                    java.io.InputStream
                       |
_______________________|_________________________
               |                                 |
ByteArrayInputStream                             |
FilterInputStream                                |
StringBufferInputStream     _____________________|_________________________________
FileInputStream                      |           |                     |           |
PipedInputStream            DataInputStream BufferedInputStream LineNumInpuStream XXX
基础的流只有左边4个,这些流代表了数据的来源,所有的流都必须从这四个中之一开始。
(注,还有一个RandomAccessFile、File)。
然后当我们需要什么添加功能,就从右边中选择一个装饰。例如,我们需要缓存功能,那么需要bufferedInputStream装饰:
BufferdInputStream is = new BufferedInputStream(new FileInputStream("xxx.txt"));


思考模式,基础流->转换流(转换为字符流)->装饰流
xxxinputStream --inputStreamReader--BufferedInputStream

先生成流,流再去读取


  


 

在这其中InputStream和OutputStream在早期的Java版本中就已经存在了,它们是基于字节流的,而基于字符流的Reader和Writer是后来加入作为补充的。以上的层次图是Java类库中的一个基本的层次体系。

  在这四个抽象类中,InputStream和Reader定义了完全相同的接口:

int read()
int read(char cbuf[])
int read(char cbuf[], int offset, int length)

  而OutputStream和Writer也是如此:

int write(int c)
int write(char cbuf[])
int write(char cbuf[], int offset, int length)

  这六个方法都是最基本的,read()和write()通过方法的重载来读写一个字节,或者一个字节数组。

  更多灵活多变的功能是由它们的子类来扩充完成的。知道了Java输入输出的基本层次结构以后,本文在这里想给大家一些以后可以反复应用例子,对于所有子类的细节及其功能并不详细讨论。

import java.io.*;

public class IOStreamDemo {

      public void samples() throws IOException {

           //1. 这是从键盘读入一行数据,返回的是一个字符串
           BufferedReader stdin =new BufferedReader(new InputStreamReader(System.in)); 
           System.out.print("Enter a line:");
           System.out.println(stdin.readLine());

           //2. 这是从文件中逐行读入数据

           BufferedReader in = new BufferedReader(newFileReader("IOStreamDemo.java"));
           String s, s2 = new String();
           while((s = in.readLine())!= null
                      s2 += s + "\n"
           in.close();

           //3. 这是从一个字符串中逐个读入字节 
           StringReader in1 = new StringReader(s2);
           int c;
           while((c = in1.read()) != -1)
                      System.out.print((char)c);

           //4. 这是将一个字符串写入文件 
           try {
                      BufferedReader in2 = new BufferedReader(new StringReader(s2));
                      PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out")));
                      int lineCount = 1;
                      while((s = in2.readLine()) != null )
                                 out1.println(lineCount++ + ": " + s);
                      out1.close();
           } catch(EOFException e) {
                      System.err.println("End of stream");
           }
      }

}

      对于上面的例子,需要说明的有以下几点:

     

      1. BufferedReaderReader的一个子类,它具有缓冲的作用,避免了频繁的从物理设备中读取信息。它有以下两个构造函数:

    BufferedReaderReader in) 
    BufferedReaderReader in, int sz)

      这里的sz是指定缓冲区的大小。

      它的基本方法:

    void close() //关闭流 

               void mark(int readAheadLimit) //标记当前位置

               boolean markSupported() //是否支持标记

               int read() //继承自Reader的基本方法

               int read(char[] cbuf, int off, int len) //继承自Reader的基本方法

               String readLine() //读取一行内容并以字符串形式返回

               boolean ready() //判断流是否已经做好读入的准备

               void reset() //重设到最近的一个标记

               long skip(long n) //跳过指定个数的字符读取

      2. InputStreamReaderInputStreamReader之间的桥梁,由于System.in是字节流,需要用它来包装之后变为字符流供给             BufferedReader使用。

      3. PrintWriter out1 = new PrintWriter(new BufferedWriter(newFileWriter("IODemo.out")));

      这句话体现了Java输入输出系统的一个特点,为了达到某个目的,需要包装好几层。首先,输出目的地是文件IODemo.out,所以最内层包装的是FileWriter,建立一个输出文件流,接下来,我们希望这个流是缓冲的,所以用BufferedWriter来包装它以达到目的,最后,我们需要格式化输出结果,于是将PrintWriter包在最外层。

      Java提供了这样一个功能,将标准的输入输出流转向,也就是说,我们可以将某个其他的流设为标准输入或输出流,看下面这个例子:

    import java.io.*;

    public class Redirecting {

           public static void main(String[] args) throws IOException {
                  PrintStream console = System.out;
                  BufferedInputStream in = new BufferedInputStream( new FileInputStream"Redirecting.java"));
                  PrintStream out = new PrintStream( new BufferedOutputStream new FileOutputStream"test.out")));
                  System.setIn(in);
                  System.setOut(out);

                  BufferedReader br = new BufferedReader( newInputStreamReader(System.in));
                  String s;
                  while((s = br.readLine()) != null
                         System.out.println(s);
                  out.close(); 
                  System.setOut(console);
          } 
    }

      在这里java.lang.System的静态方法

    static void setIn(InputStream in) 
    static void setOut(PrintStream out)

      提供了重新定义标准输入输出流的方法,这样做是很方便的,比如一个程序的结果有很多,有时候甚至要翻页显示,这样不便于观看结果,这是你就可以将标准输出流定义为一个文件流,程序运行完之后打开相应的文件观看结果,就直观了许多。

      Java流有着另一个重要的用途,那就是利用对象流对对象进行序列化。下面将开始介绍这方面的问题。

      在一个程序运行的时候,其中的变量数据是保存在内存中的,一旦程序结束这些数据将不会被保存,一种解决的办法是将数据写入文件,而Java中提供了一种机制,它可以将程序中的对象写入文件,之后再从文件中把对象读出来重新建立。这就是所谓的对象序列化Java中引入它主要是为了RMI(Remote Method Invocation)和Java Bean所用,不过在平时应用中,它也是很有用的一种技术。

      所有需要实现对象序列化的对象必须首先实现Serializable接口。下面看一个例子:

    import java.io.*;
    import java.util.*;

    public class Logon implements Serializable {

           private Date date = new Date();
           private String username;
           private transient String password;


           Logon(String name, String pwd) {
                  username = name;
                  password = pwd;
           }


           public String toString() {
                  String pwd = (password == null) ? "(n/a)" : password;
                  return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd;
           }


           public static void main(String[] args) throws IOException, ClassNotFoundException {
                  Logon a = new Logon("Morgan""morgan83");
                  System.out.println( "logon a = " + a);
                  ObjectOutputStream o = new ObjectOutputStream( newFileOutputStream"Logon.out"));
                  o.writeObject(a);
                  o.close();

                  int seconds = 5;
                  long t = System.currentTimeMillis() + seconds * 1000;
                  whileSystem.currentTimeMillis() < t) ;

                  ObjectInputStream in = new ObjectInputStream( newFileInputStream"Logon.out"));
                  System.out.println( "Recovering object at " + new Date());
                  a = (Logon)in.readObject();
                  System.out.println("logon a = " + a); 
           }
    }

      类Logon是一个记录登录信息的类,包括用户名和密码。首先它实现了接口Serializable,这就标志着它可以被序列化。之后再main方法里ObjectOutputStream o = newObjectOutputStream( new FileOutputStream"Logon.out"));新建一个对象输出流包装一个文件流,表示对象序列化的目的地是文件Logon.out。然后用方法writeObject开始写入。想要还原的时候也很简单ObjectInputStream in = new ObjectInputStream( newFileInputStream"Logon.out"));新建一个对象输入流以文件流Logon.out为参数,之后调用readObject方法就可以了。

      需要说明一点,对象序列化有一个神奇之处就是,它建立了一张对象网,将当前要序列化的对象中所持有的引用指向的对象都包含起来一起写入到文件,更为奇妙的是,如果你一次序列化了好几个对象,它们中相同的内容将会被共享写入。这的确是一个非常好的机制。它可以用来实现深层拷贝。

      关键字transient在这里表示当前内容将不被序列化,比如例子中的密码,需要保密,所以没有被写入文件。


    一下转自:http://hzxdark.iteye.com/blog/40133

    我不知道各位是师弟师妹们学java时是怎样的,就我的刚学java时的感觉,java.io包是最让我感到一头雾水的。所以现在这篇文,尽可能简单地描述java.io包的结构,希望对java.io同样一头雾水的师弟师妹们有些帮助^_^

    我开始学java时,java.io的介绍是在《java编程思想》里看的。说实话,当时完全看不明白——“java.io的是用‘decorator模式’来构建的”——刚学java时,天知道啥玩意叫decorator……

    不过要明白java.io,确实需要理解decorator设计模式,下面详细介绍下。
    所谓decorator,装饰,其实可以说是一种设计的技巧,说白了没什么难的,别看很多网上资料说的天花乱坠的(非常讨厌有些文章总是把简单的问题描述得跟两头猪的kiss问题一样复杂……)。

    decorator的结构如下:

                     MyInterface
                      |
    _______|_______
    |                                       |
    Myclass                     Decorator
                                          ____|_____
                                           |             |
                               DecoratorA DecoratorB


    decorator的目的是在不改变任何原有的类的基础下,添加新的功能(你可以理解为书上说的灵活性)。其中Myclass是你要扩展的类,DecoratorA跟DecoratorB封装了你要扩展的功能,并保存有一个MyInterface的引用。

    考虑以下代码:
    public static void main(Strings[] arg){
    myInterface a = new myClass();
    a.print();
    }
    myInterface 是myClass的接口,只声明了一个方法print(),myClass实现了该方法:

    public void print(){
    System.out.println("hello");
    }

    那么假如我们要在不改变原来的myClass的基础上,变成输出“hello world!”,要怎么做呢?
    当然我们可以考虑直接写个myClass的子类,helloClass之类,但是要是要求根据环境不同,输出"hello world!",my hello world","my Hello"之类的组合呢?
    用继承的方式将不得不写一堆类似的子类来。

    decorator,装饰模式的解决方法是,只实现基本的功能,把附加的功能抽出来放一边。
    例如以下代码:
    class DecoratorA implements Decorator{
    MyInterface myObject;
    DecoratorA(myInterface myObject){
    this.myObject = myObject;
    }
    public void print(){
    myObject.print();
    System.out.print("world!");
    }
    }

    class DecoratorB implements Decorator{
    MyInterface myObject;
    DecoratorA(myInterface myObject){
    this.myObject = myObject;
    }
    public void print(){
    System.out.print("my");
    myObject.print();
    }
    }

    DecoratorA和DecoratorB的功能分别是打印出world跟my。这时main函数要打印出my hello world可简单变为:

    public static void main(Strings[] arg){
    MyInterface a =new DecoratorA(new DecoratorB(new MyClass());
    a.print();
    }

    简单吧?简单的说,就是:
    print(){
    print("xxx");//可替换成你要添加的任何处理;
    myObject.print();//调用基础类的函数;
    xxxx; //后续处理
    }
    Decorator的介绍就到此为止,接下来讲java.io.

    看到

    MyInterface a =new DecoratorA(new DecoratorB(new MyClass());

    是不是觉得眼熟咧?这跟

    BufferedInputStream bis = new BufferedInputStream(new DataInpuStream(new FileInputStream("xxx.txt")));

    是不是很像?

    (画外音加一个臭鸡蛋扔上来:因为java.io就是用decorator模式组织的,当然像啦……)

    java.io分Stream跟reader、writer两大类,这里只详细介绍Stream,并最后两者间的关系。Stream又分inputStream、OutputStream,两者基本是对称的,这里也只介绍InputStream.

                                      java.io.InputStream
                                                  |
    _______________________|________________________
    |                                                                                                       |
    ByteArrayInputStream FilterInputStream
    StringBufferInputStream                        _____________________|____________________________
    FileInputStream | | | |
    PipedInputStream                                  DataInputStream BufferedInputStream LineNumInpuStream XXX

    (注:xxx是PushbackInputStream,上面的图放不下)

    这个图跟最初介绍的hello world的图很像吧?呵呵。
    基础的流只有左边4个,这些流代表了数据的来源,所有的流都必须从这四个中之一开始。(注,还有一个RandomAccessFile、File,这两个不在本文介绍范围)。
    然后当我们需要什么添加功能,就从右边中选择一个装饰。例如,我们需要缓存功能,那么需要bufferedInputStream装饰:

    BufferdInputStream is = new BufferedInputStream(new FileInputStream("xxx.txt"));

    假如再要DataInputStream的功能,只要在加一层:
    DataInputStream dis = new DataInputStream(new BufferdInputStream(new FileInputStream));
    (厄,我不甚明白这个类添加的功能是做什么用的,资料说是增加读取java原生数据的功能,不甚明白,有清楚的来补充一下,pipeInputStream跟sequenceInputStream也没用过,欢迎补充说明)
    这里你可以想象成,在基本的FileInputStream.readxxx()方法在BufferedInputStream的readxxx()方法调用,并添加相应的处理。

    最后说说InputStream跟reader的区别:

    老实说,我只知道一个支持字节流,一个支持unicode流,除此之外有啥米性能上的不同我还真不知道,哈哈。知道的快来补充补充~

    让被装饰者进入装饰者中.