• JAVA篇:Java IO (二)抽象基类InputStream、OutputStream、Reader、Writer


    2、抽象基类InputStream、OutputStream、Reader、Writer

    IO流的抽象基类分字节输入流(InputStream)、字节输出流(OutputStream)、字符输入流(Reader)、字符输出流(Writer)。它们是IO流中的抽象父类,是Java IO操作的基础。流都实现了Closeable接口,即都需要关闭,输出流则额外实现了一个Flushable方法,主要是通知立刻将数据刷入指定输出位置。

    2.1 InputStream

    java.io.InputStream是一个抽象类,是字节输入流的所有类的超类,除了继承自Object的属性和方法,InputStream包含9个方法:

    1. abstract int read()方法是一个虚拟方法,从输入流中读取下一个字节,其子类必须实现这个方法

    2. int read(byte[] b,int off,int len)方法将流读入byte[]数组中第off个位置开始,读取len个字节。其实现有调用read()方法进行读取,读取前判断参数是否合理,若读取到-1则退出(流以-1判断是否结束),并返回已经读取的数据个数,若读取的个数为0则返回-1。

      public int read(byte b[], int off, int len) throws IOException {
              if (b == null) {
                  throw new NullPointerException();
              } else if (off < 0 || len < 0 || len > b.length - off) {
                  throw new IndexOutOfBoundsException();
              } else if (len == 0) {
                  return 0;
              }
      ​
              int c = read();
              if (c == -1) {
                  return -1;
              }
              b[off] = (byte)c;
      ​
              int i = 1;
              try {
                  for (; i < len ; i++) {
                      c = read();
                      if (c == -1) {
                          break;
                      }
                      b[off + i] = (byte)c;
                  }
              } catch (IOException ee) {
              }
              return i;
          }

       

    3. int read(byte[])方法用处是将流的输入读入byte[]数组中,其实际上调用的是read(byte[],int,int)

    4. long skip(long),跳过指定个字节不读。其具体实现是:创建了一个跳过的缓冲数据,将读到这里面去,跳过的字节有长度限制,为2048个。通过循环排除IO异常的感染,知道读完或者读满要跳过的,然后返回实际跳过的量。

        
       public long skip(long n) throws IOException {
      ​
              long remaining = n;
              int nr;
      ​
              if (n <= 0) {
                  return 0;
              }
      ​
              int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
              byte[] skipBuffer = new byte[size];
              while (remaining > 0) {
                  nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
                  if (nr < 0) {
                      break;
                  }
                  remaining -= nr;
              }
      ​
              return n - remaining;
          }
    5. int available()返回估测的可读的数量,在InputStream中是直接return 0。这个方法对于所有流的实现来说不都是准确的,所以慎用。这个方法需要被子类覆写。单次读或跳过很多字节不会阻塞,但是可能会读取或跳过少量字节。

    6. void close(),关闭流,但是在InputStream中没有具体实现,会释放操作系统资源。

    7. synchronized void mark(int readlimit)标记读取位置,如果流实现支持,就可以重置位置,下次读取则一模一样。InputStream中什么都没做。使用之前要通过markSupported方法判断一下是否支持。

    8. synchronized void reset()reset到mark方法最后一次调用的位置,InputStream中并没有实现mark方法,所以reset方法直接抛出异常。

    9. boolean markSupported()判断当前流是否支持标记流。

    2.2 OutputStream

    java.io.OutputStream此抽象类是表示输出字节流的所有类的超累,输出流接受输出字节并将这些字节发送到某个接收器,除了继承自Object的属性和方法,OutputStream包含5个方法:

    1. abstract void write(int b),抽象方法,通常是写入一个字节byte,低8位将写入,高24位会被忽略。将指定字节写入输出位置,子类必须实现这个方法。

    2. void write(byte b[], int off, int len)方法,将数组b的起始位置off开始,写入len长度的字节。其实现包含边界判定,最后通过循环调用write方法实现。

          
      public void write(byte b[], int off, int len) throws IOException {
              if (b == null) {
                  throw new NullPointerException();
              } else if ((off < 0) || (off > b.length) || (len < 0) ||
                         ((off + len) > b.length) || ((off + len) < 0)) {
                  throw new IndexOutOfBoundsException();
              } else if (len == 0) {
                  return;
              }
              for (int i = 0 ; i < len ; i++) {
                  write(b[off + i]);
              }
          }

       

    3. void write(byte b[])通过调用方法2:write(b,0,b.length)实现

    4. void flush()刷新此输出流并强制携出所有缓冲的输出字节。flush的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用flush方法指示应将这些字节立即写入它们的预期目标。但是如果此流的预期目标是由基础操作系统提供的一个抽象(如一个文件),则flush只能保证将以前写入到流的字节传递给操作系统进行写入,不保证能立即将这些字节写入到物理设备(如磁盘驱动器)。在OutputStream中该方法不执行任何操作。

    5. void close()关闭输出流并释放与此流有关的所有系统资源。close的常规协定是:该方法将关闭输出流,关闭的流不能执行输出操作,也不能重新发开。OutputStream的close方法不执行人格操作。

    2.3 Reader

    java.io.Reader实现了接口Closeable和Readable,其是用于读取字符流的抽象类。

    相比于字节输入流InputStream,Reader中不同的有以下几点:

    1. lock对象 lock是Object类型的对象,用于同步针对此流的操作的对象。为了提高效率,字符流可以使用自身以外的对象来保护重要部分,因此,子类应使用此字段中的对象而不是this或者同步的方法。构造方法中Reader()所做的是创建一个新的字符流,其重要部分将同步其自身的reader;而Reader(Object lock)则是创建一个新的字符流reader,其重要部分将同步给定的对象。

    2. Reader的三个read方法中,read()方法和read(char cbuf[])都是使用read(char cbuf[], int off, int len)实现的,而子类必须实现read(char cbuf[], int off, int len)方法,注意:用于支持高效单字符输入的子类必须重写read()方法

         /*用于支持高效单字符输入的子类必须重写该方法*/
         public int read() throws IOException {
              char cb[] = new char[1];
              if (read(cb, 0, 1) == -1)
                  return -1;
              else
                  return cb[0];
          }
      ​
          public int read(char cbuf[]) throws IOException {
              return read(cbuf, 0, cbuf.length);
          }
      ​
          /*子类必须实现该方法*/
          abstract public int read(char cbuf[], int off, int len) throws IOException;

       

    3. read(CharBuffer target)方法:该方法试图将字符读入指定的字符缓冲区。缓冲区可照鸳鸯用作字符的存储库:所做的唯一改变时put操作的结果。不对缓冲区执行翻转或重绕操作。

        
       public int read(java.nio.CharBuffer target) throws IOException {
              int len = target.remaining();
              char[] cbuf = new char[len];
              int n = read(cbuf, 0, len);
              if (n > 0)
                  target.put(cbuf, 0, n);
              return n;
          }

       

    4. *ready()方法** 该方法在Reader中是直接返回flase的,其用来判断是否准备读取此流。如果保证下一个read()不阻塞输入,则返回True,否则返回false。注意,返回false并不保证下一次读取。

    5. skip()方法的长度限制是8192

    2.4 Writer

    java.io.Writer实现了Closeable, Flushable, Appendable三个接口,它时写入字符流的抽象类。

    Appendable:如果某个类的实例打算接收取自 Formatter 的格式化输出,那么该类必须实现 Appendable 接口。

    Writer相比于字节输出流OutputStream有更多的不同。

    1. lock对象

    2. write方法 Writer提供了5个write方法,其中子类需要实现的是void write(char cbuf[], int off, int len)

      /*该方法使用write(char cbuf[], int off, int len)方法实现*/
       public void write(int c) throws IOException {
            synchronized (lock) {
                if (writeBuffer == null){
                    writeBuffer = new char[WRITE_BUFFER_SIZE];
                }
                writeBuffer[0] = (char) c;
                write(writeBuffer, 0, 1);
            }
        }
        /*该方法使用write(char cbuf[], int off, int len)方法实现*/
        public void write(char cbuf[]) throws IOException {
            write(cbuf, 0, cbuf.length);
        }
      ​
        /*子类必须实现该方法*/
        abstract public void write(char cbuf[], int off, int len) throws IOException;
        /*该方法使用write(String str, int off, int len)方法实现*/
        public void write(String str) throws IOException {
            write(str, 0, str.length());
        }
      ​
        /*该方法使用write(char cbuf[], int off, int len)方法实现*/
        public void write(String str, int off, int len) throws IOException {
            synchronized (lock) {
                char cbuf[];
                if (len <= WRITE_BUFFER_SIZE) {
                    if (writeBuffer == null) {
                        writeBuffer = new char[WRITE_BUFFER_SIZE];
                    }
                    cbuf = writeBuffer;
                } else {    // Don't permanently allocate very large buffers.
                    cbuf = new char[len];
                }
                str.getChars(off, (off + len), cbuf, 0);
                write(cbuf, 0, len);
            }
        }
      3.append方法都来自于其实现的Appendable接口,用于将指定字符或字符序列添加到此writer。
    3.    public Writer append(CharSequence csq) throws IOException {
              if (csq == null)
                  write("null");
              else
                  write(csq.toString());
              return this;
          }
      ​
          
          public Writer append(CharSequence csq, int start, int end) throws IOException {
              CharSequence cs = (csq == null ? "null" : csq);
              write(cs.subSequence(start, end).toString());
              return this;
          }
      ​
         
          public Writer append(char c) throws IOException {
              write(c);
              return this;
          }

     

    当你深入了解,你就会发现世界如此广袤,而你对世界的了解则是如此浅薄,请永远保持谦卑的态度。
  • 相关阅读:
    Linux下c程序的编译方法:
    Linux分区机制和常见命令
    java虚拟机内存大小调整:
    break、continue、return循环三剑客之异同
    Scanner中的nextInt()陷阱
    windbg无法下载符号文件
    各种函数调用约定及浮点数传参
    一个crackme的分析
    SetProcessAffinityMask的问题
    x64内联汇编注意点
  • 原文地址:https://www.cnblogs.com/liwxmyself/p/14934529.html
Copyright © 2020-2023  润新知