概述 : ByteArrayInputStream(字节数组输入流)

简介

  • 字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。
  • java.io.ByteArrayInputStream

创建对象的方式

创建字节数组输入流对象有以下几种方式。

  • 方式1 接收字节数组作为参数创建:
ByteArrayInputStream bArray = new ByteArrayInputStream(byte [] a);
  • 方式2 接收一个字节数组,和两个整形变量 off、len,off表示第一个读取的字节,len表示读取字节的长度。
ByteArrayInputStream bArray = new ByteArrayInputStream(byte []a, int off, int len)

成功创建字节数组输入流对象后,可以参见以下列表中的方法,对流进行读操作或其他操作。

常用API

序号 方法描述
1 public int read() 从此输入流中读取下一个数据字节。
2 public int read(byte[] r, int off, int len) 将最多 len 个数据字节从此输入流读入字节数组。
3 public int available() 返回可不发生阻塞地从此输入流读取的字节数。
4 public void mark(int read) 设置流中的当前标记位置。
5 public long skip(long n) 从此输入流中跳过 n 个输入字节。

示例

下面的例子演示了ByteArrayInputStream 和 ByteArrayOutputStream的使用:

import java.io.*;

public class ByteStreamTest {

   public static void main(String args[])throws IOException {

      ByteArrayOutputStream bOutput = new ByteArrayOutputStream(12);

      while( bOutput.size()!= 10 ) {
// 获取用户输入值
bOutput.write(System.in.read());
} byte b [] = bOutput.toByteArray();
System.out.println("Print the content");
for(int x= 0 ; x < b.length; x++) {
// 打印字符
System.out.print((char)b[x] + " ");
}
System.out.println(" "); int c; ByteArrayInputStream bInput = new ByteArrayInputStream(b); System.out.println("Converting characters to Upper case " );
for(int y = 0 ; y < 1; y++ ) {
while(( c= bInput.read())!= -1) {
System.out.println(Character.toUpperCase((char)c));
}
bInput.reset();
}
}
}

out

asdfghjkly
Print the content
a s d f g h j k l y
Converting characters to Upper case
A
S
D
F
G
H
J
K
L
Y

最佳实践

基于封装 ByteArrayInputStream 的 BytesReader

