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

    总结:

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

    加油吧!

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

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

  • 相关阅读:
    [职场]最近聊到30岁以上的程序员,该何去何从了?你有啥想法?
    想跳槽涨薪,想进大厂,如何准备面试呢?
    [面试分享]想跳槽涨薪,想进大厂,如何准备面试呢?
    缓存穿透、缓存并发、缓存雪崩、缓存抖动、热点缓存、缓存双写一致性等问题...
    8天玩转并行开发——第八天 用VS性能向导解剖你的程序
    8天入门wpf—— 第一天 基础概念介绍
    8天玩转并行开发——第六天 异步编程模型
    8天入门wpf—— 第八天 最后的补充
    6天通吃树结构—— 第二天 平衡二叉树
    8天入门wpf—— 第七天 画刷
  • 原文地址:https://www.cnblogs.com/killbug/p/2651524.html
Copyright © 2020-2023  润新知