javaIO(2): InputStream和FileInputStream源码
文章目录
InputStream源码
package java.io;
/**
* 此抽象类是表示字节输入流的所有类的超类。
* 需要定义 InputStream 子类的应用程序必须总是提供返回下一个输入字节的方法。
*/
public abstract class InputStream implements Closeable {
// 静态常量,用于skip()方法
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
/**
* 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。
* 如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
* 在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
* 子类必须提供此方法的一个实现。
*/
public abstract int read() throws IOException;
/**
* 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
* 返回读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -1。
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* 将输入流中最多 len 个数据字节读入 byte 数组。
* 返回读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -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) { // 如果 len 为 0,则不读取任何字节并返回 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(); // read(b, off, len)方法重复调用方法read()
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
/**
* 跳过和丢弃此输入流中数据的 n 个字节。
* 返回跳过的实际字节数。
*/
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;
}
/**
* 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
*/
public int available() throws IOException {
return 0;
}
/**
* 关闭此输入流并释放与该流关联的所有系统资源。
*/
public void close() throws IOException {}
/**
* 在此输入流中标记当前的位置。
* 对 reset 方法的后续调用会在最后标记的位置重新定位此流,以便后续读取重新读取相同的字节。
*/
public synchronized void mark(int readlimit) {}
/**
* 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
/**
* 测试此输入流是否支持 mark 和 reset 方法
*/
public boolean markSupported() {
return false;
}
}
FileInputStream源码
FileInputStream是InputStream的直接子类,其特性是专门从文件中创建输入流。
但是,FileInputStream方法底层都调用了native方法,除非去看native方法的源码,不然其源码挺简单的。
package java.io;
import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;
/**
* FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。
* FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
*/
public class FileInputStream extends InputStream
{
/* 用于打开文件的文件描述符 */
private final FileDescriptor fd;
/**
* 文件路径
*/
private final String path;
private FileChannel channel = null;
private final Object closeLock = new Object();
private volatile boolean closed = false;
/**
* 构造方法1,传入字符串构造File对象,再调用构造方法2
*/
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
/**
* 构造方法2,传入File对象进行构造。
*/
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); // 调用native方法打开文件
}
/**
* 构造方法3,通过文件描述符进行构造
*/
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
fd.attach(this);
}
/**
* native方法打开文件以读取其内容
*/
private native void open0(String name) throws FileNotFoundException;
/**
* 构造方法调用open,open调用open0
*/
private void open(String name) throws FileNotFoundException {
open0(name);
}
/**
* 从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞。
* 底层还是调用native方法。
*/
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException; // native方法,一次读取一个字节
/**
* native方法,用于将数据读入字节数组
*/
private native int readBytes(byte b[], int off, int len) throws IOException;
/**
* 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
* 在某些输入可用之前,此方法将阻塞。
* 底层调用native方法
*/
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
/**
* 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
* 如果 len 不为 0,则在输入可用之前,该方法将阻塞;否则,不读取任何字节并返回 0。
* 底层调用native方法
*/
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
/**
* 实现父类的方法,不过是native方法
*/
public native long skip(long n) throws IOException;
/**
* 覆盖父类方法,不过是native方法
*/
public native int available() throws IOException;
/**
* 重写父类方法,底层调用native方法
*/
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();
}
});
}
/**
* 返回表示到文件系统中实际文件的连接的 FileDescriptor 对象
*/
public final FileDescriptor getFD() throws IOException {
if (fd != null) {
return fd;
}
throw new IOException();
}
/**
* 返回与此文件输入流有关的唯一 FileChannel 对象。
*/
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(); // 静态代码块,随类的加载而执行,调用了native方法。
}
/**
* 确保不再引用文件输入流时调用其 close 方法。
*/
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
close();
}
}
}
InputStream其他的直接子类,实现了特定的read()方式。如下:
- AudioInputStream:音频输入流是具有指定音频格式和长度的输入流。
- ByteArrayInputStream:包含一个内部缓冲区,该缓冲区包含从流中读取的字节。
- ObjectInputStream:ObjectInputStream 对以前使用 ObjectOutputStream
写入的基本数据和对象进行反序列化。 - SequenceInputStream:SequenceInputStream 表示其他输入流的逻辑串联。
- …
FilterInputStream,抽象装饰者类
FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
其代码同FilterOutputStream一样,没什么具体功能,只是持有父类InputStream对象,并调用父类的同名方法。
// 部分源码
public class FilterInputStream extends InputStream {
/**
* 持有父类对象
*/
protected volatile InputStream in;
/**
* 构造方法传入父类型对象,当然可以是InputStream的直接子类对象
*/
protected FilterInputStream(InputStream in) {
this.in = in;
}
// 方法均调用父类的同名方法
FilterInputStream的子类,即具体装饰者类
FilterInputStream的子类继承自FilterInputStream,自然也就拿到了父类的in对象,然后在本类的同名方法中,可对in对象的方法进行“装饰”,即功能增强。下面分析BufferedInputStream的源码。
package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192; // 默认buffer大小
/**
* 最大容量
*/
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
/**
* buffer缓冲区
*/
protected volatile byte buf[];
/**
* 缓存数组的原子更新器。
* 该成员变量与buf数组的volatile关键字共同组成了buf数组的原子更新功能实现。
*/
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
/**
* buffer中的有效字节数
*/
protected int count;
/**
* buffer中的当前位置
*/
protected int pos;
/**
* mark标记位置
*/
protected int markpos = -1;
/**
* mark最大标记位置
*/
protected int marklimit;
/**
* 检查流是否开启
*/
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
/**
* 检查buffer是否为空
*/
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
/**
* 构造方法1,传入InputStream类型“被装饰者”对象,调用构造方法2
*/
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
/**
* 构造方法2,传入"被装饰者"对象,并构造
*/
public BufferedInputStream(InputStream in, int size) {
super(in); // 调用父类的构造方法
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size]; // 初始化buffer
}
/**
* 填充方法,将流中的数据写入到buffer中
*/
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;
}
/**
* read()方法底层仍调用了InputStream类型的read()方法,
* 不过对其进行了"装饰",即使用了缓冲区技术,从而提高了效率。
*/
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
/**
* 读入字节数组的方法,供read(byte b[], int off, int len)方法调用
* 底层仍调用InputStream的read()方法
*/
private int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos;
if (avail <= 0) {
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;
}
/**
* read缓冲区方法,调用read1,对InputStream的read方法进行了"缓冲区装饰"
*/
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;
}
}
/**
* 仍然是对原方法的"装饰"
*/
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;
}
/**
* 装饰方法
*/
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;
}
/**
* 装饰方法
*/
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
}
/**
* 装饰方法
*/
public synchronized void reset() throws IOException {
getBufIfOpen(); // Cause exception if closed
if (markpos < 0)
throw new IOException("Resetting to invalid mark");
pos = markpos;
}
/**
* 测试是否有mark,reset方法支持
*/
public boolean markSupported() {
return true;
}
/**
* 仍然是对原close()方法的装饰
*/
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()
}
}
}
FilterInputStream的其他子类。
同样有各自的装饰内容,用来装饰InputStream类型对象的方法。
- DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
- PushbackInputStream :为另一个输入流添加性能,即“推回 (push back)”或“取消读取 (unread)”一个字节的能力。
- ….