BytesReader

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe; import java.io.*;
import java.lang.reflect.Field;
import java.util.Map; /**
* 以字节数据为源的读取器
* @note 核心原理: 基于字节流工具类 {@link ByteArrayInputStream } 读取字节数据
* @updateTime 2025.6.26 13:50
*/
@Slf4j
public class BytesReader implements MySourceReader {
private ByteArrayInputStream byteArrayInputStream; public BytesReader(byte [] bytes) {
this.byteArrayInputStream = new ByteArrayInputStream(bytes);
} public BytesReader(byte [] bytes, int offset, int length) {
this.byteArrayInputStream = new ByteArrayInputStream(bytes, offset, length);
} public int read(){
return this.byteArrayInputStream.read();
} /**
* 读取1个字节
* @return
*/
public byte readByte(){
int byteSize = 1;
byte [] targetBuffer = new byte[byteSize];
int off = 0;//目标字节数组的起始位置
int readSize = read(targetBuffer, off, byteSize);
if(log.isDebugEnabled()){
log.debug("off:{}, readSize:{}", off, readSize);
}
return targetBuffer[0];
} public Map.Entry<Integer, byte[]> readBytes(int length){
byte [] destinationBuffer = new byte[ length];
int destinationBufferOffset = 0;
int destinationBufferLength = destinationBuffer.length;
int actualLength = this.byteArrayInputStream.readNBytes(destinationBuffer, destinationBufferOffset, destinationBufferLength); final Map.Entry<Integer, byte[]> result = new Map.Entry<Integer, byte[]>() {
@Override
public Integer getKey() {
return actualLength;
} @Override
public byte[] getValue() {
return destinationBuffer;
} @Override
public byte[] setValue(byte[] value) {
throw new RuntimeException("Not support set value method!");
}
};
return result;
} /**
* 从指定的位置读取最多 length 个字节数据,并存放到 targetBuffer 中
* @param destinationBuffer 目标字节数组
* @param destinationBufferOffset 目标字节数组的起始位置 (容易理解错误,多注意)
* @param length 要读取的字节数
* @return
*/
public int read(byte destinationBuffer[], int destinationBufferOffset, int length){
return this.byteArrayInputStream.readNBytes(destinationBuffer, destinationBufferOffset, length);
}
public int read(byte destinationBuffer[], int length){
int destinationBufferOffset = 0;
return this.byteArrayInputStream.readNBytes(destinationBuffer, destinationBufferOffset, length);
}
public int read(byte destinationBuffer[]){
int destinationBufferOffset = 0;
int destinationBufferLength = destinationBuffer.length;
return this.byteArrayInputStream.readNBytes(destinationBuffer, destinationBufferOffset, destinationBufferLength);
} public long skip(int length){
return byteArrayInputStream.skip(length);
} /**
* 获取下一字节的位置
* @description
* 1. 利用反射原理,将 java.io.ByteArrayInputStream 的 private 属性 pos 读取出来
* 2. 不建议高频调用 (影响调用程序的性能)
* @return
*/
@SneakyThrows
public int next(){
int next = Integer.MIN_VALUE; //读取失败时,以此值为标志
//反射方法1 : Java 17 中需结合 VM Option 参数 : `--add-opens java.base/java.io=ALL-UNNAMED`
//java.lang.reflect.Field field = java.io.ByteArrayInputStream.class.getDeclaredField("pos");
//field.setAccessible(true);
//next = field.getInt( this );//读取 next 的值
////field.set(this, Integer.MIN_VALUE);//设置字段的值 //反射方法2: 基于 Unsafe
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 获取私有字段的偏移量
Field nextField = ByteArrayInputStream.class.getDeclaredField("pos");
long offset = unsafe.objectFieldOffset(nextField);
next = unsafe.getInt(this.byteArrayInputStream, offset);
//unsafe.putInt(byteArrayInputStream, offset, 10);// 设置字段值 return next;//next : 下标从 0 开始; 即将读取的下一个 char 的下标位置
} public Boolean hasNext(){
return byteArrayInputStream.available() > 0;
}
}

Demo


import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; import java.util.Map; @Slf4j
public class BytesReaderTest {
/**
* 读取字节数据
*/
@Test
public void readBytesTest(){
byte bytes [] = new byte [] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
BytesReader bytesReader = new BytesReader(bytes);
int offset = 0;
while (bytesReader.hasNext()) {
try {
offset = bytesReader.next();
//log.info("bytes[{}] : 0x{}", offset, BytesUtils.byteToHexString( bytesReader.readByte() ) );//每次读取1个字节 int readSize = 3;//每次读取的字节数
Map.Entry<Integer, byte[]> readResult = bytesReader.readBytes(readSize);
int actualLength = readResult.getKey();
byte [] readBytes = readResult.getValue();
log.info("bytes[offset={}] : 0x{}", offset, BytesUtils.bytesToHexString( readBytes ) );//eg: "bytes[offset=0] : 0x010203"
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

out

[2025/06/26 14:08:25.668] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest                     :27 readBytesTest] bytes[offset=0] : 0x010203
[2025/06/26 14:08:25.676] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=3] : 0x040506
[2025/06/26 14:08:25.677] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=6] : 0x070809
[2025/06/26 14:08:25.678] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=9] : 0x101112
[2025/06/26 14:08:25.679] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=12] : 0x131415
[2025/06/26 14:08:25.680] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=15] : 0x160000

Y 推荐文献

X 参考文献

