Java I/O系列(二)ByteArrayInputStream与ByteArrayOutputStream源码分析及理解
1. ByteArrayInputStream
定义
继承了InputStream,数据源是内置的byte数组buf,那read ()方法的使命(读取一个个字节出来),在ByteArrayInputStream就是简单的通过定向的取buf元素实现的
核心源码理解
源码:
public ByteArrayInputStream(byte buf[], int offset, int length) {
this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.mark = offset;
}
理解:
1. 构造ByteArrayInputStream, 直接将外部的byte数组作为内置的buf,作为被读取的数据源
源码:
// 存放数据的地方
protected byte buf[]; // 下一个要被读取的位置,即等待读取的位置
protected int pos; // 标记pos的位置
protected int mark = 0; // 实际能被读取的byte的数量
protected int count;
理解:
源码:
public synchronized int read() { return (pos < count) ? (buf[pos++] & 0xff) : -1;}
理解:
1. 该方法是被synchronized修饰的,其它方法也是,故ByteArrayInputStream是线程安全的
2. byte类型和0xff做与运算,转成byte的无符号类型(0-255),上节也说明过
源码:
public synchronized int read(byte b[], int off, int len) {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} if (pos >= count) {
return -1;
} int avail = count - pos;
if (len > avail) {
len = avail;
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
理解:
1. 因为数据源是byte数组,目的源也是byte数组,故直接采用了数组copy的方法,写入到b数组中
源码:
public synchronized long skip(long n) {
long k = count - pos;
if (n < k) {
k = n < 0 ? 0 : n;
} pos += k;
return k;
}
理解:
1. 通过调整pos的值,来实现skip操作
源码:
public void close() throws IOException { }
理解:
1. 空实现,故close后并不会有任何影响,还是可以继续往外读的
2. ByteArrayOutputStream
定义
继承了OutputStream,目的源是内置的byte数组buf,write()方法是一个个字节写入到buf中(其实就是简单到buf[i]=b,这种赋值操作)
核心源码理解
源码:
// 目的源,存储数据的地方
protected byte buf[]; // buf中实际的byte数(不是buf.length)
protected int count;
public ByteArrayOutputStream() { this(32); }
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
理解:
1. 默认构造ByteArrayOutputStream的buf长度是32;可以指定存储数据的buf数组的长度
源码:
public synchronized void write(int b) {
ensureCapacity(count + 1);
buf[count] = (byte) b;
count += 1;
}
理解:
1. 重写了InputStream的write(int b) 方法,直接给buf某个元素赋值;
2. 被synchronized修饰(包括其它方法),因此ByteArrayOutputStream是线程安全的
3. ensureCapacity,是判断当前的容量和buf.length的大小,如果超过了buf.length,则会对buf进行扩容(申请一个更大的数组,通过Arrays.copyOf方法进行旧元素copy,让buf指向这个新的大数组)
源码:
public synchronized void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);
System.arraycopy(b, off, buf, count, len);
count += len;
}
理解:
1. 参数b数组是数据源,待被读取的字节数组,由于目的源是内置的字节数组buf,直接通过数组copy的形式完成写入操作
2. 与ByteArrayInputStream的read(byte b[], int off, int len)几乎一样,只是内置的buf数组和参数b数组的数据源角色和目的源角色互调下而已
源码:
public synchronized byte toByteArray()[] { return Arrays.copyOf(buf, count); }
public synchronized String toString() {
return new String(buf, 0, count);
}
public synchronized String toString(String charsetName)
throws UnsupportedEncodingException
{
return new String(buf, 0, count, charsetName);
}
理解:
1. 这三个方法都是取出内置的buf数据,比较容易理解
源码:
public void close() throws IOException { }
理解:
1. close后还是可以继续写入的
3. 总结:
1. 实现了mark与reset方法,mark方法中让mark=pos,reset时让pos=mark,比较容易理解
4. 问题:
1. 欢迎大家提出问题,共同交流学习!
5. 示例:
/**
* ByteArrayInputStream的read是从自己内置的buf中读,read的数据源是buf
* ByteArrayOutputStream的write是写入到自己内置的buf中去,write的目的源是buf
*/
public static void test1() {
// 对应abcddefghijklmnopqrsttuvwxyz的ASCII码十六进制分别为
byte[] bytes = new byte[] { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
}; // 0x64, 0x65, 0x66, 0x67, 0x68
ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 3, 5);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); //默认内置的buf长度为32哦
int b = 0;
while((b = bis.read()) != -1) {
System.out.println("ASCII码的十进制: " + b); //将读到的字节,再写入到bos中的内置buf中
bos.write(b);
} // 虽然close都是空实现,但养成一个关闭资源(比如流,连接)的习惯
try {
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
} System.out.println("bos的内置buf数据: " + bos.toString());
}
执行:
public static void main(String args[]) {
test1();
}
结果:
ASCII码的十进制: 100
ASCII码的十进制: 101
ASCII码的十进制: 102
ASCII码的十进制: 103
ASCII码的十进制: 104
bos的内置buf数据: defgh
6. 参考:
1. http://www.cnblogs.com/skywang12345/p/io_03.html
Java I/O系列(二)ByteArrayInputStream与ByteArrayOutputStream源码分析及理解的更多相关文章
- Spring源码系列(二)--bean组件的源码分析
简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...
- java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)
我们以ByteArrayInputStream,拉开对字节类型的“输入流”的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的 ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- 图解Janusgraph系列-图数据底层序列化源码分析(Data Serialize)
图解Janusgraph系列-图数据底层序列化源码分析(Data Serialize) 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 图数据库文章总目录: 整理所有图相关文章,请移步 ...
- Eureka 系列(04)客户端源码分析
Eureka 系列(04)客户端源码分析 [TOC] 0. Spring Cloud 系列目录 - Eureka 篇 在上一篇 Eureka 系列(01)最简使用姿态 中对 Eureka 的简单用法做 ...
- Java 序列化和反序列化(三)Serializable 源码分析 - 2
目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass ...
- Java 集合系列(四)—— ListIterator 源码分析
以脑图的形式来展示Java集合知识,让零碎知识点形成体系 Iterator 对比 Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素. Iterator 包含四个方法 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
随机推荐
- javascript中闭包最简单的简绍
javascript中闭包是什么 JavaScript 变量可以是局部变量或全局变量.私有变量可以用到闭包.闭包就是将函数内部和函数外部连接起来的一座桥梁. 函数的闭包使用场景:比如我们想要一个函数来 ...
- css3在页面中插入内容
A. 使用选择器来插入内容 h2:before{ content:"前缀"; } h2:after{ content:"后缀"; } B. 指定个别的元素不进行 ...
- 移动端App开发 - 02 - iPhone/iPad/Android UI尺寸规范
移动端app开发 - iPhone/iPad/Android UI尺寸规范 本笔记抛去无用的前期分析什么的,全是干货,简洁干练 本笔记不单独针对 ios 或者 Android,两种都介绍,当然我们实际 ...
- 《Python指南》学习笔记 一
更新时间:2018-06-14 <Python指南>原文在这里.本篇笔记主要是划重点. Python 3.6.3 1.简单入门 1.1 编码 默认情况下,Python 源文件是 UTF-8 ...
- [翻译] JNWSpringAnimation
JNWSpringAnimation https://github.com/jwilling/JNWSpringAnimation JNWSpringAnimation is a subclass o ...
- 什么是TTL值?(简单明了的解释)
什么是TTL值? TTL值全称是“生存时间(Time To Live)”,简单的说它表示DNS记录在DNS服务器上的缓存时间. 要理解TTL值,请先看下面的一个例子:假设,有这样一个域名myhost. ...
- 6、tornado
3反射(reflect) tornado是一款异步非阻塞高效的web框架 自定制session.form.auth,以及第三方ORM框架sqlachme
- Java学习---多线程的学习
基础知识 每个正在系统上运行的程序都是一个进程(process).每个进程包含一到多个线程(thread).进程也可能是整个程序或者是部分程序的动态执行. 线程是一组指令的集合,或者是程序的特殊段,它 ...
- Linux下php-fpm进程过多导致内存耗尽问题
这篇文章主要介绍了解决Linux下php-fpm进程过多导致内存耗尽问题,需要的朋友可以参考下 最近,发现个人博客的Linux服务器,数据库服务经常挂掉,导致需要重启,才能正常访问,极其恶心,于是 ...
- contextlib
contextlib with 语句 上下文 任何对象,只要正确实现了上下文管理,就可以用于with语句. 实现上下文管理是通过__enter__和__exit__这两个方法实现的. 例如,下面的 ...