概览
Java中的IO源为4个抽象类,2个字节流相关抽象类InputStream,OutputStream. 2个字符流相关抽象类Reader,Writer.直接联系是都实现了Closeable接口,另外,整个IO的代码基于装饰者模式设计,存在大量的组合 > 聚合 > 关联 > 依赖关系,不了解UML模型图和关系的可以点击这里快速了解。我这里为了UML图的可读性,没有打开dependencies。有兴趣的读者可以自己使用IDEA的diagrams功能查看
除了抽象类Reader下面的FilterReader是抽象类,其他实现全部是非抽象类。包括缓冲Buffered,数据Data,文件File,字节数组ByteArray,Object,String等各种字节/字符流实现类
IO实现类中分为两类,一类是被装饰者类,一类是装饰者。被装饰者类就是能够从数据源获取数据的类,比如网络,文件,标准输入/输出等,另一类型是装饰者类,比如BufferedInputStream/BufferedOutputStream等,装饰者类中依赖一个数据源被装饰者类,来实现特定功能,比如缓冲的流读写等,体现在装饰者类的构造函数中需要传入一个被装饰的数据流
字节流抽象类约定的需实现的抽象方法read()返回的是int,即ASCII,而字符流约定需要实现的抽象方法为abstract public int read(char cbuf[], int off, int len) throws IOException;读取字符数据到字符数组cbuf中,返回读取到的字符数int
InputStream-被装饰者超类
package java.io; /** * 此类只有一个抽象方法 read() 所有继承此类的方法必须实现read()方法 用于从输入流中处理读取数据的过程 */ public abstract class InputStream implements Closeable { //顾名思义 最大可跳过的字节数 private static final int MAX_SKIP_BUFFER_SIZE = 2048; /** * 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. * 从输入流读取下一个字节 返回0-255的int值 其实就是ASCII 这个方法的实现在输入数据到达前必须阻塞,检测到输入流结束或者抛出异常则此方法可以结束 */ public abstract int read() throws IOException; //读取一批数据到传入的数组b中 public int read(byte b[]) throws IOException { return read(b, 0, b.length); } /** * 源码这里一大堆注释 但是实现其实很简单 * 传入一个数组b,然后正常读取数据,把读出来的字节从off参数位置开始放,总共读取len个字节 */ 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; } /** * Skips over and discards <code>n</code> bytes of data from this input * stream. The <code>skip</code> method may, for a variety of reasons, end * up skipping over some smaller number of bytes, possibly <code>0</code>. * This may result from any of a number of conditions; reaching end of file * before <code>n</code> bytes have been skipped is only one possibility. * The actual number of bytes skipped is returned. If {@code n} is * negative, the {@code skip} method for class {@code InputStream} always * returns 0, and no bytes are skipped. Subclasses may handle the negative * value differently. * * <p> The <code>skip</code> method of this class creates a * byte array and then repeatedly reads into it until <code>n</code> bytes * have been read or the end of the stream has been reached. Subclasses are * encouraged to provide a more efficient implementation of this method. * For instance, the implementation may depend on the ability to seek. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * @exception IOException if the stream does not support seek, * or if some other I/O error occurs. * 跳过并丢弃n个字节的数据 如果n是负数 则返回0不跳过 官方鼓励实现类用更高效的方法实现 */ 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; } /** * Returns an estimate of the number of bytes that can be read (or * skipped over) from this input stream without blocking by the next * invocation of a method for this input stream. The next invocation * might be the same thread or another thread. A single read or skip of this * many bytes will not block, but may read or skip fewer bytes. * * <p> Note that while some implementations of {@code InputStream} will return * the total number of bytes in the stream, many will not. It is * never correct to use the return value of this method to allocate * a buffer intended to hold all data in this stream. * * <p> A subclass' implementation of this method may choose to throw an * {@link IOException} if this input stream has been closed by * invoking the {@link #close()} method. * * <p> The {@code available} method for class {@code InputStream} always * returns {@code 0}. * * <p> This method should be overridden by subclasses. * * @return an estimate of the number of bytes that can be read (or skipped * over) from this input stream without blocking or {@code 0} when * it reaches the end of the input stream. * @exception IOException if an I/O error occurs. */ //返回可读字节数 子类应该实现 public int available() throws IOException { return 0; } /** * Closes this input stream and releases any system resources associated * with the stream. * * <p> The <code>close</code> method of <code>InputStream</code> does * nothing. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException {} /** * Marks the current position in this input stream. A subsequent call to * the <code>reset</code> method repositions this stream at the last marked * position so that subsequent reads re-read the same bytes. * * <p> The <code>readlimit</code> arguments tells this input stream to * allow that many bytes to be read before the mark position gets * invalidated. * * <p> The general contract of <code>mark</code> is that, if the method * <code>markSupported</code> returns <code>true</code>, the stream somehow * remembers all the bytes read after the call to <code>mark</code> and * stands ready to supply those same bytes again if and whenever the method * <code>reset</code> is called. However, the stream is not required to * remember any data at all if more than <code>readlimit</code> bytes are * read from the stream before <code>reset</code> is called. * * <p> Marking a closed stream should not have any effect on the stream. * * <p> The <code>mark</code> method of <code>InputStream</code> does * nothing. * * @param readlimit the maximum limit of bytes that can be read before * the mark position becomes invalid. * @see java.io.InputStream#reset() */ //可以标记并用reset方法跳回去的一个预定义方法 public synchronized void mark(int readlimit) {} /** * Repositions this stream to the position at the time the * <code>mark</code> method was last called on this input stream. * * <p> The general contract of <code>reset</code> is: * * <ul> * <li> If the method <code>markSupported</code> returns * <code>true</code>, then: * * <ul><li> If the method <code>mark</code> has not been called since * the stream was created, or the number of bytes read from the stream * since <code>mark</code> was last called is larger than the argument * to <code>mark</code> at that last call, then an * <code>IOException</code> might be thrown. * * <li> If such an <code>IOException</code> is not thrown, then the * stream is reset to a state such that all the bytes read since the * most recent call to <code>mark</code> (or since the start of the * file, if <code>mark</code> has not been called) will be resupplied * to subsequent callers of the <code>read</code> method, followed by * any bytes that otherwise would have been the next input data as of * the time of the call to <code>reset</code>. </ul> * * <li> If the method <code>markSupported</code> returns * <code>false</code>, then: * * <ul><li> The call to <code>reset</code> may throw an * <code>IOException</code>. * * <li> If an <code>IOException</code> is not thrown, then the stream * is reset to a fixed state that depends on the particular type of the * input stream and how it was created. The bytes that will be supplied * to subsequent callers of the <code>read</code> method depend on the * particular type of the input stream. </ul></ul> * * <p>The method <code>reset</code> for class <code>InputStream</code> * does nothing except throw an <code>IOException</code>. * * @exception IOException if this stream has not been marked or if the * mark has been invalidated. * @see java.io.InputStream#mark(int) * @see java.io.IOException */ public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } /** * Tests if this input stream supports the <code>mark</code> and * <code>reset</code> methods. Whether or not <code>mark</code> and * <code>reset</code> are supported is an invariant property of a * particular input stream instance. The <code>markSupported</code> method * of <code>InputStream</code> returns <code>false</code>. * * @return <code>true</code> if this stream instance supports the mark * and reset methods; <code>false</code> otherwise. * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return false; } }
FilterInputStream-装饰者超类
package java.io; /** * A <code>FilterInputStream</code> contains * some other input stream, which it uses as * its basic source of data, possibly transforming * the data along the way or providing additional * functionality. The class <code>FilterInputStream</code> * itself simply overrides all methods of * <code>InputStream</code> with versions that * pass all requests to the contained input * stream. Subclasses of <code>FilterInputStream</code> * may further override some of these methods * and may also provide additional methods * and fields. * * @author Jonathan Payne * @since JDK1.0 * 此类包含一些其他的被装饰者的InputStream,用被装饰者的输入流数据作为基础数据,此类几乎没有干什么事儿 只是把被装饰者的依赖关系包进来了 掉用的都是传入进来的被装饰者的方法 */ public class FilterInputStream extends InputStream { /** * 被装饰者 需要被过滤的输入流 读取方法调用的是传入的in的读取方法 */ protected volatile InputStream in; protected FilterInputStream(InputStream in) { this.in = in; } public int read() throws IOException { return in.read(); } public int read(byte b[]) throws IOException { return read(b, 0, b.length); } public int read(byte b[], int off, int len) throws IOException { return in.read(b, off, len); } public long skip(long n) throws IOException { return in.skip(n); } public int available() throws IOException { return in.available(); } public void close() throws IOException { in.close(); } public synchronized void mark(int readlimit) { in.mark(readlimit); } public synchronized void reset() throws IOException { in.reset(); } public boolean markSupported() { return in.markSupported(); } }
ByteArrayInputStream-被装饰者的字节数组输入流实现
package java.io; /** * A <code>ByteArrayInputStream</code> contains * an internal buffer that contains bytes that * may be read from the stream. An internal * counter keeps track of the next byte to * be supplied by the <code>read</code> method. * <p> * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in * this class can be called after the stream has been closed without * generating an <tt>IOException</tt>. * * @author Arthur van Hoff * @see java.io.StringBufferInputStream * @since JDK1.0 * 此实现包含一个内部的字节数组缓冲,用来读取数据流 */ public class ByteArrayInputStream extends InputStream { /** * An array of bytes that was provided * by the creator of the stream. Elements <code>buf[0]</code> * through <code>buf[count-1]</code> are the * only bytes that can ever be read from the * stream; element <code>buf[pos]</code> is * the next byte to be read. * 用于读取输入流的字节数组 */ protected byte buf[]; /** * The index of the next character to read from the input stream buffer. * This value should always be nonnegative * and not larger than the value of <code>count</code>. * The next byte to be read from the input stream buffer * will be <code>buf[pos]</code>. * 下一个待读数组位置 */ protected int pos; /** * The currently marked position in the stream. * ByteArrayInputStream objects are marked at position zero by * default when constructed. They may be marked at another * position within the buffer by the <code>mark()</code> method. * The current buffer position is set to this point by the * <code>reset()</code> method. * <p> * If no mark has been set, then the value of mark is the offset * passed to the constructor (or 0 if the offset was not supplied). * * @since JDK1.1 * 标记回朔地址 */ protected int mark = 0; /** * The index one greater than the last valid character in the input * stream buffer. * This value should always be nonnegative * and not larger than the length of <code>buf</code>. * It is one greater than the position of * the last byte within <code>buf</code> that * can ever be read from the input stream buffer. * 比有效字符(可读取字符)大的索引,默认为数组长度 */ protected int count; /** * Creates a <code>ByteArrayInputStream</code> * so that it uses <code>buf</code> as its * buffer array. * The buffer array is not copied. * The initial value of <code>pos</code> * is <code>0</code> and the initial value * of <code>count</code> is the length of * <code>buf</code>. * * @param buf the input buffer. */ public ByteArrayInputStream(byte buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length; } /** * Creates <code>ByteArrayInputStream</code> * that uses <code>buf</code> as its * buffer array. The initial value of <code>pos</code> * is <code>offset</code> and the initial value * of <code>count</code> is the minimum of <code>offset+length</code> * and <code>buf.length</code>. * The buffer array is not copied. The buffer's mark is * set to the specified offset. * * @param buf the input buffer. * @param offset the offset in the buffer of the first byte to read. * @param length the maximum number of bytes to read from the buffer. */ public ByteArrayInputStream(byte buf[], int offset, int length) { this.buf = buf; this.pos = offset; this.count = Math.min(offset + length, buf.length); this.mark = offset; } /** * Reads the next byte of data from this 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. * <p> * This <code>read</code> method * cannot block. * * @return the next byte of data, or <code>-1</code> if the end of the * stream has been reached. * 这里主要有个0xFF 不知道为何要如此使用? TODO */ public synchronized int read() { return (pos < count) ? (buf[pos++] & 0xff) : -1; } /** * Reads up to <code>len</code> bytes of data into an array of bytes * from this input stream. * If <code>pos</code> equals <code>count</code>, * then <code>-1</code> is returned to indicate * end of file. Otherwise, the number <code>k</code> * of bytes read is equal to the smaller of * <code>len</code> and <code>count-pos</code>. * If <code>k</code> is positive, then bytes * <code>buf[pos]</code> through <code>buf[pos+k-1]</code> * are copied into <code>b[off]</code> through * <code>b[off+k-1]</code> in the manner performed * by <code>System.arraycopy</code>. The * value <code>k</code> is added into <code>pos</code> * and <code>k</code> is returned. * <p> * This <code>read</code> method cannot block. * * @param b the buffer into which the data is read. * @param off the start offset in the destination array <code>b</code> * @param len the maximum number of bytes read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the stream has been reached. * @exception NullPointerException If <code>b</code> is <code>null</code>. * @exception IndexOutOfBoundsException If <code>off</code> is negative, * <code>len</code> is negative, or <code>len</code> is greater than * <code>b.length - off</code> * 按量读取 使用System.arraycopy来进行实现 */ public synchronized int read(byte b[], int off, int len) { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } if (pos >= count) { return -1; } int avail = count - pos; if (len > avail) { len = avail; } if (len <= 0) { return 0; } System.arraycopy(buf, pos, b, off, len); pos += len; return len; } /** * Skips <code>n</code> bytes of input from this input stream. Fewer * bytes might be skipped if the end of the input stream is reached. * The actual number <code>k</code> * of bytes to be skipped is equal to the smaller * of <code>n</code> and <code>count-pos</code>. * The value <code>k</code> is added into <code>pos</code> * and <code>k</code> is returned. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * 跳过的实现 直接移动指针位置pos */ public synchronized long skip(long n) { long k = count - pos; if (n < k) { k = n < 0 ? 0 : n; } pos += k; return k; } /** * Returns the number of remaining bytes that can be read (or skipped over) * from this input stream. * <p> * The value returned is <code>count - pos</code>, * which is the number of bytes remaining to be read from the input buffer. * * @return the number of remaining bytes that can be read (or skipped * over) from this input stream without blocking. */ public synchronized int available() { return count - pos; } /** * Tests if this <code>InputStream</code> supports mark/reset. The * <code>markSupported</code> method of <code>ByteArrayInputStream</code> * always returns <code>true</code>. * * @since JDK1.1 */ public boolean markSupported() { return true; } /** * Set the current marked position in the stream. * ByteArrayInputStream objects are marked at position zero by * default when constructed. They may be marked at another * position within the buffer by this method. * <p> * If no mark has been set, then the value of the mark is the * offset passed to the constructor (or 0 if the offset was not * supplied). * * <p> Note: The <code>readAheadLimit</code> for this class * has no meaning. * * @since JDK1.1 */ public void mark(int readAheadLimit) { mark = pos; } /** * Resets the buffer to the marked position. The marked position * is 0 unless another position was marked or an offset was specified * in the constructor. */ public synchronized void reset() { pos = mark; } /** * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in * this class can be called after the stream has been closed without * generating an <tt>IOException</tt>. */ public void close() throws IOException { } }
BufferedInputStream-装饰者
package java.io; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * support the <code>mark</code> and <code>reset</code> * methods. When the <code>BufferedInputStream</code> * is created, an internal buffer array is * created. As bytes from the stream are read * or skipped, the internal buffer is refilled * as necessary from the contained input stream, * many bytes at a time. The <code>mark</code> * operation remembers a point in the input * stream and the <code>reset</code> operation * causes all the bytes read since the most * recent <code>mark</code> operation to be * reread before new bytes are taken from * the contained input stream. * * @author Arthur van Hoff * @since JDK1.0 * 装饰类 主要在InputStream的基础上 使用一个byte[]来使用缓冲区进行读取 * 操作的单位从原始类的字节 可以灵活的操作缓冲区数据 */ public class BufferedInputStream extends FilterInputStream { //顾名思义 默认缓冲区大小 private static int DEFAULT_BUFFER_SIZE = 8192; /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit * 最大缓冲区大小 受限于某些VM的数组内存分配策略 */ private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; /** * The internal buffer array where the data is stored. When necessary, * it may be replaced by another array of * a different size. * 核心缓冲数组 */ protected volatile byte buf[]; /** * Atomic updater to provide compareAndSet for buf. This is * necessary because closes can be asynchronous. We use nullness * of buf[] as primary indicator that this stream is closed. (The * "in" field is also nulled out on close.) * 保证buf缓冲区的原子操作性 */ private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); /** * The index one greater than the index of the last valid byte in * the buffer. * This value is always * in the range <code>0</code> through <code>buf.length</code>; * elements <code>buf[0]</code> through <code>buf[count-1] * </code>contain buffered input data obtained * from the underlying input stream. */ protected int count; /** * The current position in the buffer. This is the index of the next * character to be read from the <code>buf</code> array. * <p> * This value is always in the range <code>0</code> * through <code>count</code>. If it is less * than <code>count</code>, then <code>buf[pos]</code> * is the next byte to be supplied as input; * if it is equal to <code>count</code>, then * the next <code>read</code> or <code>skip</code> * operation will require more bytes to be * read from the contained input stream. * * @see java.io.BufferedInputStream#buf */ protected int pos; /** * The value of the <code>pos</code> field at the time the last * <code>mark</code> method was called. * <p> * This value is always * in the range <code>-1</code> through <code>pos</code>. * If there is no marked position in the input * stream, this field is <code>-1</code>. If * there is a marked position in the input * stream, then <code>buf[markpos]</code> * is the first byte to be supplied as input * after a <code>reset</code> operation. If * <code>markpos</code> is not <code>-1</code>, * then all bytes from positions <code>buf[markpos]</code> * through <code>buf[pos-1]</code> must remain * in the buffer array (though they may be * moved to another place in the buffer array, * with suitable adjustments to the values * of <code>count</code>, <code>pos</code>, * and <code>markpos</code>); they may not * be discarded unless and until the difference * between <code>pos</code> and <code>markpos</code> * exceeds <code>marklimit</code>. * * @see java.io.BufferedInputStream#mark(int) * @see java.io.BufferedInputStream#pos */ protected int markpos = -1; /** * The maximum read ahead allowed after a call to the * <code>mark</code> method before subsequent calls to the * <code>reset</code> method fail. * Whenever the difference between <code>pos</code> * and <code>markpos</code> exceeds <code>marklimit</code>, * then the mark may be dropped by setting * <code>markpos</code> to <code>-1</code>. * * @see java.io.BufferedInputStream#mark(int) * @see java.io.BufferedInputStream#reset() */ protected int marklimit; /** * Check to make sure that underlying input stream has not been * nulled out due to close; if not return it; */ private InputStream getInIfOpen() throws IOException { InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; } /** * Check to make sure that buffer has not been nulled out due to * close; if not return it; */ private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer; } /** * Creates a <code>BufferedInputStream</code> * and saves its argument, the input stream * <code>in</code>, for later use. An internal * buffer array is created and stored in <code>buf</code>. * * @param in the underlying input stream. */ public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); } /** * Creates a <code>BufferedInputStream</code> * with the specified buffer size, * and saves its argument, the input stream * <code>in</code>, for later use. An internal * buffer array of length <code>size</code> * is created and stored in <code>buf</code>. * * @param in the underlying input stream. * @param size the buffer size. * @exception IllegalArgumentException if {@code size <= 0}. */ public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } /** * Fills the buffer with more data, taking into account * shuffling and other tricks for dealing with marks. * Assumes that it is being called by a synchronized method. * This method also assumes that all data has already been read in, * hence pos > count. * 从原始输入流往缓冲区填充数据 */ private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0) pos = 0; /* no mark: throw away the buffer */ else if (pos >= buffer.length) /* no room left in buffer */ if (markpos > 0) { /* can throw away early part of the buffer */ int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ } else if (buffer.length >= MAX_BUFFER_SIZE) { throw new OutOfMemoryError("Required array size too large"); } else { /* grow buffer */ int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } /** * See * the general contract of the <code>read</code> * method of <code>InputStream</code>. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @exception IOException if this input stream has been closed by * invoking its {@link #close()} method, * or an I/O error occurs. * @see java.io.FilterInputStream#in */ public synchronized int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; } /** * Read characters into a portion of an array, reading from the underlying * stream at most once if necessary. */ private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { /* If the requested length is at least as large as the buffer, and if there is no mark/reset activity, do not bother to copy the bytes into the local buffer. In this way buffered streams will cascade harmlessly. */ if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); } fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; } /** * Reads bytes from this byte-input stream into the specified byte array, * starting at the given offset. * * <p> This method implements the general contract of the corresponding * <code>{@link InputStream#read(byte[], int, int) read}</code> method of * the <code>{@link InputStream}</code> class. As an additional * convenience, it attempts to read as many bytes as possible by repeatedly * invoking the <code>read</code> method of the underlying stream. This * iterated <code>read</code> continues until one of the following * conditions becomes true: <ul> * * <li> The specified number of bytes have been read, * * <li> The <code>read</code> method of the underlying stream returns * <code>-1</code>, indicating end-of-file, or * * <li> The <code>available</code> method of the underlying stream * returns zero, indicating that further input requests would block. * * </ul> If the first <code>read</code> on the underlying stream returns * <code>-1</code> to indicate end-of-file then this method returns * <code>-1</code>. Otherwise this method returns the number of bytes * actually read. * * <p> Subclasses of this class are encouraged, but not required, to * attempt to read as many bytes as possible in the same fashion. * * @param b destination buffer. * @param off offset at which to start storing bytes. * @param len maximum number of bytes to read. * @return the number of bytes read, or <code>-1</code> if the end of * the stream has been reached. * @exception IOException if this input stream has been closed by * invoking its {@link #close()} method, * or an I/O error occurs. */ public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } /** * See the general contract of the <code>skip</code> * method of <code>InputStream</code>. * * @exception IOException if the stream does not support seek, * or if this input stream has been closed by * invoking its {@link #close()} method, or an * I/O error occurs. */ public synchronized long skip(long n) throws IOException { getBufIfOpen(); // Check for closed stream if (n <= 0) { return 0; } long avail = count - pos; if (avail <= 0) { // If no mark position set then don't keep in buffer if (markpos <0) return getInIfOpen().skip(n); // Fill in buffer to save bytes for reset fill(); avail = count - pos; if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; return skipped; } /** * Returns an estimate of the number of bytes that can be read (or * skipped over) from this input stream without blocking by the next * invocation of a method for this input stream. The next invocation might be * the same thread or another thread. A single read or skip of this * many bytes will not block, but may read or skip fewer bytes. * <p> * This method returns the sum of the number of bytes remaining to be read in * the buffer (<code>count - pos</code>) and the result of calling the * {@link java.io.FilterInputStream#in in}.available(). * * @return an estimate of the number of bytes that can be read (or skipped * over) from this input stream without blocking. * @exception IOException if this input stream has been closed by * invoking its {@link #close()} method, * or an I/O error occurs. */ public synchronized int available() throws IOException { int n = count - pos; int avail = getInIfOpen().available(); return n > (Integer.MAX_VALUE - avail) ? Integer.MAX_VALUE : n + avail; } /** * See the general contract of the <code>mark</code> * method of <code>InputStream</code>. * * @param readlimit the maximum limit of bytes that can be read before * the mark position becomes invalid. * @see java.io.BufferedInputStream#reset() */ public synchronized void mark(int readlimit) { marklimit = readlimit; markpos = pos; } /** * See the general contract of the <code>reset</code> * method of <code>InputStream</code>. * <p> * If <code>markpos</code> is <code>-1</code> * (no mark has been set or the mark has been * invalidated), an <code>IOException</code> * is thrown. Otherwise, <code>pos</code> is * set equal to <code>markpos</code>. * * @exception IOException if this stream has not been marked or, * if the mark has been invalidated, or the stream * has been closed by invoking its {@link #close()} * method, or an I/O error occurs. * @see java.io.BufferedInputStream#mark(int) */ public synchronized void reset() throws IOException { getBufIfOpen(); // Cause exception if closed if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } /** * Tests if this input stream supports the <code>mark</code> * and <code>reset</code> methods. The <code>markSupported</code> * method of <code>BufferedInputStream</code> returns * <code>true</code>. * * @return a <code>boolean</code> indicating if this stream type supports * the <code>mark</code> and <code>reset</code> methods. * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ public boolean markSupported() { return true; } /** * Closes this input stream and releases any system resources * associated with the stream. * Once the stream has been closed, further read(), available(), reset(), * or skip() invocations will throw an IOException. * Closing a previously closed stream has no effect. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } // Else retry in case a new buf was CASed in fill() } } }
FileInputStream-从文件读书数据的流
package java.io; import java.nio.channels.FileChannel; import sun.nio.ch.FileChannelImpl; //此类的读取实现和一些其他操作 多依赖于c/c++的native本地方法实现 public class FileInputStream extends InputStream { /* File Descriptor - handle to the open file */ //文件描述句柄 private final FileDescriptor fd; /** * The path of the referenced file * (null if the stream is created with a file descriptor) */ //文件路径 private final String path; private FileChannel channel = null; //关闭锁对象 private final Object closeLock = new Object(); private volatile boolean closed = false; /** * Creates a <code>FileInputStream</code> by * opening a connection to an actual file, * the file named by the path name <code>name</code> * in the file system. A new <code>FileDescriptor</code> * object is created to represent this file * connection. * <p> * First, if there is a security * manager, its <code>checkRead</code> method * is called with the <code>name</code> argument * as its argument. * <p> * If the named file does not exist, is a directory rather than a regular * file, or for some other reason cannot be opened for reading then a * <code>FileNotFoundException</code> is thrown. * * @param name the system-dependent file name. * @exception FileNotFoundException if the file does not exist, * is a directory rather than a regular file, * or for some other reason cannot be opened for * reading. * @exception SecurityException if a security manager exists and its * <code>checkRead</code> method denies read access * to the file. * @see java.lang.SecurityManager#checkRead(java.lang.String) * 打开一个文件连接 */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } /** * Creates a <code>FileInputStream</code> by * opening a connection to an actual file, * the file named by the <code>File</code> * object <code>file</code> in the file system. * A new <code>FileDescriptor</code> object * is created to represent this file connection. * <p> * First, if there is a security manager, * its <code>checkRead</code> method is called * with the path represented by the <code>file</code> * argument as its argument. * <p> * If the named file does not exist, is a directory rather than a regular * file, or for some other reason cannot be opened for reading then a * <code>FileNotFoundException</code> is thrown. * * @param file the file to be opened for reading. * @exception FileNotFoundException if the file does not exist, * is a directory rather than a regular file, * or for some other reason cannot be opened for * reading. * @exception SecurityException if a security manager exists and its * <code>checkRead</code> method denies read access to the file. * @see java.io.File#getPath() * @see java.lang.SecurityManager#checkRead(java.lang.String) */ public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } fd = new FileDescriptor(); fd.attach(this); path = name; open(name); } /** * Creates a <code>FileInputStream</code> by using the file descriptor * <code>fdObj</code>, which represents an existing connection to an * actual file in the file system. * <p> * If there is a security manager, its <code>checkRead</code> method is * called with the file descriptor <code>fdObj</code> as its argument to * see if it's ok to read the file descriptor. If read access is denied * to the file descriptor a <code>SecurityException</code> is thrown. * <p> * If <code>fdObj</code> is null then a <code>NullPointerException</code> * is thrown. * <p> * This constructor does not throw an exception if <code>fdObj</code> * is {@link java.io.FileDescriptor#valid() invalid}. * However, if the methods are invoked on the resulting stream to attempt * I/O on the stream, an <code>IOException</code> is thrown. * * @param fdObj the file descriptor to be opened for reading. * @throws SecurityException if a security manager exists and its * <code>checkRead</code> method denies read access to the * file descriptor. * @see SecurityManager#checkRead(java.io.FileDescriptor) */ public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; path = null; /* * FileDescriptor is being shared by streams. * Register this stream with FileDescriptor tracker. */ fd.attach(this); } /** * Opens the specified file for reading. * @param name the name of the file */ private native void open0(String name) throws FileNotFoundException; // wrap native call to allow instrumentation /** * Opens the specified file for reading. * @param name the name of the file */ private void open(String name) throws FileNotFoundException { open0(name); } /** * Reads a byte of data from this input stream. This method blocks * if no input is yet available. * * @return the next byte of data, or <code>-1</code> if the end of the * file is reached. * @exception IOException if an I/O error occurs. * read调用的read0调用的本地读取方法 */ public int read() throws IOException { return read0(); } private native int read0() throws IOException; /** * Reads a subarray as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data * @param len the number of bytes that are written * @exception IOException If an I/O error has occurred. */ private native int readBytes(byte b[], int off, int len) throws IOException; /** * Reads up to <code>b.length</code> bytes of data from this input * stream into an array of bytes. This method blocks until some input * is available. * * @param b the buffer into which the data is read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the file has been reached. * @exception IOException if an I/O error occurs. */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * Reads up to <code>len</code> bytes of data from this input stream * into an array of bytes. If <code>len</code> is not zero, the method * blocks until some input is available; otherwise, no * bytes are read and <code>0</code> is returned. * * @param b the buffer into which the data is read. * @param off the start offset in the destination array <code>b</code> * @param len the maximum number of bytes read. * @return the total number of bytes read into the buffer, or * <code>-1</code> if there is no more data because the end of * the file has been reached. * @exception NullPointerException If <code>b</code> is <code>null</code>. * @exception IndexOutOfBoundsException If <code>off</code> is negative, * <code>len</code> is negative, or <code>len</code> is greater than * <code>b.length - off</code> * @exception IOException if an I/O error occurs. */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } /** * Skips over and discards <code>n</code> bytes of data from the * input stream. * * <p>The <code>skip</code> method may, for a variety of * reasons, end up skipping over some smaller number of bytes, * possibly <code>0</code>. If <code>n</code> is negative, the method * will try to skip backwards. In case the backing file does not support * backward skip at its current position, an <code>IOException</code> is * thrown. The actual number of bytes skipped is returned. If it skips * forwards, it returns a positive value. If it skips backwards, it * returns a negative value. * * <p>This method may skip more bytes than what are remaining in the * backing file. This produces no exception and the number of bytes skipped * may include some number of bytes that were beyond the EOF of the * backing file. Attempting to read from the stream after skipping past * the end will result in -1 indicating the end of the file. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * @exception IOException if n is negative, if the stream does not * support seek, or if an I/O error occurs. */ public native long skip(long n) throws IOException; /** * Returns an estimate of the number of remaining bytes that can be read (or * skipped over) from this input stream without blocking by the next * invocation of a method for this input stream. Returns 0 when the file * position is beyond EOF. The next invocation might be the same thread * or another thread. A single read or skip of this many bytes will not * block, but may read or skip fewer bytes. * * <p> In some cases, a non-blocking read (or skip) may appear to be * blocked when it is merely slow, for example when reading large * files over slow networks. * * @return an estimate of the number of remaining bytes that can be read * (or skipped over) from this input stream without blocking. * @exception IOException if this file input stream has been closed by calling * {@code close} or an I/O error occurs. */ public native int available() throws IOException; /** * Closes this file input stream and releases any system resources * associated with the stream. * * <p> If this stream has an associated channel then the channel is closed * as well. * * @exception IOException if an I/O error occurs. * * @revised 1.4 * @spec JSR-51 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { close0(); } }); } /** * Returns the <code>FileDescriptor</code> * object that represents the connection to * the actual file in the file system being * used by this <code>FileInputStream</code>. * * @return the file descriptor object associated with this stream. * @exception IOException if an I/O error occurs. * @see java.io.FileDescriptor */ public final FileDescriptor getFD() throws IOException { if (fd != null) { return fd; } throw new IOException(); } /** * Returns the unique {@link java.nio.channels.FileChannel FileChannel} * object associated with this file input stream. * * <p> The initial {@link java.nio.channels.FileChannel#position() * position} of the returned channel will be equal to the * number of bytes read from the file so far. Reading bytes from this * stream will increment the channel's position. Changing the channel's * position, either explicitly or by reading, will change this stream's * file position. * * @return the file channel associated with this file input stream * * @since 1.4 * @spec JSR-51 */ public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, path, true, false, this); } return channel; } } private static native void initIDs(); private native void close0() throws IOException; static { initIDs(); } /** * Ensures that the <code>close</code> method of this file input stream is * called when there are no more references to it. * * @exception IOException if an I/O error occurs. * @see java.io.FileInputStream#close() */ protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } } }