重新认识Java流这种数据结构

一、背景

基于之前文章中的疑问,重新学习了一下《Java编程思想》关于Java I/O系统章节中的流的相关部分。因此,这篇文章就是记录下结合对Servlet流,重新学习IO的感受。

  1. 为什么会有流这种数据结构?
    答:一个系统一般都会存在很多的系统输入与输出。数据格式也各不相同,例如二进制、字节、字符等。而且数据源也是各种各样,例如:字节数组、String对象、文件、管道、其他流、网络I/O等。所以流就是用来屏蔽这些底层细节的,让你不用去关注每个输入或输出对数据的具体处理细节。

二、Java流

流这种数据结构,基本上都是伴随着I/O出现。这里有一个我之前对I/O的一个很刻板的印象就是:I/O就是指的读写网络数据、读写磁盘。但是实际上I/O并不止于此。

Input输入:
指的是可以从数据源,读取数据到程序中。那数据源就不仅仅是网络、磁盘,也可以是一个字节数组、String对象、管道等
Output输出:
是可以将程序中的数据,写到目的地。同样,目的地也可以是String对象、磁盘、字节数组、网络等等。

总结来说就类似下图:

目前Java中最基本的输入流是InputStreamReader. 它们都是抽象类,其中的抽象方法就是read(). 但是InputStream是用来读取字节的,Reader是用来读取字符的。
下面是InputStream中的抽象方法read()

 /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the stream
     * has been reached, the value <code>-1</code> is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * <p> A subclass must provide an implementation of this method.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public abstract int read() throws IOException;

注释中说明了read是读取流中的下一个字节,并将该字节转化为int返回。而Reader中的抽象方法为:

/**
     * Reads characters into a portion of an array.  This method will block
     * until some input is available, an I/O error occurs, or the end of the
     * stream is reached.
     *
     * @param      cbuf  Destination buffer
     * @param      off   Offset at which to start storing characters
     * @param      len   Maximum number of characters to read
     *
     * @return     The number of characters read, or -1 if the end of the
     *             stream has been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    abstract public int read(char cbuf[], int off, int len) throws IOException;

可以看到读取字符流,需要传入一个char数组最为读取容器,一个偏移量和指定读取的长度。而且返回值也是int,但是这个返回值表达的却是读取了多少个字符。
在这两个read方法中,都一点说明就是,除非发生IO异常或者流读完了,否则会一直阻塞。

三、关于流的重复读

首先流只能读一次这个说法肯定是错误的,具体要看流的实现类。
在两种基础的流对象中,都有mark()、reset()以及markSupported()方法,在不同的子类中可以覆写,默认是不支持mart和reset的。
例如:ServletInputStream就是直接继承InputStream,因此HttpServletRequest获取到的输入流中,确实不支持重复读。但是像ByteArrayInputStream就是支持重复读的。

四、ByteBuffer

ByteBuffer是NIO中我们用来与Channel打交道的对象,无论读还是写都是通过这个对象,并且这个对象是支持重复读的。
而ServletInputStream的实现类CoyoteInputStream中使用的InputBuffer里层就是用的ByteBuffer和CharBuffer,所以为什么实际中ServletInputStream不设计为可重复读,现在也不能理解。

posted @ 2021-03-11 17:26  Bencakes  阅读(148)  评论(0编辑  收藏  举报