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();  
    }

总结:

理解还不是很深刻,对它的实现的理解还有很多不理解的地方,还需要继续学习。

加油吧!

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。
共勉。

 

posted on 2012-08-22 21:51  每当变幻时  阅读(5886)  评论(3编辑  收藏  举报

导航