JAVA网络编程-流
OutputStream源码
InputStream源码
FileOutputStream源码
FileInputStream源码
ByteArrayOutputStream源码
ByteArrayInputStream源码
FilterOutputStream源码
FilterInputStream源码
BufferedOutputStream源码
BufferedInputStream源码
Writer源码
Reader源码
StringWriter源码
StringReader源码
CharArrayWriter源码
CharArrayReader源码
BufferedWriter源码
BufferedReader源码
OutputStream源码
作为字节输出流的父类,OutputStream中定义了字节输出流的基本功能三个write本质上都是一次往目标中写入一个字节.flush和close则没有实现.
public abstract void write(int b) throws IOException;//这是主要方法下边两个write最终都是调用这个本地方法
public void write(byte b[]) throws IOException {
write(b, 0, b.length);//调用下边的
}
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)) {//off,len两个参数检验逻辑不麻烦
throw new IndexOutOfBoundsException();
} else if (len == 0) {//写入长度0返回
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);//循环从字节数组的偏移量开始一个一个写.
}
}
public void flush() throws IOException {
}
public void close() throws IOException {
}
InputStream源码
InputStream作为字节输入流的父类,定义了基本的读取方法,三个read方法最终都是调用本地方法一个字节一个字节读取的.跳过则是将需要跳过的先读出来,造成跳过的假象.标记和返回则未实现.
public abstract int read() throws IOException; public int read(byte b[]) throws IOException { return read(b, 0, b.length);//调用下边构造函数 } 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;//注意!如果要输出的长度是0则返回0也不会返回-1这里会有坑的 } int c = read(); if (c == -1) { return -1;//这里还是调用本地方法一次读一个字节,-1在这里产生 } b[off] = (byte)c;//如果不是-1则将读取的字节存到数组中 int i = 1;//已经读到一个了. try { for (; i < len ; i++) { c = read(); if (c == -1) { break;//循环读,-1停止 } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
private static final int MAX_SKIP_BUFFER_SIZE = 2048; 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);//跳过的字节不能大于2048 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 {} //查看是否支持mark和reset这连个方法 public boolean markSupported() { return false; } //这一对方法mark标记当前读取的位置,reset回退到标记点 //标记点只能有一个,后来的会覆盖前一个 public synchronized void mark(int readlimit) {} //返回上一个标记点 public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); }
FileOutputStream源码
FileOutputStream继承自OutputStream将提供的源数据写入到目标文件中,两种模式1追加2覆盖所以在类中可以看到open打开文件,close关闭文件这些都是本地方法.写入则有一次写一个字节和一次写一个数组两种方式.其父类OutputStream本质上都是一个字节一个字节写入.
getChannel则是获取文件对应的管道,供NIO使用.需要注意的是FileOutputStream并没有flush方法,而是沿用的父类的空实现这意味着FileOutputStream在写入数据时并不需要刷新流.
public FileOutputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, false);
}
public FileOutputStream(String name, boolean append)
throws FileNotFoundException
{
this(name != null ? new File(name) : null, append);
}
public FileOutputStream(File file) throws FileNotFoundException {
this(file, false);
}
//上边三个都是调用的这个,参数为file,false,append的值如果为true则表示在文件内容后追加,false则会覆盖
public FileOutputStream(File file, boolean append)
throws FileNotFoundException
{
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(name);//查询文件是否可写,此处有疑问.
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {//检查路径是否合法
throw new FileNotFoundException("Invalid file path");
}
this.fd = new FileDescriptor();//创建路径对应文件的描述符
fd.attach(this);
this.append = append;
this.path = name;
open(name, append);//调用的是open0本地方法
}
private native void open0(String name, boolean append) throws FileNotFoundException;//打开指定名称的文件,以便追加或者覆盖
//这个构造器是没用过,琢磨了半天想了一个脱裤子放屁的写法.
//FileDescriptor fd = new FileOutputStream("D:\\11.txt").getFD();
//FileOutputStream out = new FileOutputStream(fd);
public FileOutputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkWrite(fdObj);
}
this.fd = fdObj;
this.append = false;
this.path = null;
fd.attach(this);
}
出现的问题
启动时带上这个参数可以启用安全管理器
-Djava.security.manager
File f = new File("D:\\11.txt");
String path = f.getPath();
//启动安全管理器后security才不为null
SecurityManager security = System.getSecurityManager();
security.checkWrite(path);//但这个检验会报错,但是明明文件可写
public void write(int b) throws IOException {
write(b, append);
}
//将指定的字节写入此文件输出流。write(int b)专用
private native void write(int b, boolean append) throws IOException;
public void write(byte b[]) throws IOException {
writeBytes(b, 0, b.length, append);
}
public void write(byte b[],int off,int len) throws IOException {
writeBytes(b, 0, b.length, append);
}
//以字节数组的形式写入子数组。write(byte b[]) write(byte b[],int off,int len)专用
//b 要写入的数据;off 数据中的起始偏移量; len 写入的字节数;append 覆盖或追加
private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException;
//NIO的API会使用
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, false, true, append, this);
}
return channel;
}
}
private volatile boolean closed = false;
public void close() throws IOException {
synchronized (closeLock) {//同步块
if (closed) {
return;
}
closed = true;//调用第二次就直接return了,只能close一次
}
if (channel != null) {
channel.close();//如果有开启通道就关闭
}
fd.closeAll(new Closeable() {//关闭文件描述符
public void close() throws IOException {
close0();
}
});
}
private native void close0() throws IOException;//关闭流
FileInputStream源码
FileInputStream继承自InputStream从目标文件中读取字节,两种读取方式1一次读一个字节2一次读N个字节放入字节数组中.skip方法则是native方法,并不是通过读取跳过的部分的假跳过.close只能关闭一次,FileInputStream并未提供mark和reset的实现
public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null);//调用下边的构造方法 } 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);//打开文件 } //这个构造函数和FileOutputStream一样,不会用.. 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); }
private native void open0(String name) throws FileNotFoundException;//打开文件
public int read() throws IOException { return read0(); } private native int read0() throws IOException;//从目标文件中一次读取一个字节 public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } private native int readBytes(byte b[], int off, int len) throws IOException;//从目标文件中一次读取len个字节放入字节数组中,从off的位置开始放.
public long skip(long n) throws IOException { return skip0(n); } private native long skip0(long n) throws IOException;//这个跳过方法是本地方法,效率肯定比InputStream的假跳过要好
public int available() throws IOException {//返回此输出流中可读取或可跳过的字节数的预估值. return available0(); } private native int available0() throws IOException; public FileChannel getChannel() { synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, path, true, false, this); } return channel; } }
public FileChannel getChannel() {//获取通道NIO可以使用 synchronized (this) { if (channel == null) { channel = FileChannelImpl.open(fd, path, true, false, this); } return channel; } }
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(); } }); } private native void close0() throws IOException;
ByteArrayOutputStream源码
ByteArrayOutputStream是OutputStream的子类,ByteArrayOutputStream将源数据写入到自己内部的byte数组中,并提供了自动对数组的扩容,因为存储的容器是内部数组,所以ByteArrayOutputStream不需要flush也不需要close.另外该类中大部分方法都带有synchroized关键字是线程安全的。
protected byte buf[];
public ByteArrayOutputStream() {
this(32);//默认数组大小32个子节
}
public ByteArrayOutputStream(int size) {//可以指定子节数组大小
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];//创建子节数组
}
protected int count;//默认0,记录已经写入的子节个数
public synchronized void write(int b) {
ensureCapacity(count + 1);//扩容
buf[count] = (byte) b;将子节写入到指定下标
count += 1;//长度自增
}
private void ensureCapacity(int minCapacity) {
//下标+1-数组容量 > 0 扩容,一个子节都装不下了才扩容
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 数组长度 初始值32
int oldCapacity = buf.length;
//原数组长度的二倍 64
int newCapacity = oldCapacity << 1;
//扩容后的容量不如数组长度+1则使用数组长度+1有负数的情况
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
buf = Arrays.copyOf(buf, newCapacity);//扩容数组
}
//小于0异常大于int最大值-8则使用int最大值,否则使用int最大值-8
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//一次写入一个字节数组 参数 b 数据源 off 从off开始拷贝 len 拷贝字节数
public synchronized void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
//以上4中情况为了保证健壮性
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);//扩容
System.arraycopy(b, off, buf, count, len);//复制数组到buf
count += len;//长度加上len
}
//将该输出流中的字节流中的子节拷贝到指定的输出流
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write("你好".getBytes());
ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
bos.writeTo(bos2);
System.out.println(new String(bos2.toByteArray()));
}
//将输出流中的子节数组返回,注意这个返回仅仅返回count个子节
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
//如果将count置为0然后调用toByteArray则会返回一个空数组
public synchronized void reset() {
count = 0;
}
//字节流输出流不需要关闭
public void close() throws IOException {
}
//返回String
public synchronized String toString() {
return new String(buf, 0, count);
}
//返回String,按照指定编码
public synchronized String toString(String charsetName)
throws UnsupportedEncodingExceptionx{
return new String(buf, 0, count, charsetName);
}
ByteArrayInputStream源码
ByteArrayInputStream是InputStream的子类,它从指定的字节数组中读取数据,两种模式1读取单个字节通过下标移动获取字节2从off开始读取len个字节保存在目标数组中,通过拷贝数组实现.skip,mark,reset方法都是通过pos,count数组的位置和长度进行移动
实现的.ByteArrayInputStream数据源和存储容器都是数组所以不需要close.另外该类中大部分方法都带有synchroized关键字是线程安全的。
protected byte buf[];//要读取的字节数组 protected int pos;//下次可读取的偏移量 protected int mark = 0;//标记位,reset返回的位置 protected int count;//可读数组的总长度 public ByteArrayInputStream(byte buf[]) {//传入要读取的数据源数组 this.buf = buf; this.pos = 0; this.count = buf.length; } public ByteArrayInputStream(byte buf[], int offset, int length) {//传入要读取的数据源数组,但只能读取off-len之间的元素 this.buf = buf; this.pos = offset; this.count = Math.min(offset + length, buf.length); this.mark = offset; }
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;//如果偏移量位置小于数组总长度,则返回对应下标的字节,否则返回-1
}
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;//读取完毕返回-1
}
int avail = count - pos;
if (len > avail) {
len = avail;//就算传入的len大于可读数组的长度也不会报错,最大读取长度为总长度-下一次的位置.
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);//复制数组
pos += len;//改变偏移量
return len;//返回读取的字节数
}
public synchronized long skip(long n) { long k = count - pos; if (n < k) { k = n < 0 ? 0 : n; } //先获取剩余可读取的字节数量,如果要跳过的字节数小于剩余字节数并且要跳过的字节大于等于0,则移动pos指针+n,否则不移动pos pos += k; return k; }
public synchronized int available() { return count - pos;//获取可读或可跳过的字节数,好理解,全部字节-已读字节的pos就是剩余可读字节 }
public boolean markSupported() { return true;//允许标记和回退 } public void mark(int readAheadLimit) { mark = pos;//设置标记点 } public synchronized void reset() { pos = mark;//移动pos位置到mark } public void close() throws IOException {//空实现 }
FilterOutputStream源码
FilterOutputStream是OutputStream的直接子类,正如它的类名,FilterOutputStream输出流的过滤器,它的直接子类有 BufferedOutputStream DataOutputStream PrintStream CipherOutputStream等等,需要注意的是FilterOutputStream的子类并不是都在io包下。它们根据自身特性分布在不同的包下。CipherOutputStream在javax.crypto包下。
protected OutputStream out; public FilterOutputStream(OutputStream out) { this.out = out;//过滤器流必须有一个基础的OutputStream流 } public void write(int b) throws IOException { out.write(b);//下边两个write方法最终还是走的这个,而这个方法则使用的是基础流的方法。 } public void write(byte b[]) throws IOException { write(b, 0, b.length); } public void write(byte b[], int off, int len) throws IOException { if ((off | len | (b.length - (len + off)) | (off + len)) < 0) throw new IndexOutOfBoundsException(); for (int i = 0 ; i < len ; i++) { write(b[off + i]); } } public void flush() throws IOException { out.flush();//基础流的刷新 } @SuppressWarnings("try") public void close() throws IOException { try (OutputStream ostream = out) {//这里使用了try-close语句,自动关闭流。 flush(); } }
FilterInputStream源码
FilterInputStream是InputStream的直接子类它是输入流的过滤器,它的直接子类有 BufferedInputStream DataInputStream CipherInputStream。
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();//调用基础类的实现 }
BufferedOutputStream源码
BufferedOutputStream是FilterOutputStream的子类,它的内部持有一个byte数组作为输出流缓冲区,每次写入的数据首先会写入到缓冲区内,缓冲区内的数据会在一定条件下自动写入的基础流中,除非可以保证写入的数据一定会触发自动刷新,否则需要手动调用flush刷新缓冲区。另外该类中大部分方法都带有synchroized关键字是线程安全的。
protected byte buf[];//作为缓冲区的子节数组 protected int count;//默认值0记录当前缓冲区中已有的字节数 public BufferedOutputStream(OutputStream out) {//接收基础输出流 this(out, 8192);//默认缓冲区8192 }
public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) {//如果设置的缓冲区长度小于0异常 throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size];//开辟缓冲区数组。 }
private void flushBuffer() throws IOException { if (count > 0) {//如果当前缓冲区中的子节大于0则调用基础流的write,并将count记录值重置为0 out.write(buf, 0, count); count = 0; } } public synchronized void write(int b) throws IOException { if (count >= buf.length) { flushBuffer();//写入当个子节,如果缓冲区已有字节数大于等于缓冲区长度则刷新。 } buf[count++] = (byte)b;//写入缓冲区,count自增 } public synchronized void write(byte b[], int off, int len) throws IOException { if (len >= buf.length) {//要写入的长度大于缓冲区的总长度,那就直接属性缓冲区,先将缓冲区内数据写入到基础流,然后在直接写入基础流,不使用缓冲区。 flushBuffer(); out.write(b, off, len); return; } if (len > buf.length - count) { flushBuffer();//如果要写入的长度大于当前缓冲区可用长度也要刷新 } System.arraycopy(b, off, buf, count, len);//拷贝数组,写入到缓冲区 count += len;//count增加 } public synchronized void flush() throws IOException { flushBuffer();//如果写入的子节数没用触发缓冲区的刷新方法就必须要手动调用flush。 out.flush(); }
BufferedInputStream源码
BufferedInputStream是FilterInputStream的子类,它的内部持有一个byte数组作为输入流缓冲区,每次读取数据时先读取缓冲区中的,如果缓冲区没有数据,则缓冲区会读取基础流中的数据,每次读多少个根据设置的BUFFER_SIZE为准。缓冲区的操作比较负责的步骤在于从基础流中填充数据到缓冲区。另外该类中大部分方法都带有synchroized关键字是线程安全的。
private static int DEFAULT_BUFFER_SIZE = 8192; public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) {//缓冲区必须大于0 throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size];//创建子节缓冲区 }
protected int count; //缓冲区内里可读子节数
protected int pos;//下一个可读子节的pos
public synchronized int read() throws IOException {
if (pos >= count) {
fill();//缓冲区内没有,要去基础流中获取,填充缓冲
if (pos >= count)
return -1;//取不到返回-1
}
return getBufIfOpen()[pos++] & 0xff;//到缓冲区中获取一个子节,pos为下标
}
private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer;//获取缓冲数组 }
protected int count; //缓冲区内里可读子节数
protected int pos;//下一个可读子节的pos
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;//缓冲区最大长度
protected int markpos = -1;//mark标记位
protected int marklimit;//mark标记后多少位后不保存。很重要
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0) //a
pos = 0;
else if (pos >= buffer.length)//b
if (markpos > 0) {//b1
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {//b2
markpos = -1;
pos = 0;
} else if (buffer.length >= MAX_BUFFER_SIZE) {//b3
throw new OutOfMemoryError("Required array size too large");
} else {//b4
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)) {
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);//c
if (n > 0)
count = n + pos;
}
扩容操作分为6种情况
1 没有设置markpos标记位,将pos修改为0表示下次从缓冲区的0号下标开始读取。从基础流中读取数据到buffer读取的长度的为buffer的长度,从0号位置开始覆盖。count的值是读取的长度+pos也就是当前缓冲区可读取的数据总长。(对应步骤a-c)
2 设置了markpos标记位,但当前pos位小于buffer的长度。这种情况在第一次读取时会发生,初始化buffer后buffer有长度但pos为0,count也是0.这种情况直接读取基础流(对应步骤c)
3 设置了标记位markpos,当前pos位已经在buffer的末尾表示缓冲区数据已经读完。此时pos位在缓冲区末尾,如果markpos在非0号位置,表示当前缓冲区还是可以存下mark位到pos位的。此时就要拷贝markpos和pos之间的数据到缓冲区中,为了给用户reset使用。
pos-markpos就是要拷贝的长度。拷贝结束后markpos在0位置,pos位则要在被拷贝的数组+1位置。也就是pos-markps。最后在从基础流中读取子节,但当前buffer可用的空间就只剩下buffer.len-pos了。(对应步骤b1-c)
4 如果markpos在0号位,但是marklimit等于或者小于buffer.leng。表示要缓冲区保存的mark长度已经超过了,则不需要复制markpos标记点之后的数据。将markps置为-1,pos置为0.然后从基础流读取数据。(对应步骤b2-c)
5 如果markpos在0号位,但是marklimit大于buffer.leng。表示当前缓冲区内的数据需要全部保存。全部保存的情况肯定是要对缓冲区扩容的,不然当前缓冲区为10,已经读取到了10.用户还要保存10,这样程序异常了。扩容前需要判断如果当前缓冲区长度已经大于等于缓冲区最大容量则异常。
6 如果情况5没有异常则进行缓冲区扩容操作。nsz扩容后的缓冲区长度,如果pos小于缓冲区最大长度-pos缓冲区的新长度为pos*2否则为缓冲区最大长度。如果新的缓冲区长度大于marklimit那么将marklimit修改为nsz(不介意帮你多保存一点)否则说明你要保存的数据比缓冲区扩容后的还多,那就不管了,下次在说。创建一个新的数组,长度为nsz将缓冲区的数据全部拷贝到新容器内。这里显示进行了原子更新,然后又进行了普通的赋值更新,我觉得有些多余了,一个操作就可以了。不知道JDK的作者是如何考虑的。最后进行基础流读取。(对应步骤b4-c)
public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); //检验缓冲区是否存在 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException();//检查下标越界 } else if (len == 0) { return 0;//读取的长度为0则返回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; } } 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; }
一次读取len长度的子节放入字节数组中的方式的流程。这里从read1方法描述
1 avail表示当前可读子节的长度,如果当前可读子节大于0表示缓存去内还有数据,则判断缓存去内的数据长度avail和len。取小值,如果len<avail表示缓冲区内的子节还够本次使用。如果len>avail则表示缓冲区内的子节不够本次读取。随后进行数组复制,将缓冲区内的子节复制到目标数组中。修改pos返回读取的个数。
2 在read方法中判断返回字节数如果和len一样表示读取结束。如果是-1则返回-1.否则调用基础流的available查看当前可读子节,如果发现基础流当前不可读数据则返回已读的长度。否则再次循环,这就对应了步骤1中len>avail。则再次调用read1.
3 再次调用avail后可读子节数为0。再次判断如果当前没有markpos标记,缓冲区的长度还没有len大则。表示1当前不需要存储markpos的子节2当前要读的子节已经比缓存区大了。就直接调用基础流的read,不需要缓冲区。
4 如果判断后发现需要缓冲区,则调用fill方法去读取基本流中读取填充缓冲区。填充缓冲区后如果发现可读子节为-1则返回-1.否则继续复制数组。如果这次复制的长度达到了len则循环结束,否则循环继续。
public synchronized long skip(long n) throws IOException { getBufIfOpen(); //检查基础流 if (n <= 0) { return 0;//跳过0返回0 } long avail = count - pos;//缓冲区可读字节数 if (avail <= 0) {//缓冲区内没有可读子节了。 if (markpos <0)//如果没有markpos标记则可以直接调用基础流的skip return getInIfOpen().skip(n); //需要填充缓冲区 fill(); avail = count - pos; if (avail <= 0)//填充后可读子节为0则返回0 return 0; } long skipped = (avail < n) ? avail : n;//确定可跳过的子节。可读子节大于跳过子节则可以满足skip否则只能跳过当前可读字节数 pos += skipped;//修改pos后下次读取从新的pos位读取 return skipped;//返回跳过字节数 }
public synchronized int available() throws IOException { int n = count - pos;//缓冲区内可读字节数 int avail = getInIfOpen().available();//基础流中可读字节数 return n > (Integer.MAX_VALUE - avail)//如果缓冲区可读子节数大于Integer-基础流可读子节则返回Integer最大值。否则返回缓冲区可读子节+基础流可读子节。 ? Integer.MAX_VALUE : n + avail; }
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {//将缓冲区数组修改为null
InputStream input = in;
in = null;//将基础流修改为null
if (input != null)
input.close();//关闭基础流
return;
}
}
}
public boolean markSupported() { return true;//支持mark操作 } public synchronized void mark(int readlimit) { marklimit = readlimit;//设置当前markpos标记位后边marklimit个子节为保留子节。这个很重要!!!!
markpos = pos;//下一个可读取的位置为markpos } public synchronized void reset() throws IOException { getBufIfOpen(); //检查基础流 if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos;//pos回退到markpos }
Writer源码
Writer是字符输出流(以下称书写器)的基类,它内部持有一个子类需要的lock,默认是this.多个写入方法调用的是抽象的write,由具体的子类实现.
protected Object lock;//给子类使用的锁 protected Writer() { this.lock = this;//没有构造函数使用 } protected Writer(Object lock) {//传入对象锁 if (lock == null) { throw new NullPointerException(); } this.lock = lock; }
private static final int WRITE_BUFFER_SIZE = 1024;
private char[] writeBuffer;
public void write(int c) throws IOException {//1 synchronized (lock) { if (writeBuffer == null){ writeBuffer = new char[WRITE_BUFFER_SIZE]; } writeBuffer[0] = (char) c;//单字节写入char数组 write(writeBuffer, 0, 1);//调用5 } } public void write(char cbuf[]) throws IOException {//2 write(cbuf, 0, cbuf.length);//调用5 } public void write(String str) throws IOException {//3 write(str, 0, str.length());//调用4 } public void write(String str, int off, int len) throws IOException {//4 synchronized (lock) { char cbuf[]; if (len <= WRITE_BUFFER_SIZE) {//写入字节数小于buffer_size则将buffer赋值cbuf if (writeBuffer == null) { writeBuffer = new char[WRITE_BUFFER_SIZE]; } cbuf = writeBuffer; } else { cbuf = new char[len];//buffer_size不够用则创建一个len长度的char数组 } str.getChars(off, (off + len), cbuf, 0);//将此字符串中的字符复制到目标字符数组中。 write(cbuf, 0, len);//调用5 } }
abstract public void write(char cbuf[], int off, int len) throws IOException;//5 cbuf的数据写入从off到len
public Writer append(CharSequence csq) throws IOException {//7 if (csq == null) write("null");//调用3,会传入null else write(csq.toString());//调用3 return this; } public Writer append(CharSequence csq, int start, int end) throws IOException {//8 CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString());//调用3,取csq的一部分 return this; } public Writer append(char c) throws IOException {//9 write(c);//调用1 return this; }
abstract public void flush() throws IOException; abstract public void close() throws IOException;
Reader源码
Reader是字符输入流的基类(以下称阅读器),它定义了子类应该使用的lock锁和一个抽象的read方法,由具体的子类实现.
protected Object lock;//锁 protected Reader() { this.lock = this; } protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; }
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); } public int read(java.nio.CharBuffer target) throws IOException { int len = target.remaining();//查询CharBuffer可用容量 char[] cbuf = new char[len]; int n = read(cbuf, 0, len); if (n > 0) target.put(cbuf, 0, n);//读取到的cbuf存入CharBuffer return n; } abstract public int read(char cbuf[], int off, int len) throws IOException;//读取的数据放入cbuf中,上边三个都是调用的这个.
private static final int maxSkipBufferSize = 8192;
private char skipBuffer[] = null;
public long skip(long n) throws IOException { if (n < 0L) throw new IllegalArgumentException("skip value is negative"); int nn = (int) Math.min(n, maxSkipBufferSize); synchronized (lock) { if ((skipBuffer == null) || (skipBuffer.length < nn)) skipBuffer = new char[nn]; long r = n; while (r > 0) { int nc = read(skipBuffer, 0, (int)Math.min(r, nn));//将跳过的字符存入skipbuff if (nc == -1) break; r -= nc; } return n - r; } }
public boolean ready() throws IOException { return false; } public boolean markSupported() { return false; } public void mark(int readAheadLimit) throws IOException { throw new IOException("mark() not supported"); } public void reset() throws IOException { throw new IOException("reset() not supported"); } abstract public void close() throws IOException;
StringWriter源码
StringWriter是书写器的直接子类.它将字符写入到StringBuffer中.write方法调用的是StringBuffer的append方法.append方法则调用write方法返回this,可用于链式编程.写入到StringBuffer不需要刷新,不需要关闭.
private StringBuffer buf;//写入到这个缓冲区
public StringWriter() { buf = new StringBuffer();//默认长度16 lock = buf;//父类里的锁 } public StringWriter(int initialSize) { if (initialSize < 0) { throw new IllegalArgumentException("Negative buffer size"); } buf = new StringBuffer(initialSize);//指定长度的缓冲区 lock = buf; }
public void write(int c) { buf.append((char) c);//调用StringBuffer的添加方法. } public void write(char cbuf[], int off, int len) { if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException();//下标越界检查 } else if (len == 0) { return; } buf.append(cbuf, off, len);//调用StringBuffer的添加方法. } public void write(String str) { buf.append(str);//调用StringBuffer的添加方法 } public void write(String str, int off, int len) { buf.append(str.substring(off, off + len));//调用StringBuffer的添加方法,添加的内容为off开始到off+len结束. }
public StringWriter append(CharSequence csq) {//调用write(String str) if (csq == null) write("null");//允许写入null else write(csq.toString()); return this; } public StringWriter append(CharSequence csq, int start, int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString());写入csq从start-end,调用的write(String str) return this; } public StringWriter append(char c) { write(c);//写入单个字符. return this; }
public String toString() { return buf.toString();//返回StringBuffer中的字符串 } public StringBuffer getBuffer() { return buf;//返回StringBuffer } public void flush() {}//不需要刷新 public void close() throws IOException {}//不需要关闭
StringReader源码
StringReader是阅读器的直接子类.它从目标字符串中读取字符.可以读取单个字符也可以读取多个字符放入到指定的字符数组中,skip方法则可以回退下标.提供了close的实现.关闭流即将目标字符串置为null.
private String str;//要读取的字符串 private int length;//字符串长度 public StringReader(String s) {//接收要读取的字符串 this.str = s; this.length = s.length(); }
private void ensureOpen() throws IOException { if (str == null)//判断数据源字符串是否为null throw new IOException("Stream closed"); } private int next = 0;//下次读取的位置 public int read() throws IOException { synchronized (lock) { ensureOpen(); if (next >= length)//没有可以读取的字符 return -1;
return str.charAt(next++);//读取指定下标,然后下边自增. } } public int read(char cbuf[], int off, int len) throws IOException {//读取len个字符到cbuf中,从off偏移量开始放置. synchronized (lock) { ensureOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } if (next >= length) return -1; int n = Math.min(length - next, len);//n要么是当前可读字符数,要么是len,取决于哪个比较小. str.getChars(next, next + n, cbuf, off);//将str中的字符串next到next+n之间的字符从off位置拷贝到cbuf. next += n;//next加上这次读的长度. return n; } }
public long skip(long ns) throws IOException { synchronized (lock) { ensureOpen(); if (next >= length) return 0; //这个跳过有意思,先看当前可读和要跳过的谁小,ns小说明没有超过字符串剩余可读.ns大那就最多只能跳过leng-next long n = Math.min(length - next, ns); n = Math.max(-next, n);//-next和n谁大,这里是允许n是负数的.如果n是正数本次执行结果就是next+n.如果n是负数要看看-next和n谁大.如果-next大表示可退回到0.如果n大则退回到next-n next += n; return n; } }
private int mark = 0;
public boolean ready() throws IOException {//返回是否准备完毕.str为null抛出异常. synchronized (lock) { ensureOpen(); return true; } } public boolean markSupported() { return true; } public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0){//在BufferedInputStream中这个参数有用,在这其实是没用 throw new IllegalArgumentException("Read-ahead limit < 0"); } synchronized (lock) { ensureOpen(); mark = next;//当前位置作为mark标记. } } public void reset() throws IOException { synchronized (lock) { ensureOpen(); next = mark;//返回到mark标记. } } public void close() { str = null;//关流str为null.ensureOpen()抛出异常. }
CharArrayWriter源码
CharArrayWriter是书写器的直接子类,它接受单个字符或者字符串,将他们存入到char数组内.默认的char数组长度是32.它的扩容方式是使用Arrays实现拷贝.append调用的是基础的write方法但会返回this实现链式调用.reset()方法对于用户来说是清空char数组,但其内部其实是覆盖char数组中的元素.这个流不需要flush或者close所以这两个方法是空实现.
protected char buf[];//数据写入到这里 public CharArrayWriter() { this(32);//默认长度 } public CharArrayWriter(int initialSize) { if (initialSize < 0) { throw new IllegalArgumentException("Negative initial size: " + initialSize); } buf = new char[initialSize]; }
protected int count;//已经写入的字符数 public void write(int c) { synchronized (lock) { int newcount = count + 1;//buf的容量不够了,<<1是扩容为leng*2 if (newcount > buf.length) { buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } buf[count] = (char)c;//写入count位置,int转char. count = newcount;//更改count } } public void write(char c[], int off, int len) { if ((off < 0) || (off > c.length) || (len < 0) || ((off + len) > c.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException();//检查c的下标. } else if (len == 0) { return; } synchronized (lock) { int newcount = count + len;//写入后的长度. if (newcount > buf.length) {//buf存不下,buf*2 buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } System.arraycopy(c, off, buf, count, len);//从c的off位置开始,写入len个字符到buf的count位置起. count = newcount;//修改count. } } public void write(String str, int off, int len) { synchronized (lock) { int newcount = count + len;//写入后的长度. if (newcount > buf.length) {//buf不够buf*2 buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); } str.getChars(off, off + len, buf, count);//str字符串的off位置开始后的len个长度的字符串写入到buf中,从count位置开始. count = newcount;//修改count. } }
public CharArrayWriter append(CharSequence csq) {//写入字符串 String s = (csq == null ? "null" : csq.toString()); write(s, 0, s.length()); return this; } public CharArrayWriter append(CharSequence csq, int start, int end) {//从字符串的start位置到end位置写入. String s = (csq == null ? "null" : csq).subSequence(start, end).toString(); write(s, 0, s.length()); return this; } public CharArrayWriter append(char c) {//写入单个字符 write(c); return this; }
public void writeTo(Writer out) throws IOException { synchronized (lock) { out.write(buf, 0, count);//将this的字符数组拷贝到目标书写器中. } } public char toCharArray()[] { synchronized (lock) { return Arrays.copyOf(buf, count);//返回字节数组的副本. } } public int size() { return count;//返回当前书写器内的字符数 } public String toString() { synchronized (lock) { return new String(buf, 0, count);//字符数组的字符串形式. } } public void flush() { } public void close() { }
public void reset() {
count = 0;//将count修改为0后,下一次写入字符的位置是buf[0]也就是覆盖的效果,但无论是通过toString()还是toCharArray()或者是其他public方法都无法查看到源数组的内容,对于用户来说像是清空一样,除非你断点查看.
}
CharArrayReader源码
charArrayReader是阅读器的直接子类,它从源数据字符数组中读取字符,可以一次读取一个字符或将字符批量读到指定的数组中。skip改变pos指针已达到跳过的目的,允许mark,reset操作。
protected char buf[]; protected int pos; protected int count; public CharArrayReader(char buf[]) {//需要一个被读取的char数组 this.buf = buf;//被读取的数组 this.pos = 0;//读取的下标 this.count = buf.length;//总共可读的总数
}public CharArrayReader(char buf[], int offset, int length) {//要读去buf数组中从offset开始后的length个字符 if ((offset < 0) || (offset > buf.length) || (length < 0) || ((offset + length) < 0)) { throw new IllegalArgumentException();//offset不能小于0,offset不能大于buf的总长,length不能是0,offset+length不能小于0 } this.buf = buf; this.pos = offset; this.count = Math.min(offset + length, buf.length);//最多可读buf.length个字符,offset+length如果大于length读取肯定越界。 this.markedPos = offset;//回退标记位 }
private void ensureOpen() throws IOException {//检查流是否关闭,关闭后buf==null。 if (buf == null) throw new IOException("Stream closed"); } public int read() throws IOException { synchronized (lock) {//父类的锁 ensureOpen(); if (pos >= count)//已经读取完毕了。 return -1; else return buf[pos++];//读取后下标+1 } }
public int read(char b[], int off, int len) throws IOException {//读取len个字符到b数组中,从off开始放置。 synchronized (lock) { ensureOpen();//检查流是否关闭 if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException();//检查下标 } else if (len == 0) { return 0; } if (pos >= count) {//已经没有可读的 return -1; } int avail = count - pos;//剩余可读 if (len > avail) {//avail没有len多,最多读avail len = avail; } if (len <= 0) {//读取0个返回0 return 0; } System.arraycopy(buf, pos, b, off, len);//buf里的字符从pos位开始,复制len个到b中,从off开始放置。 pos += len;//pos修改 return len; } }
public long skip(long n) throws IOException {//跳过n个字符 synchronized (lock) { ensureOpen();//检查是否关闭 long avail = count - pos;//可读字符 if (n > avail) { n = avail;//最多跳过avail } if (n < 0) { return 0; } pos += n;//修改pos return n; } } public boolean ready() throws IOException {//没有关闭,并且有可读字符算准备完毕。 synchronized (lock) { ensureOpen(); return (count - pos) > 0; } } public void close() {//关闭后buf赋值null buf = null; }
public boolean markSupported() { return true;//可以标记,回退 } public void mark(int readAheadLimit) throws IOException {//入参没用上。BufferInputStream使用了。 synchronized (lock) { ensureOpen();//流关闭抛出异常 markedPos = pos;//当前pos标记为回退标记 } } public void reset() throws IOException { synchronized (lock) { ensureOpen(); pos = markedPos;//pos修改为markpos } }
BufferedWriter
BufferedWriter是书写器的直接子类,它内部持有一个默认长度为8192的字符数组,允许用户一次写入一个字符或者字符串或者字符数组。先将源数据存入缓冲区数组中,在适当的时间一次写入到基础流。
private static int defaultCharBufferSize = 8192;//默认缓冲区大小 public BufferedWriter(Writer out) {//数据需要被写入到基础流 this(out, defaultCharBufferSize); } private char cb[];
private int nChars, nextChar;//nchars缓冲区总长度,nextChar下一个写入位。
private String lineSeparator;//操作系统对应的换行符
public BufferedWriter(Writer out, int sz) {//指定缓冲区长度 super(out); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0");//缓冲区长度错误 this.out = out; cb = new char[sz];//创建缓冲区数组 nChars = sz; nextChar = 0; lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator"));//获取操作系统相关的换行符。 }
private void ensureOpen() throws IOException { if (out == null) throw new IOException("Stream closed");//流已关闭,抛出异常 } public void write(int c) throws IOException {//写入一个字符 synchronized (lock) { ensureOpen();//检查流是否关闭 if (nextChar >= nChars)//缓冲区满,需要刷新 flushBuffer();//刷新 cb[nextChar++] = (char) c;//写入nextChar位。 } }
void flushBuffer() throws IOException {
synchronized (lock) {
ensureOpen();//检查流关闭
if (nextChar == 0)//缓冲区标记为0返回
return;
out.write(cb, 0, nextChar);//使用基础流的write,从缓冲区cb的0位写nextChar个字符到基础流。
nextChar = 0;//修改缓冲区标记。
}
}
public void write(String s, int off, int len) throws IOException {//从字符串的off开始写入len个字符
synchronized (lock) {
ensureOpen();//检查流关闭
int b = off, t = off + len;//b:偏移量,t:写入总长
while (b < t) {//循环写入
int d = min(nChars - nextChar, t - b);//nChars-nextChar算的是当前缓冲区剩余空间。t-b算的是要写入的字符数。取两者中较小的。
s.getChars(b, b + d, cb, nextChar);//将字符串s从off开始到off+d(len小end为len否则为当前缓冲区剩余容量)之间的字串写入到cb中从nextChar位开始。
b += d;//修改off,加上已经写入的d。
nextChar += d;缓冲区下标+d
if (nextChar >= nChars)刷新缓冲区。
flushBuffer();
}
}
}
private int min(int a, int b) {//返回a,b中较小的。 if (a < b) return a; return b; }
public void write(char cbuf[], int off, int len) throws IOException {//从源数组的off位置开始写入len个字符到缓冲区
synchronized (lock) {
ensureOpen();//检查流关闭
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();//下标越界检查
} else if (len == 0) {
return;//写入0返回0
}
if (len >= nChars) {//要写入的长度大于当前缓冲区的长度。就不需要写缓冲区了,先刷新缓冲区,在调用底层流写入。
flushBuffer();
out.write(cbuf, off, len);
return;
}
int b = off, t = off + len;//b:要写入的偏移量,t:偏移量+总数
while (b < t) {//循环写入
int d = min(nChars - nextChar, t - b);//取缓冲区剩余容量和要写入长度中较小的。
System.arraycopy(cbuf, b, cb, nextChar, d);//将cbuf从b开始写入d个字符到cb中的nextChar位置开始。
b += d;//修改off
nextChar += d;修改缓冲区off
if (nextChar >= nChars)//刷新缓冲区。
flushBuffer();
}
}
}
public void newLine() throws IOException { write(lineSeparator);//写入换行符 } public void flush() throws IOException { synchronized (lock) { flushBuffer();//刷新缓冲区 out.flush();//刷新基础流 } } @SuppressWarnings("try") public void close() throws IOException { synchronized (lock) { if (out == null) { return; } try (Writer w = out) { flushBuffer();//关闭前刷新。刷新基础流 } finally { out = null;//基础流置为null cb = null;//缓冲区置为null. } } }
BufferedReader源码
BufferedReader是阅读器的直接子类,它的内部持有一个char数组,每次都会从基础流中读取一批数据当作缓冲数据,用户每次读取先从缓冲区中读取,缓冲区内没有数据在从基础流填充,如果有markpos则需要注意保留mark数据的长度.另外BufferedReader有readLine方法每次读取一行,如果读到\r则会判断下一个字符是不是\n,会跳过\n换行符.
private static int defaultCharBufferSize = 8192;//缓冲区大小,每次从基础流读取的字符数 private Reader in;//基础流 private char cb[];//缓冲区数组 private int nChars, nextChar;//nChars缓冲区内剩余可读count,nextChar缓冲区pos
public BufferedReader(Reader in) {//需要基础流 this(in, defaultCharBufferSize);//默认8192 } public BufferedReader(Reader in, int sz) {//设置缓冲区大小 super(in); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0");//缓冲区不能小于1 this.in = in; cb = new char[sz];//实例缓冲区数组 nextChar = nChars = 0;//缓冲区剩余子节和pos初始0 }
private boolean skipLF = false; private boolean markedSkipLF = false; private int readAheadLimit = 0;//标记后允许保留的长度 public boolean markSupported() { return true;//允许设置mark标记 } public void mark(int readAheadLimit) throws IOException {//设置markpos,limit标记:因为缓冲区满以后要刷新到流中,设置mark标记又允许回退所以设置limit允许mark后最大的保留长度 if (readAheadLimit < 0) {//小于0失败。 throw new IllegalArgumentException("Read-ahead limit < 0"); } synchronized (lock) { ensureOpen();//检查关闭状态 this.readAheadLimit = readAheadLimit;//marklimit长度 markedChar = nextChar;//当前pos为markpos markedSkipLF = skipLF; } } public void reset() throws IOException {//回退 synchronized (lock) { ensureOpen(); if (markedChar < 0) throw new IOException((markedChar == INVALIDATED) ? "Mark invalid" : "Stream not marked"); nextChar = markedChar;//设置markpos为pos。 skipLF = markedSkipLF; } }
private static final int UNMARKED = -1;//未设置mark标记
private int markedChar = UNMARKED;//mark标记,默认是没有设置。
private static final int INVALIDATED = -2;mark标记过期常量
private void fill() throws IOException {//当缓冲区内的字符读取完毕后要到基础流中读取,填充到缓冲区中。 int dst; if (markedChar <= UNMARKED) {//1 dst = 0; } else { int delta = nextChar - markedChar;//当前读取pos-标记markpos的差 if (delta >= readAheadLimit) {//3 markedChar = INVALIDATED; readAheadLimit = 0; dst = 0; } else { if (readAheadLimit <= cb.length) {//4 System.arraycopy(cb, markedChar, cb, 0, delta); markedChar = 0; dst = delta; } else {//6 char ncb[] = new char[readAheadLimit]; System.arraycopy(cb, markedChar, ncb, 0, delta); cb = ncb; markedChar = 0; dst = delta; } nextChar = nChars = delta;//5 } } int n;//2 do { n = in.read(cb, dst, cb.length - dst); } while (n == 0); if (n > 0) { nChars = dst + n; nextChar = dst; } }
1 没有设置mark标记直接读取填充。循环调用基础流的read(char[]b,off,len)方法。将基础流的数据读取到buf中,dst为0,因为没有设置mark标记,读取的长度就是buf的长度。如果读取到的是0则表示基础流暂不可读(IO未就绪)则停止循环。设置nchars可读长度为读取到的字符数,设置nextChar为0。(步骤1,2)
2 设置了mark标记但当前读取的位置已经超过了marklimit,则无需保存。设置markpos过期。dst变量在步骤2中使用,从基础流中读取的数据放入缓冲区的哪个位置。不需要保留mark则重新读取的数据就从0开始放。随后循环调用基础流的read(步骤3,2)
3 设置了mark标记,需要保存的字符数小于缓冲区的长度,这种情况下就需要保留缓冲区内数据了System.arraycopy(cb, markedChar, cb, 0, delta);将缓冲区cb里的数据从markpos标记开始复制delta个字符到cb缓冲区中,从cb的0号下标开始。设置markpos为0,因为当前缓冲区内的数据都是需要保存的。dst设为当前缓冲区内最后位的后一位,也就是保留数据后。设置下一次读取位为delta,缓冲区可读总数为delta。随后循环调用基础流的read(步骤4,5,2)
4 设置了mark标记但是需要保留的字符数大于了当前缓冲区的长度,此时缓冲区内空间就需要全部用于保留mark数据。只能扩大缓冲区空间。创建新的缓冲区大小为mark保留的最大值。System.arraycopy(cb, markedChar, ncb, 0, delta);将cb中的数据从markpos开始复制到新的缓冲区数组,复制长度为需要保留的字符数,从新数组的0号位开始。随后将新数组指向缓冲区数组变量,markpos设置为0,dst为已有数据的后边。随后执行步骤5,2(步骤6,5,2)
public long skip(long n) throws IOException {
if (n < 0L) {//不能跳过负数
throw new IllegalArgumentException("skip value is negative");
}
synchronized (lock) {
ensureOpen();//关闭流检查
long r = n;
while (r > 0) {//跳过字符数大于0
if (nextChar >= nChars)//没用可读字符,无法跳过则填充。
fill();
if (nextChar >= nChars) //一次填充后可读子节没用增加则停止循环
break;
if (skipLF) {//只有readLine读取到\r时才会走这里,跳过换行符
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;//\n也算一个字符跳过。
}
}
long d = nChars - nextChar;//可读字符数
if (r <= d) {//d满足跳锅的数量r
nextChar += r;//修改nextChar+n,这就算跳过了。
r = 0;不需要跳过了
break;//停止循环
}
else {//d不满足跳过数量,还需要继续循环读取基础流。
r -= d;//本次计划跳过的r需要减去可读的d个字符。
nextChar = nChars;//修改pos
}
}
return n - r;
}
}
public int read() throws IOException {//读取单个字符 synchronized (lock) { ensureOpen();//检查关闭流 for (;;) {//循环读取 if (nextChar >= nChars) {//需要填充 fill(); if (nextChar >= nChars)//基础流没有数据,返回-1 return -1; } if (skipLF) {//只有上个字符是\r才走这里,跳过换行符 skipLF = false; if (cb[nextChar] == '\n') {// nextChar++; continue; } } return cb[nextChar++];//从缓冲区中读取,修改pos } } }
private int read1(char[] cbuf, int off, int len) throws IOException {//读取len个字符到cbuf中从off开始复制 if (nextChar >= nChars) {//缓冲区内无数据 if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {//缓冲区的总长度不满足len,并且没有标记,则直接调用基础流read。 return in.read(cbuf, off, len); } fill();//缓冲区总长度满足len则填充 } if (nextChar >= nChars) return -1;//填充过一次后缓冲区内没有数据返回-1 if (skipLF) {//只有上一个是\r才会走,跳过换行符 skipLF = false; if (cb[nextChar] == '\n') { nextChar++; if (nextChar >= nChars) fill(); if (nextChar >= nChars) return -1; } } int n = Math.min(len, nChars - nextChar);//n取缓冲区中可读字符数,或者len。 System.arraycopy(cb, nextChar, cbuf, off, n);//将缓冲区数据从nextChar开始拷贝n个到cbuf中从off未开始放 nextChar += n;//修改pos return n; } public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen();//检查流关闭 if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException();//检查下标 } else if (len == 0) { return 0; } int n = read1(cbuf, off, len);//返回已经读取的字符数 if (n <= 0) return n;//读取完毕 while ((n < len) && in.ready()) {//如果读取的字符数不满足len并且流还可读。 int n1 = read1(cbuf, off + n, len - n);//循环继续读,每次循环需要读取len-n个字符,从off+n开始放。 if (n1 <= 0) break;//再次读取返回-1则停止 n += n1;//累加读取个数 } return n; } }
public String readLine() throws IOException { return readLine(false); } private static int defaultExpectedLineLength = 80;//没用找到换行符则默认的行长度 String readLine(boolean ignoreLF) throws IOException { StringBuilder s = null;//返回的行数据 int startChar;// synchronized (lock) { ensureOpen();//检查流关闭 boolean omitLF = ignoreLF || skipLF;//忽略换行符false,跳过换行符false bufferLoop: for (;;) { if (nextChar >= nChars)//缓冲区内没用可读字符 fill();//从基础流填充缓冲区 if (nextChar >= nChars) {//读取完毕返回 if (s != null && s.length() > 0) return s.toString(); else return null; } boolean eol = false; char c = 0; int i; //如果上一个是\r则必定会跳过\n if (omitLF && (cb[nextChar] == '\n')) nextChar++; skipLF = false;//换行符结束 omitLF = false;//换行符结束 charLoop://循环找可读字符中是否有\r或\n for (i = nextChar; i < nChars; i++) { c = cb[i]; if ((c == '\n') || (c == '\r')) { eol = true;//找到了停止循环 break charLoop; } } //开始读取位置 startChar = nextChar; nextChar = i;//要么是可读字符的末尾位置,要么是\r或\n的位置 if (eol) {//找到了换行符 String str; if (s == null) {//StringBuilder未创建,则创建字符串返回.从cb中复制从pos位开始到换行符结束 str = new String(cb, startChar, i - startChar); } else { s.append(cb, startChar, i - startChar);//添加到StringBuilder str = s.toString(); } nextChar++;//因为nextChar已经在charLoop时被赋值了,此时只需要自增1 if (c == '\r') {//如果是\r则表示还有\n需要跳过. skipLF = true; } return str; } if (s == null)//没用找到换行符则使用默认行长度80读取并返回. s = new StringBuilder(defaultExpectedLineLength); s.append(cb, startChar, i - startChar); } } }
public boolean ready() throws IOException {//流是否可读 synchronized (lock) { ensureOpen();//检查流关闭 if (skipLF) { if (nextChar >= nChars && in.ready()) {//填充流 fill(); } if (nextChar < nChars) {//检查是否可以跳过\n if (cb[nextChar] == '\n') nextChar++; skipLF = false;//不需要跳过\n } } return (nextChar < nChars) || in.ready();//pos位小于count或者基础流可读则返回true } } public void close() throws IOException { synchronized (lock) { if (in == null) return; try { in.close();//关闭基础流 } finally { in = null;//基础流变量置为null cb = null;//缓冲区数组置为null } } }
public Stream<String> lines() {//获取Stream,NIO使用 Iterator<String> iter = new Iterator<String>() { String nextLine = null; @Override public boolean hasNext() {//如果可以通过readLine()读取到行数据则返回true if (nextLine != null) { return true; } else { try { nextLine = readLine(); return (nextLine != null); } catch (IOException e) { throw new UncheckedIOException(e); } } } @Override public String next() {//返回整行数据 if (nextLine != null || hasNext()) { String line = nextLine; nextLine = null; return line; } else { throw new NoSuchElementException(); } } }; return StreamSupport.stream(Spliterators.spliteratorUnknownSize( iter, Spliterator.ORDERED | Spliterator.NONNULL), false); }