[Java/字节流/BytesReader] 核心源码精讲: ByteArrayInputStream(字节数组输入流)的更多相关文章

  1. java中ReentrantLock核心源码详解

    ReentrantLock简介 ReentrantLock是一个可重入且独占式的锁,它具有与使用synchronized监视器锁相同的基本行为和语义,但与synchronized关键字相比,它更灵活. ...

  2. 3 手写Java HashMap核心源码

    手写Java HashMap核心源码 上一章手写LinkedList核心源码,本章我们来手写Java HashMap的核心源码. 我们来先了解一下HashMap的原理.HashMap 字面意思 has ...

  3. 手写 Java HashMap 核心源码

    手写 Java HashMap 核心源码 手写 Java HashMap 核心源码 上一章手写 LinkedList 核心源码,本章我们来手写 Java HashMap 的核心源码. 我们来先了解一下 ...

  4. Java内存管理-掌握类加载器的核心源码和设计模式(六)

    勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇文章介绍了类加载器分类以及类加载器的双亲委派模型,让我们能够从整体上对类加载器有 ...

  5. 6 手写Java LinkedHashMap 核心源码

    概述 LinkedHashMap是Java中常用的数据结构之一,安卓中的LruCache缓存,底层使用的就是LinkedHashMap,LRU(Least Recently Used)算法,即最近最少 ...

  6. 2 手写Java LinkedList核心源码

    上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...

  7. 1 手写Java ArrayList核心源码

    手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue ...

  8. Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...

  9. 并发编程之 SynchronousQueue 核心源码分析

    前言 SynchronousQueue 是一个普通用户不怎么常用的队列,通常在创建无界线程池(Executors.newCachedThreadPool())的时候使用,也就是那个非常危险的线程池 ^ ...

  10. HashMap的结构以及核心源码分析

    摘要 对于Java开发人员来说,能够熟练地掌握java的集合类是必须的,本节想要跟大家共同学习一下JDK1.8中HashMap的底层实现与源码分析.HashMap是开发中使用频率最高的用于映射(键值对 ...

随机推荐

  1. 一文彻底拿下HarmonyOS NEXT开发实战调试技巧

    > 程序员Feri一名12年+的程序员,做过开发带过团队创过业,擅长Java.嵌入式.鸿蒙.人工智能等,专注于程序员成长那点儿事,希望在成长的路上有你相伴!君志所向,一往无前! --- # 1. ...

  2. 图像处理中的 Gaussina Blur 和 SIFT 算法

    Gaussina Blur 高斯模糊 高斯模糊的数学定义 高斯模糊是通过 高斯核(Gaussian Kernel) 对图像进行卷积操作实现的. 二维高斯函数定义为 \[G(x, y, \sigma) ...

  3. Unity资源打包之Asset Bundle

    Asset Bundle的作用: 1.AssetBundle是一个压缩包包含模型.贴图.预制体.声音.甚至整个场景,可以在游戏运行的时候被加载: 2.AssetBundle自身保存着互相的依赖关系: ...

  4. Sa-Token v1.42.0 发布 🚀,新增 API Key、TOTP 验证码、RefreshToken 反查等能力

    Sa-Token 是一款 免费.开源 的轻量级 Java 权限认证框架,主要解决:登录认证.权限认证.单点登录.OAuth2.0.微服务网关鉴权 等一系列权限相关问题. 目前最新版本 v1.42.0 ...

  5. Mybatis的关联关系的配置

    例子1. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC &qu ...

  6. DeepSeekV3:写代码很强了

    以前,我是不信AI能最先替代程序员的. DeepSeek的热度虽然在降,但是能力在悄摸的迭代. 在今年2月中旬测试DeepSeekR1的时候,虽然被它的文本处理能力惊艳到,但是当时吐槽过几句它的编程水 ...

  7. 没错,Go 语言的函数参数没有引用传递方式

    这篇文章想浅浅地讲解 Go 语言函数参数传递的值拷贝. 一句话观点 Go语言中所有传递都是值传递,严格来说并不存在引用传递的概念.传递指针只是传递指针的值,并不是引用传递,只不过通过指针可以间接修改变 ...

  8. 前端传字符串,需要转List对象

    前端传字符串,需要转List对象 import com.alibaba.fastjson.JSONObject; List<LogySbjsJdsbqxxxAccount> param = ...

  9. 【经验】博客|Windows下,一键安装和部署 hexo-admin 插件(Hexo 静态博客)

    1. 在博客根目录下运行下列指令 npm install --save hexo-admin -y echo "hexo clean && hexo g -d"&g ...

  10. 【记录】Python3|用百度语音 API 朗读你的小说TXT

    百度语音合成官方教程_AI开放平台 百度语音合成官方demo_github.com 简单地写了一个按段落朗读文本的demo:DEMO链接_gitee.com. 有时候会请求不到数据,不知道是网络原因还 ...