Java IO源码分析(二)——ByteArrayInputStream 和 ByteArrayOutputStream
简介
ByteArrayInputStream 是字节数组输入流,它继承于InputStream。
它的内部数据存储结构就是字节数组。
ByteArrayOutputStream是字节数组输出流,它继承于OutputStream。
它的内部数据存储结构也是字节数组。
源码分析
InputStream
在分析ByteArrayInputStream之前,应该先看InputStream,父类InputStream是ByteArrayInputStream的父类,主要实现了读取和跳跃的方法。
public abstract class InputStream implements Closeable {
// 最大可跳过的字节数
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
// 向后读取一个字节
public abstract int read() throws IOException;
// 将字节流中的数据装到字节数组的0位开始的位置
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
// 将字节流中的数据装到字节数组的指定位置当中
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
// 跳过输入流中的n个字节
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
// 是否还有
public int available() throws IOException {
return 0;
}
// 关闭流
public void close() throws IOException {}
// 标记
public synchronized void mark(int readlimit) {}
// 重置
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
// 是否支持标记方法
public boolean markSupported() {
return false;
}
}
ByteArrayInputStream
public
class ByteArrayInputStream extends InputStream {
// 字节数组,存储数据
protected byte buf[];
// 记录当前可读的第一个位置
protected int pos;
// 标记的位置
protected int mark = 0;
// 数据最大的可读长度
protected int count;
// 初始化字节流数组
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
// 初始化字节流,填入字节数组的指定位置
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;
}
// 读取单个字节
public synchronized int read() {
// 这里& 0xff的操作是为了只取低八位
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
// 读取数据到数组的指定位置
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;
}
// 跳过指定长度的字节
public synchronized long skip(long n) {
long k = count - pos;
if (n < k) {
// 如果是负数那么就不动
// 取n和k最小的一个
k = n < 0 ? 0 : n;
}
// 移动k位
pos += k;
return k;
}
// 是否还有数据可以读
public synchronized int available() {
return count - pos;
}
// 是否支持标记功能
public boolean markSupported() {
return true;
}
// 标记当前位置,这个传入参数是个摆设
public void mark(int readAheadLimit) {
mark = pos;
}
// 重置,也就是将当前指针指向之前mark的位置
public synchronized void reset() {
pos = mark;
}
// 关闭字节流
public void close() throws IOException {
}
}
OutputStream
OutputStream是ByteArrayOutputStream的父类,先看看它的源码。
很短,实现了Closeable, Flushable。
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
public void flush() throws IOException {
}
public void close() throws IOException {
}
}
ByteArrayOutputStream
public class ByteArrayOutputStream extends OutputStream {
// 存储数据的字节数组
protected byte buf[];
// 数组长度
protected int count;
// 默认构造,默认大小是32
public ByteArrayOutputStream() {
this(32);
}
// 初始化长度的构造
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
// 初始化一个数组对象
buf = new byte[size];
}
// 查看是否需要扩容
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0)
grow(minCapacity);
}
// 数组的最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = buf.length;
// 默认先扩容两倍
int newCapacity = oldCapacity << 1;
// 如果还是不够就将容量扩到需求的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 边界判断
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将旧数组复制到新数组
buf = Arrays.copyOf(buf, newCapacity);
}
// 边界判断
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
// 写入单个字节
public synchronized void write(int b) {
// 先判断是否需要扩容
ensureCapacity(count + 1);
// 写入字节
buf[count] = (byte) b;
// 增加可用长度
count += 1;
}
// 写入字节数组
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;
}
// 将当前Stream输出到指定的Streamz中
public synchronized void writeTo(OutputStream out) throws IOException {
// 直接将可用长度的数组写入
out.write(buf, 0, count);
}
// 重置
public synchronized void reset() {
count = 0;
}
// 转化为字符数组
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
// 获取可用长度
public synchronized int size() {
return 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);
}
@Deprecated
// 获取高位的字节
public synchronized String toString(int hibyte) {
return new String(buf, hibyte, 0, count);
}
public void close() throws IOException {
}
}
总结
输入输出流的本质就是一个中间缓存器,暂时将数据放在中间的缓存区,然后根据指定要求输出或如输入。
ByteArrayInputStream 特点
- 数组实现中间缓存;
- 修改和读取操作都是线程安全了,因为加了synchronized;
- 具有标记回读的功能,就是可以先读后面的数据,然后经过重置,再去读前面标记位置的数据。
ByteArrayOutputStream特点
- 数组实现中间缓存;
- 修改读取也是具有线程安全的;
- 具有扩容功能,应为想要写入的数据是增长的,在写入之前,就会进行依次扩容判断;
- 默认的初始大小是32,如果一个个数据写入的扩容,每次是扩一倍的大小;
- 可以写入到其他的输出流上。
Java IO源码分析(二)——ByteArrayInputStream 和 ByteArrayOutputStream的更多相关文章
- Java IO源码分析(三)——PipedOutputStream和PipedInputStream
简介 PipedOutputStream和PipedInputStream主要用于线程之间的通信 .二者必须配合使用,也就是一段写入,另一端接收.本质上也是一个中间缓存区,讲数据缓存在PipedInp ...
- 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>
目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...
- java io 源码研究记录(一)
Java IO 源码研究: 一.输入流 1 基类 InputStream 简介: 这是Java中所有输入流的基类,它是一个抽象类,下面我们简单来了解一下它的基本方法和抽象方法. 基本方法: publ ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- java集合源码分析(三):ArrayList
概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...
- java集合源码分析(六):HashMap
概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...
- Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题
4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...
- Java Reference 源码分析
@(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...
- Java 集合源码分析(一)HashMap
目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...
随机推荐
- 1-03 Java的基本程序设计结构
1-03 Java的基本程序设计结构 3.1 & 3.2 在一个单词中间使用大写字母的方式称为骆驼命名法.以其自身为例,应该写成CamelCase). 与C/C++一样,关键字void表示这个 ...
- Markdown 常用语言关键字
Markdown 语法高亮支持的语言还是比较多的,记下来备用. 语言名 关键字 Bash bash CoffeeScript coffeescript C++ cpp C# cs CSS css Di ...
- [代码审计Day2] filter_var函数缺陷代码审计
简介 // composer require "twig/twig" require 'vendor/autoload.php'; class Template { private ...
- MySql的远程登录问题
1.linux中先连接数据库:mysql -uroot -p(密码) 2.在mysql命令行中输入: GRANT ALL PRIVILEGES ON *.* TO '登录id'@'%' IDENTIF ...
- Mac垃圾忘记清理?开启自动清理提醒帮助你!
Mac系统被广泛应用于各种职场生活和学习生活上,平时由于工作和学习忙碌,用户难免会忽略了对Mac的照料,Mac在日复一日地使用中,必定会逐渐变得"臃肿",系统中存在着各种文件缓存垃 ...
- C语言讲义——变量(variable)
变量(variable) 变量用于存放数据 变量是供程序操作的存储区的名字 变量有类型,该类型决定了变量占用内存的大小 字节→ C语言有以下6种简单变量类型: 类型细分: 变量在内存中需要占据空间,内 ...
- python -- 对list去重并找出列表list中的重复元素
一.一个列表中可能含有重复元素,使用set()可以实现列表的去重处理,但是无法知道哪些元素是重复的,下面的函数用于找出哪些元素重复了,以及重复的次数. 代码: from collections imp ...
- 浅谈Abp vNext的模块化设计
abp的模块化给我留下深刻的印象,模块化不是什么新概念,大家都习以为常,但是为什么要模块化,模块化的意义或者说目的是什么?也许我们思考得并不深入.难得的是abp不仅完美的阐述了模块化概念,而且把模块化 ...
- python之迭代器,生成器小结
1.凡是可作用于for循环的对象都是Iterable类型: 2.凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列: 3.集合数据类型如list.dict.str等 ...
- Django----Modelviewset继承
1.modelviewset 认证.权限.限流.序列化.分页.过滤.排序 modelviewset的应用场景是: 1.主要应用于数据接口 2.对数据库的增删改查 3.在视图函数中没有很多业务逻辑需要来 ...