BufferedInputStream-我们到底能走多远系列(3)
我们到底能走多远系列(3)
扯淡:
现在项目里的java代码,都是封装,封装,再封装。在没有空闲的赶工编码,几年后会感觉学不动,毕竟少了很多思考的时间。也基本不会去研究代码的底层实现。时间一长就会觉得自己什么也不会。
一个大型的项目,一般不可能只用java实现,可能会用到C,C++,shell,python等等。单单一个web就要学很多,jsp,jquery,javascript,html,css,各种开源的不开源的框架,各种web服务器,数据库等等等。java程序员的确容易迷茫,但精通一样吧,怕找不到工作,全学吧,怕一直是码农。
其实,我觉得大多数的人都只是希望能做到“工程师”,解决问题的人。我看到现在接近40的程序员也还是蛮吃香的,他们的能力也是这样慢慢码过来的。和他们交流的时候感觉他们什么都知道点,也有自己及其精通的一面。这个方向似乎也是一种不错的职业规划。
-----------------------------------------------------------------------------
总之,不要停止学习,不要停止进步。
-----------------------------------------------------------------------------
BufferedInputStream应该比较实用吧。读读源码,学习下。
继承结构:
BufferedInputStream ----> FilterInputStream -----> InputStream
概述:
FilterInputStream继承自InputStream属于输入流中的链接流,同时引用了InputStream,将 InputStream封装成一个内部变量,同时构造方法上需要传入一个InputStream。这是一个典型的装饰器模式,他的任何子类都可以对一个继 承自InputStream的原始流或其他链接流进行装饰,如我们常用的使用BufferedInputStream对FileInputStream进 行装饰,使普通的文件输入流具备了内存缓存的功能,通过内存缓冲减少磁盘io次数。
BufferedInputStream方法一览:
private void fill() private byte[] getBufIfOpen() private InputStream getInIfOpen() private int read1(byte[] b, int off, int len) ------------------------------------------------- public BufferedInputStream(InputStream in) public BufferedInputStream(InputStream in, int size) -------------------------------------------------- public synchronized int available() 返回此输入流方法的下一个调用方可以不受阻塞地从此输入流读取(或跳过)的字节数。 public void close() 关闭此输入流并释放与该流关联的所有系统资源。 public synchronized void mark(int readlimit) 在此输入流中标记当前的位置。对 reset 方法的后续调用会在最后标记的位置重新定位此流,以便后续读取重新读取相同的字节。 readlimit - 在标记位置失效前可以读取字节的最大限制。 public boolean markSupported() 测试此输入流是否支持 mark 和 reset 方法。 public synchronized int read() 从输入流读取下一个数据字节。返回 0 到 255 范围内的 int 字节值。如果因已到达流末尾而没有可用的字节,则返回值 -1。 public synchronized int read(byte b[], int off, int len) 将输入流中最多 len 个数据字节读入字节数组。尝试读取多达 len 字节,但可能读取较少数量。以整数形式返回实际读取的字节数。 public synchronized void reset() 将此流重新定位到对此输入流最后调用 mark 方法时的位置。 public synchronized long skip(long n) 跳过和放弃此输入流中的 n 个数据字节。
源码:
package java.io; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; //继承FilterInputStream,FilterInputStream继承InputStream //该类主要完成对被包装流,加上一个缓存的功能,所谓的缓存不就是new一个byte数组 public class BufferedInputStream extends FilterInputStream { private static int defaultBufferSize = 8192; //默认缓存的大小 protected volatile byte buf[]; //内部的缓存(数组)volatile修饰,保证不同的线程总是看到某个成员变量的同一个值 protected int count; //buffer的大小,表示当前缓冲区内总共有多少有效数据 protected int pos; //buffer中cursor的位置,即byte数组的当前下标,下次读取从该位置读取 protected int markpos = -1; //mark的位置 protected int marklimit; //mark的范围,最多能mark的字节长度,也就是从mark位置到当前pos的最大长度,作为参数传入 //原子性更新。和一致性编程相关 private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); private InputStream getInIfOpen() throws IOException { //检查输入流是否关闭,同时返回被包装流 InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; } private byte[] getBufIfOpen() throws IOException { //检查buffer的状态,同时返回缓存 byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); //不太可能发生的状态 return buffer; } public BufferedInputStream(InputStream in) { //构造器 this(in, defaultBufferSize); //指定默认长度(defaultBufferSize)为buffer的长度 } public BufferedInputStream(InputStream in, int size) { //构造器 super(in); if (size <= 0) { //检查size参数, throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; //创建指定长度的buffer,这就缓存! } //从流中读取数据,填充如缓存中。构造函数开辟了缓存,都是空值,需要方法来填充它们。 private void fill() throws IOException { byte[] buffer = getBufIfOpen(); //得到buffer //buffer没有被mark,也就是说没有被调用mark方法 if (markpos < 0) pos = 0; //mark位置小于0,此时pos为0 // buffer被mark的情况 else if (pos >= buffer.length) //pos大于buffer的长度,读到buffer的最后 if (markpos > 0) { //有mark,将mark-pos段的数组保留 int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { //buffer的长度大于marklimit时,mark失效 markpos = -1; pos = 0; //丢弃buffer中的内容 } else {//buffer的长度小于marklimit时对buffer扩容 int nsz = pos * 2; if (nsz > marklimit) nsz = marklimit;//扩容为原来的2倍,太大则为marklimit大小 byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); //将buffer中的字节拷贝如扩容后的buf中 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//在buffer在被操作时,不能取代此buffer throw new IOException("Stream closed"); } buffer = nbuf; //将新buf赋值给buffer } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } public synchronized int read() throws IOException { //读取下一个字节 if (pos >= count) { //到达buffer的末端 fill();//就从流中读取数据,填充buffer if (pos >= count) return -1; //读过一次,没有数据则返回-1 } return getBufIfOpen()[pos++] & 0xff; //返回buffer中下一个位置的字节 } private int read1(byte[] b, int off, int len) throws IOException { //将数据从流中读入buffer中 int avail = count - pos; //buffer中还剩的可读字符 if (avail <= 0) {//buffer中没有可以读取的数据时 if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); //将输入流中的字节读入b中 } fill();//填充 avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; //从流中读取后,检查可以读取的数目 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //将当前buffer中的字节放入b的末端 pos += cnt; return cnt; } // byte b[]调用者提供,调用者要使用的也是它。 public synchronized int read(byte b[], int off, int len)throws IOException { getBufIfOpen(); // 检查buffer是否open 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(); // 检查buffer是否关闭 if (n <= 0) { return 0; } //检查输入参数是否正确 long avail = count - pos; //buffered中可以读取字节的数目 if (avail <= 0) { //可以读取的小于0,则从流中读取 if (markpos <0) return getInIfOpen().skip(n); //mark小于0,则mark在流中 fill(); // 从流中读取数据,填充缓冲区。 avail = count - pos; //可以读的取字节为buffer的容量减当前位置 if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; //当前位置改变 return skipped; } public synchronized int available() throws IOException { return getInIfOpen().available() + (count - pos); } //该方法不会block!返回流中可以读取的字节的数目。 //该方法的返回值为缓存中的可读字节数目加流中可读字节数目的和 public synchronized void mark(int readlimit) { //当前位置处为mark位置 marklimit = readlimit; markpos = pos; } public synchronized void reset() throws IOException { getBufIfOpen(); // 缓冲去关闭了,肯定就抛出异常!程序设计中经常的手段 if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } public boolean markSupported() { //该流和ByteArrayInputStream一样都支持mark return true; } //关闭当前流同时释放相应的系统资源。 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() } } }
fill()---> 从字面理解就是填充的意思,实际上是从真正的输入流中读取一些新数据放入缓冲内存中,之后直到缓冲内存中的数据读完前都不会再从真正的流中读取数据。
网上对这个方法的分析:
普通的情况:
有mark的情况:
普通的练习,复制文件:
first:
private void copyFile(String fromPath, String toPath) throws IOException{ // input File fromFile = new File(fromPath); InputStream is = new FileInputStream(fromFile); BufferedInputStream bis = new BufferedInputStream(is); // output File toFile = new File(toPath); OutputStream os = new FileOutputStream(toFile); BufferedOutputStream bos = new BufferedOutputStream(os); // transfer station byte b[] = new byte[(int)fromFile.length()] ; while(bis.read(b, 0, b.length) != -1){ bos.write(b, 0, b.length); } bis.close(); bos.close(); }
second:
public static void copy() throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("d:/我的文档/123.txt")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:/我的文档/txt.txt")); int by = 0; while ((by=bufis.read()) != -1) { bufos.write(by); } bufos.close(); bufis.close(); }
总结:
理解还不是很深刻,对它的实现的理解还有很多不理解的地方,还需要继续学习。
加油吧!
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
共勉。