BufferedInputStream实现原理分析

ufferedInputStream是一个带有缓冲区的输入流,通常使用它可以提高我们的读取效率,现在我们看下BufferedInputStream的实现原理: 
BufferedInputStream 内部有一个缓冲区,默认大小为8M,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源 (譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容部分或全部返回给用户.由于从缓冲区里读取数据远比直接 从物理数据源(譬如文件)读取速度快,所以BufferedInputStream的效率很高! 

在具体看源码之前,我们还需要了解BufferedInputStream的mark操作:void mark(int markLimit) 
当 你调用mark方法时,内部会保存一个markPos标志,它的值为目前读取字节流的pos位置,倘若你调用reset方法,这时候会把pos重置为 markPos的值,这样你就可以重读已经读过的字节.好像说的不是很清楚,那我们打个比方:有一段字节流是abcdefg, 当你读取完字母a调用mark方法(此时markPos指向字母b),接着你继续读取字母b,字母c,字母d,然后此时你调用reset方法(内部把 pos重置为markPos),当你再读取下一个字节的时候,你会发现你读取到的是b而不是字母e,这样通过mark方法我们就是实现了重复读(re- read the same bytes) 

mark 方法中还有个参数markLimit,它的值表示在调用mark方法后reset方法前最多允许读取的字节数(根据我的测试,以及查看源代码发现,这个最 大字节数,其实是由markLimit和buffer.size中较大的那个决定的),如果超过这个限制,则在调用reset方法时会 报:Reseting to invalid mark 异常 
说完了这么多,让我们来赶紧看看源码吧: 

  1. // BufferedInputStream主要有这两个构造方法  
  2.   
  3. public BufferedInputStream(InputStream in) {  
  4.      this(in, defaultBufferSize);   // 默认缓冲区为8M  
  5.     }  
  6.   
  7. public BufferedInputStream(InputStream in, int size) {  
  8.     super(in);  
  9.     if (size <= 0) {  
  10.         throw new IllegalArgumentException("Buffer size <= 0");  
  11.     }  
  12.     buf = new byte[size];  
  13. }  

你需要指定InputStream(装饰模式的体现)以及bufferSize(可选) 
当我们调用read()方法时,它在内部做了一下事情: 

  1. public synchronized int read() throws IOException {  
  2.     if (pos >= count) {         // 检查是否有可读缓冲数据  
  3.         fill();                 // 没有缓冲数据可读,则从物理数据源读取数据并填充缓冲区  
  4.         if (pos >= count)       // 若物理数据源也没有多于可读数据,则返回-1,标示EOF  
  5.         return -1;  
  6.     }  
  7.   
  8.  // 从缓冲区读取buffer[pos]并返回(由于这里读取的是一个字节,而返回的是整型,所以需要把高位置0)  
  9.     return getBufIfOpen()[pos++] & 0xff;     
  10. }  
  11.   
  12. private byte[] getBufIfOpen() throws IOException {  
  13.     byte[] buffer = buf;      // buf为内部缓冲区  
  14.     if (buffer == null)  
  15.         throw new IOException("Stream closed");  
  16.         return buffer;  
  17. }  


其中pos为缓冲区buffer下一个可读的数组下标,我们可以一直从缓冲区里读取数据,直到pos变为count(此时只能从物理数据源读取数据),下面我们就分析下,当缓冲区里没有数据可读时,BufferInputStream是如何处理的: 

1. 若用户没有开启re-read功能(即未调用mark方法),当pos == count时,我们只需要将pos重新置为0,然后从物理源读取数据(假设读到了n个字节),最后把count设置成 n + pos 即可 (其实就是n,因为pos之前被设置成了0), 当下次你在调用read方法时,就直接从缓冲读取,非常快速(如下图); 


 

2.若用户调用了mark方法,情况就变得很复杂了,为什么呢? 这意味着我们需要保存从markPos到pos这段数据(以供用户re-read),现在我们分情况讨论: 
a.此时pos < buffer.length,这意味着缓冲区还有多余空间,所以我们可以继续从物理数据源读取数据放入到缓冲区中(如下图); 


 

b.pos == buffer.length,这意味着缓冲区已经没有多余空间,所以只能清空缓冲区内容,但是不要忘了,我们还必须保留原来 
markPos到pos那段数据,以供用户re-read,所以我需要这样做: 

  1. // 计算需要保留多少字节的数据  
  2. int sz = pos - markPos;     
  3. // 然后拷贝到缓冲头部                                
  4. System.arraycopy(buffer, markpos, buffer, 0, sz);   
  5. // 重置pos以及markPos        
  6. pos=sz;                                                                                  
  7. markPos=0;                          

接着从缓冲区的sz 到 buffer.length又变成可用区间,用来存放从物理数据源读到的数据(如下图) 


 

到 这里似乎问题完美的解决了,其实不然,我们忘记考虑markPos失效问题,以及若pos - markPos == buffer.length,那么移了等于白移动,还是没有挪出多余空间,所以实我们应该这样做(后面讨论都是建立在pos == buffer.length的基础上): 


2.1 若markPos > 0, 那么 pos - makrPos一定小于缓冲区大小,这样意味着我们按照刚才的算法挪动后,缓冲区就有了空余空间 


 
2.2 若makrPos == 0,  这意味着需要保存的数据满满的充斥着缓冲区,所以这时候我们是无法通过挪动位置来使缓冲区有多余空间的,所以我们只可以清空或扩展缓冲区 
2.2.1 当buffer.length >= marklimit时 ,此时意味着markPos已经失效,用户不可以在进行re-read,所以此时我们就可以简单释放整个缓冲区了:pos=0, markPos=-1; 
2.2.2 其余情况,意味着markPos还有效,所以我们只能通过扩展缓冲区的方式来使缓冲区有多余空间 


 
说了这么多,我们还是看下相关代码吧: 

  1. private void fill() throws IOException {  
  2.         byte[] buffer = getBufIfOpen();        // 得到当前缓冲区  
  3.      if (markpos < 0)                          // 对应情况1  
  4.          pos = 0;           
  5.      else if (pos >= buffer.length)            // 对应情况2  
  6.          if (markpos > 0) {                    // 对应情况2.1  
  7.                int sz = pos - markpos;  
  8.                System.arraycopy(buffer, markpos, buffer, 0, sz);  
  9.                pos = sz;  
  10.                markpos = 0;  
  11.          } else if (buffer.length >= marklimit) {  // 对应情况2.2.1  
  12.                markpos = -1;      
  13.                pos = 0;      
  14.          } else {                                  // 对应情况2.2.2  
  15.                int nsz = pos * 2;  
  16.                if (nsz > marklimit)  
  17.                    nsz = marklimit;  
  18.                byte nbuf[] = new byte[nsz];  
  19.                System.arraycopy(buffer, 0, nbuf, 0, pos);  
  20.             ...  
  21.          }  
  22.     count = pos;  
  23.      int n = getInIfOpen().read(buffer, pos, buffer.length - pos);  
  24.     if (n > 0)  
  25.         count = n + pos;  
  26. }  


好 了关于BufferedInputStream就说道这里,它的 read(byte b[], int off, int len)其实内部实现也大概如此:先从缓冲区读,如果读不到则从物理数据源读取并刷新到缓冲区(可能需要对缓冲区原来内容作必要的挪动或者对缓冲区大小进 行扩展)

 

[转自 http://www.software8.co/wzjs/java/1770.html]

微信小程序 - 实现Java微信支付退款功能以及退款流程,微信小程序前端+java后端完整示例源码,微信小程序退款功能详解,小程序订单申请退款和退款回调方法、查询退款等(新版微信支付,详细示例代码) 微信小程序开发-微信支付退款功能以及退款回调方法,微信支付退款API详解,微信小程序支付V3(支付、退款)Java实现微信小程序开发 小程序支付开发-小程序退款功能,微信小程序小程序和公众号 退款功能教程(含申请退款和退款回调,退款回调地址在商户后台配置或者代码自定义)最新版微信小程序支付,退款问题,小程序支付和退款功能详解,小程序申请退款,小程序处理退款,微信小程序支付与退款java,微信小程序退款及回调,JAVA实现微信小程序支付退款功能,退款api调用报错,如何解决退款申请无响应,如何用实现小程序订单 阅读详情

相关推荐

Vscode+ROS/ROS2 开发调试基于ROS/ROS2应用环境配置

在ROS操作系统开发应用程序时,我们常常需要调试代码,验证程序的正确性。但是在用rosrun或者roslaunch启动节点后,若程序运行过程中发生错误而导致发生出错停止工作,这时候终端并不会输出具体的信息指出在何处发生了错误,具体错误的原因又是什么。往往仅仅是core dump 或者segmentation fault 等等一类的模糊提示。

Chasinglight的博客 5800

Java的BufferedInputStream类

  BufferedInputStream是缓冲输入流,继承于FilterInputStream,作用是为另一个输入流添加一些功能,本质上是通过一个内部缓冲数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当通过read读取数据时,BufferedInputStream会将输入流的数据分批地填入到缓冲区中,每当缓冲区的数据读完之后,输入流会再次填充数据缓冲区,直到读完数据。   BufferedInputStream主要的函数如下: BufferedInputStream(In

健身房 2884

一图文带您认识磁编码器

磁编码器是以磁电原理技术生产的编码器。 磁编码器工作原理: 当电流通过一个位于磁场中的导体的时候,磁场会对导体中的电子产生一个垂直于电子运动方向上的的作用力,从而在垂直于导体与磁感线的方向上产生电势差。 如果让施加在这个导体上的磁场以电流流经路径为轴线,按照上图箭头所示的方向旋转,那么这个霍尔电势差就会因为磁场与导体之间角度的改变而发生变化,而这个电势差的变化趋势,与之前一文中次级线圈旋转时的输出电压一样,是一条正弦曲线。因此,基于这个通电导体两侧的电压,就可以反推计算出磁场旋转的角度了。 .

encoder_yang的博客 2万+

Java BufferedInputStream mark()方法与示例

BufferedInputStream类mark()方法 (BufferedInputStream Class mark() method) mark() method is available in java.io package. mark()方法在java.io包中可用。 mark() method is used to set the limit of bytes to be read ...

947

Proteus 8.17安装部署全指南:避坑、汉化与STM32库配置

Proteus是一款广泛用于单片机仿真与电路设计的EDA工具,其核心价值在于支持嵌入式系统原型验证与教学实践。Proteus 8.17版本重构了许可证机制(LIC Engine)、依赖环境(VC++ 2019/NET 4.8)及路径加载逻辑,导致传统安装方法失效。掌握正确安装部署流程,是保障原理图绘制、VSM仿真(如51/STM32/ESP32)及外设波形观测(OLED、SPI、ADC)稳定运行的前提。本文聚焦Windows平台下零失败安装路径选择、权限配置、汉化实施与高频器件(如STM32F103)元件库

weixin_33377602的博客 402

详解机器人标定

这里是机器人在取料之前,先把自己的角度补正到与 物料当前角度一致,到(X’,Y’)位置处,然后移动 CDx,Cdy,与物料位置也重合,然后去取料。可以使用实物标定,机器人抓住工件摆放9个位置,得到9个机械坐标,相机也得到9个像素坐标,(机器人每次需要回到固定位置拍照),然后标定。(X0,Y0)为旋转中心, (X1,Y1)及(X2,Y2)为工件在视野中旋转角度a的前后坐标,(Xt,Yt)为两点连线的延长。(rx0, ry0)为旋转中心,( x, y)为被旋转的点,(x0,y0)旋转后的点。

weixin_56819992的博客 7631

java.io.BufferedInputStream.mark()方法实例

java.io.BufferedInputStream.mark(int)方法通过设置int值的字节之前,标记现在的位置为无效读取。 更多教程请访问http://www.manongzj.com 声明 以下是java.io.BufferedInputStream.mark()方法的声明 public void mark(int readlimit) 参数 readLimit-- 在标记位置变得无效之前的被读取的字节数。 返回值 此方法不返回任何值。 异常 NA 例子 ..

wuxiaopengnihao1的博客 268

SAP MM 物料描述MAKTX 40个字符不够用的问题处理

SAP物料描述表,长度40个字符一般情况下,40个字符在描述中也够用了,但是要加上规格之类的额外描述就不一定了,特别是对于辅助件-刀具。解决方案一:对maktx增强,换成CHAR80或者CHAR100的,但是风险较大,涉及到好多地方,另外SAP+ 算号器 也不支持。解决方案二:增强字段。内部顾问,建议在 那就在这里加一个字段,具体过程如下:1、创建域2、创建架构3、附加架构4、GUI 设置 5、后台设置6、MASS/MM17字段新增。过程1、创建域 SE112、创建结构 se11 3、MARA附加架构

GAVIN 4728

java bufferedinputstream 编码_Java BufferedInputStream mark()用法及代码示例

Java中BufferedInputStream类的mark()方法用于标记输入流中的当前位置。在mark()方法之后,调用相同类BufferedInputStream的Reset()方法。 Reset()将位置固定在最后标记的位置,以便可以再次读取相同的字节。常规合同:输入流以某种方式保存了在调用mark()方法之后读取的所有字节,并在调用reset()方法时再次返回相同的字节。仅当markSu...

weixin_39860201的博客 374

航空电子系统标准全景图:ARP4754A、ARP4761、DO-178C、DO-254 的协同作用

通过 ARP4754A、ARP4761、DO-178C 和 DO-254 的相互配合,确保航空电子系统的安全性和功能性在开发过程中得到充分保障。每一个文件在不同层面发挥作用,共同构成了一个完整的、系统的开发和验证框架,确保航空电子设备达到适航要求。这些标准不仅帮助开发人员有条理地开发复杂的航空系统,还确保所有系统组件(软件和硬件)在整个生命周期内都能满足最严格的安全和可靠性要求。

qq_41854911的博客 1812

使用Python与OpenCV实现基于霍夫变换的圆形缺陷检测与定位详细教程

本文详细探讨了如何使用Python和OpenCV库,通过图像边缘检测和霍夫变换实现圆形对象的检测与缺陷定位。我们不仅介绍了算法的基本原理,还通过具体的代码示例展示了如何在实际应用中实现这些技术。对于工业检测和医学影像分析,本文的方法提供了高效且易于实现的解决方案。随着图像处理技术的不断发展,未来我们可以期待更多新算法和新技术的应用,如深度学习与传统方法的结合,以及多传感器融合技术的引入,这将进一步提升缺陷检测与定位的效率和准确性。

qq_38334677的博客 1295

Java常用类 - BufferedInputStream实现原理分析

BufferedInputStream是一个带有缓冲区的输入流,通常使用它可以提高我们的读取效率,现在我们看下BufferedInputStream的实现原理: BufferedInputStream内部有一个缓冲区,默认大小为8M,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取 失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读...

TrustNature 205

针对小缺陷目标:在YOLOv11中引入小目标检测层

摘要: 针对YOLOv11在工业检测中漏检微小目标(如焊点气泡)的问题,研究发现深层特征下采样导致小目标信息丢失。通过在YOLOv11中增加P2检测层(4倍下采样)并融合浅层高分辨率特征,可显著提升小目标召回率。关键优化包括:调整模型结构、谨慎设计数据增强策略(减少MixUp、增加小目标粘贴)、重新分配损失权重(提升P2层权重)、优化推理速度(动态分辨率、通道裁剪)。同时强调数据质量的重要性,建议根据具体场景特性(目标尺寸、分布)定制方案,而非盲目调整模型。

as1225120794的博客 366

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

JDK之BufferedInputStream分析

最近写了一个下载文件的功能。 因为文件是在另一台服务器上面,所以要读取网络文件,需用到HttpURLConnection类。 先贴出来代码。 import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; im

业精于勤,荒于嬉;行成于思,毁于随。 948

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Java之 BufferedInputStream详解 (缓冲输入流)的认知、源码和示例

本章内容包括3个部分:BufferedInputStream介绍,BufferedInputStream源码,以及BufferedInputStream使用示例。 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_12.html BufferedInputStream 介绍 BufferedInputStream 是缓冲输入流。它继

Android Linux MCU VC++ 3194

Java中IO框架——BufferedInputStream源码解析

BufferedInputStream 继承自 FilterInputStream 。 InputSteam 的源码分析见博客:http://blog.youkuaiyun.com/yx0628/article/details/79343633 FilterInputStream 的源码分析见博客:http://blog.youkuaiyun.com/yx0628/article/details/79366049 ...

在梅边的专栏 602

java io系列12之 BufferedInputStream(缓冲输入流)的认知、源码和示例

本章内容包括3个部分:BufferedInputStream介绍,BufferedInputStream源码,以及BufferedInputStream使用示例。 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_12.html BufferedInputStream 介绍 BufferedInputStream 是缓冲输入流。它继

XINJing的专栏 578
上一篇: CountDownLatch和CyclicBarrier的区别
下一篇: Java IO流分析整理
lucklyming
博客等级 码龄19年 0粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值