概述 : ByteArrayInputStream(字节数组输入流)
简介
- 字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。
- java.io.ByteArrayInputStream : 自 JDK 1.0 起即有此类
创建对象的方式
创建字节数组输入流对象有以下几种方式。
- 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 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 result = new Map.Entry() { @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 readResult = bytesReader.readBytes(readSize); int actualLength = readResult.getKey(); byte [] readBytes = readResult.getValue(); log.info("bytes[offset={}] : 0x{}, actualLength:{}", offset, BytesUtils.bytesToHexString( readBytes ), actualLength );//eg: "bytes[offset=0] : 0x010203, actualLength:3" , "bytes[offset=15] : 0x160000, actualLength:1" } catch (Exception e) { e.printStackTrace(); } } } }
复制代码out - [2025/06/26 14:12:54.921] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=0] : 0x010203, actualLength:3 [2025/06/26 14:12:54.927] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=3] : 0x040506, actualLength:3 [2025/06/26 14:12:54.928] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=6] : 0x070809, actualLength:3 [2025/06/26 14:12:54.929] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=9] : 0x101112, actualLength:3 [2025/06/26 14:12:54.929] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=12] : 0x131415, actualLength:3 [2025/06/26 14:12:54.930] [INFO ] [main] [com.xxx.sdk.utils.bytes.BytesReaderTest :27 readBytesTest] bytes[offset=15] : 0x160000, actualLength:1
复制代码 Y 推荐文献
- [Java/HexStringReader] 核心源码精讲:java.io.StringReader类(JDK1.1-) - 博客园/千千寰宇
- [Java SE] 基础工具类:ByteUtils(字节操作) - 博客园/千千寰宇
X 参考文献
- Java ByteArrayInputStream类 - 菜鸟教程
本文作者: 千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |