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 ...
随机推荐
- jdk包结构及用途分析
Table of Contents 概述 jdk包总览 rt.jar包结构分析 概述 jdk是每一个使用java的人员每一天都在使用的东西,博主也已经研究了jdk源代码中的一些类了,本篇博客是想从jd ...
- win7-64位 jdk安装
1.jdk安装 jdk安装主要是进行jdk以及jre安装,注意jre需要安装到一个空文件夹内即可. 官网地址:http://www.oracle.com/technetwork/java/javase ...
- 使用qmake编译时拷贝文件
使用qmake构建项目时,需要在make时拷贝一些文件到指定位置,非执行make install,下面总结列举一下我了解的方式 COPIES 示例pro 1 COPY_DIR = $$PWD/copy ...
- 初识redis协议
有关redis协议信息(https://redis.io/topics/protocol) 搭建环境 //jedis连接客户端 public class RedisClient { public st ...
- rsync未授权访问漏洞复现
rsync未授权访问漏洞简介 rsync是Linux/Unix下的一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件和目录,默认运行在873端口.由于配置不当,导致任何人可未授权访问r ...
- php 抛出异常
<?php //try里面执行的东西如果不成立,可直接 throw new Exception('异常信息'),那么try里面的程序将会被停止执行,直接执行catch里面的程序 try { if ...
- Talk About My Route To Edit
Initially, I plan to write 4 columns:Python, Qt, Linux,and respectively, Career. However, my ambitio ...
- guitar pro系列教程(十四):Guitar Pro教程之创建新乐谱后的设置
前面的章节我们有对Guitar Pro的单个功能作介绍,对于初学作曲,且又是吉他初学者的朋友们来说,学完这些功能介绍,自己还不能融会贯通起来,创建了一个新的乐谱后,但是看起来还不是很满意,今天我们就创 ...
- objetive-C中属性变量和成员变量
属性变量 @property和@synthesize可以自动生成某个类成员变量的存取方法. readwrite:这个属性是默认的情况,会自动生成存取器 assign:这个属性一般用来处理基础类型,比如 ...
- 【Updating】汇编语言学习记录02
换码指令.字符的输出 前置知识: XLAT 指令:将BX指定的缓冲区中.AL指定的位移处的一个字节数据取出赋给AL,实际相当于(AL) = (DS:(BX+AL)).注意,不是单纯地赋予AL+BX,而 ...