Java--ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter

一、字节数组输入流ByteArrayInputStream
1、继承关系、属性

public class ByteArrayInputStream extends InputStream
//字节数组
protected byte buf[];
// pos指向数组中的某个指定位置
protected int pos;
//当前的标记位置,初始设置为0
protected int mark = 0;
// buf字节数组中比最后一位存放字节位置索引大1的位置
protected int count;

2、理解
从一个byte数组里读取数据到内存的缓冲区,然后利用输入流的对象对缓冲区数据进行特定操作。将byte数组里的数据读入缓冲区,然后操作这个缓冲区。
3、构造函数

///创建一个字节数组输入流,buf是指定字节数组类型的数据源
ByteArrayInputStream(byte[] buf)
//创建一个字节数组输入流,buf是指定字节数组类型的数据源,offset指在数组中开始读取数据的偏移量,length指定读取的元素个数
ByteArrayInputStream(byte[] buf,int offse,int length)

4、常用方法
(1)read()

    public synchronized int read() {
        // 这里只要当前游标位置没有走完最后一个字节,即pos<count
        // 就返回pos++位置的字节,转为int返回
        // 如果游标已经走到了count位置,此位置已经没有字节可读,返回-1
        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();
    }
    // 如果游标pos走到了count位置,已经没有字节可读,返回-1 
    if (pos >= count) {
        return -1;
    }
    // 剩余可读的字节数量avail,等于count减去当前读取位置
    // pos位置的当前还未读取,一直到最后一个有字节的下标 (count-1) 总共的个数为
    // (count-1)-pos+1 = count-pos
    int avail = count - pos;
    // 需要读取的字节数不能超过可读字节数
    if (len > avail) {
        len = avail;
    }
    // 需要读取的字节数如果不大于0,即不需要读取,那么直接返回读取的字节总数为0即可。
    if (len <= 0) {
        return 0;
    }
    // 执行数组拷贝,从buf的pos位置开始拷贝len个字节到b数组的off位置开始。
    System.arraycopy(buf, pos, b, off, len);
    // 数组复制完成后,游标pos要往后走len个长度
    pos += len;
    // 返回读取的字节总数len
    return len;
}

(2)skip()

        public synchronized long skip(long n) {
        // k为当前可读的字节数量
        long k = count - pos;
        // 如果需跳过的比剩余的可读字节少
        if (n < k) {
            // 那么需跳过多少字节就可以跳过多少
            k = n < 0 ? 0 : n;
        }
        // 上边如果n>k,即需跳过的字节比剩余字节还多,那么就全跳过即可
        // 这里游标要向后移动k个位置,因为跳过了k个字节
        pos += k;
        // 返回跳过的字节总数k
        return k;
    }

(3)available()

        public synchronized int available() {
        //返回字节数组中可以读取的剩余字节总数。(count-1) 是最后一个字节的位置,pos位置是当前位置(未读取到),则(count-1)-pos+1 = count-pos
        return count - pos;
    }

(4)mark()、reset()

 // 支持标记及重置位置
    public boolean markSupported() {
        return true;
    }
    // 将当前游标位置设置为标记点,此标记点可用于还原重置
    // 注意readAheadLimit无意义
    public void mark(int readAheadLimit) {
        mark = pos;
    }
    // 还原位置,将之前保存的标记为作为游标位置
    public synchronized void reset() {
        pos = mark;
    }

说明:
ByteArrayInputStream实际上是通过“字节数组”去保存数据。

(1) 通过ByteArrayInputStream(byte buf[]) 或 ByteArrayInputStream(byte buf[], int offset, int length) ,根据buf数组来创建字节流对象。
(2) read()的作用是从字节流中“读取下一个字节”。
(3) read(byte[] buffer, int offset, int length)是从字节流读取字节数据,并写入到字节数组buffer中。offset是将字节写入到数组的起始位置,length是写入长度。
(4) markSupported()是判断字节流是否支持“标记功能”。一直返回true。
(5) mark(int readlimit)的作用是记录标记位置。reset()之后再读取字节流时,是从mark()标记的位置开始读取。
示例:

import java.io.ByteArrayInputStream;

public class ByteArrayInputStreamTest {
    public static void main(String[] args) {
        byte[] ArrayLetters = {
                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
        };
        ByteArrayInputStream inputStream = new ByteArrayInputStream(ArrayLetters);

        //读取5个字节
        System.out.print("前5个字节为: ");
        for(int i=0;i<5;i++) {
            //是否可读
            if (inputStream.available() >= 0) {
                int buf = inputStream.read();
                System.out.printf("0x%s ", Integer.toHexString(buf));//toHexString十六进制转换
            }
        }
        System.out.println();

        //是否支持标记
        if (inputStream.markSupported()) {
            //此处已读取了5个字节,标记处为0x66
            inputStream.mark(0);

            //跳过2个字节,0x68->h
            inputStream.skip(2);

            //读取5个字节到buffer
            byte[] buffer = new byte[5];
            inputStream.read(buffer, 0, 5);
            System.out.println("buffer: " + new String(buffer));//hijkl

            //重置
            inputStream.reset();//0x66开始
            inputStream.read(buffer, 0, 5);
            System.out.println("重置后读取5个字符为: " + new String(buffer));
        }
    }
}

在这里插入图片描述
二、字节数组输出流ByteArrayOutputStream
1、继承关系、属性

public class ByteArrayOutputStream extends OutputStream
// 缓冲字节数组,数据写入这里
protected byte buf[];
// 字节数组中的有效字节个数
protected int count;
// 数组最大容量值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2、理解
将数据写入了一个大小可变的byte数组,用输出流,即可以操作这个byte 数组的一个对象,通过调用对象的方法操作这个数组。即向byte数组里输出数据,然后操作这个数组。
3、构造函数

//创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节
ByteArrayOutputStream()
//创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定
ByteArrayOutputStream(int size)

4、常用方法
(1)ensureCapacity()

    private void ensureCapacity(int minCapacity) {
        // 如果需要的最小容量大于数组的容量,那么调用grow函数来扩展
        // 否则,不需要改变这个数组的大小
        if (minCapacity - buf.length > 0)
            grow(minCapacity);
    }

    // 扩张容量
    private void grow(int minCapacity) {
        // 保存当前的数组大小
        int oldCapacity = buf.length;
        // 新的大小将旧大小扩张二倍
        int newCapacity = oldCapacity << 1;
        // 此时判断新容量和所需容量,如果新容量还不满足要求,使用所需容量作为新容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 然后比较新容量和最大容量,如果新容量大于最大容量,调用hugeCapacity方法
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 将旧数组中所有字节拷贝到新容量大小的新数组中,这个新数组作为新的缓冲字节数组
        buf = Arrays.copyOf(buf, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        // 如所需容量大于最大容量,那么就使用最大整数,否则就使用最大容量即可
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

(2)write()

        // 同步方法,写入一个字节
    public synchronized void write(int b) {
        // 调用ensureCapacity函数确保count位置有空间可以写入
        // count由于是已存在有效字节的个数,比如count为10,代表数组中0-9这10个位置有字节
        // 如果数组容量就是10,那么count下标即10的下标是越界的,所以要进行判断
        // 这里要么有空间直接能写入,要么扩张大小后写入字节
        ensureCapacity(count + 1);
        // 将字节写入count下标处
        buf[count] = (byte) b;
        // 有效字节数count加1
        count += 1;
    }
        //从b中下标off开始将len个字节写入输出流
    public synchronized void write(byte b[], int off, int len) {
        if ((off < 0) || (off > b.length) || (len < 0) ||
            ((off + len) - b.length > 0)) {
            // 偏移量off及个数len应该满足的条件,否则抛出范围异常
            throw new IndexOutOfBoundsException();
        }
        // 确保空间是否需要增长len的长度
        ensureCapacity(count + len);
        // 数组拷贝,b数组off下标开始拷贝到buf数组count下标处,共len个字节
        System.arraycopy(b, off, buf, count, len);
        // 元素个数count增加len个
        count += len;
    }

(3)writeTo()

 // 这里是将当前字节数组流的字节数组的所有字节,写入到目标输出流中
    public synchronized void writeTo(OutputStream out) throws IOException {
        out.write(buf, 0, count);
    }

(4)reset()

public synchronized void reset() {
    // 将count置为0,代表字节数组中的字节都要丢弃
    count = 0;
}

说明:
ByteArrayOutputStream实际上是将字节数据写入到“字节数组”中去。

(1) 通过ByteArrayOutputStream()创建的“字节数组输出流”对应的字节数组大小是32。
(2) 通过ByteArrayOutputStream(int size) 创建“字节数组输出流”,它对应的字节数组大小是size。
(3) write(int oneByte)将int类型的oneByte换成byte类型,然后写入到输出流中。
(4) write(byte[] buffer, int offset, int len) 是将字节数组buffer写入到输出流中,offset是从buffer中读取数据的偏移位置,len是读取的长度。
(5) writeTo(OutputStream out) 将该字节数组输出流的数据全部写入到输出流中。
示例:

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ByteArrayOutputStreamTest {
    public static void main(String[] args) {
        //String tmp = new String(ArrayLetters);
        //System.out.println("ArrayLetters="+tmp);
        byte[] ArrayLetters = {
                0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
                0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
        };
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // 依次写入a,b,c
        byteArrayOutputStream.write(0x61);
        byteArrayOutputStream.write(0x62);
        byteArrayOutputStream.write(0x63);
        System.out.println("byteArrayOutputStream:"+byteArrayOutputStream);

        // 将ArrayLetters数组中前5个字节defgh写入到byteArrayOutputStream中。
        byteArrayOutputStream.write(ArrayLetters, 0, 5);
        System.out.println("byteArrayOutputStream:"+byteArrayOutputStream);

        // 计算长度
        System.out.println("size:"+byteArrayOutputStream.size());

        // 转换成byte[]数组
        byte[] buf = byteArrayOutputStream.toByteArray();
        String str = new String(buf);
        System.out.println("str:"+str);

        // 将byteArrayOutputStream写入到另一个输出流中
        try {
            ByteArrayOutputStream byteArrayOutputStream2 = new ByteArrayOutputStream();
            byteArrayOutputStream.writeTo(byteArrayOutputStream2);
            System.out.println("byteArrayOutputStream2:"+byteArrayOutputStream2);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
小结:ByteArrayInputStream与ByteArrayOutputStream类用于以IO流的方式来完成对字节数组的内容的读写,来支持类似内存虚拟文件或者内存映像文件的功能。
三、字符数组输入流CharArrayReader
1、继承关系、属性、构造方法、常用方法

public class CharArrayReader extends Reader

在这里插入图片描述
在这里插入图片描述
说明:
CharArrayReader实际上是通过“字符数组”去保存数据。

(1) 通过 CharArrayReader(char[] buf) 或 CharArrayReader(char[] buf, int offset, int length) ,我们可以根据buf数组来创建CharArrayReader对象。
(2) read()的作用是从CharArrayReader中“读取下一个字符”。
(3) read(char[] buffer, int offset, int len)的作用是从CharArrayReader读取字符数据,并写入到字符数组buffer中。offset是将字符写入到buffer的起始位置,len是写入的字符的长度。
(4) markSupported()是判断CharArrayReader是否支持“标记功能”。它始终返回true。
(5) mark(int readlimit)的作用是记录标记位置。记录标记位置之后,某一时刻调用reset()则将“CharArrayReader下一个被读取的位置”重置到“mark(int readlimit)所标记的位置”;也就是说,reset()之后再读取CharArrayReader时,是从mark(int readlimit)所标记的位置开始读取。
示例:

import java.io.CharArrayReader;
import java.io.IOException;

public class CharArrayReaderTest {

    public static void main(String[] args) {
        int len=5;
        char[] chars = new char[] {'a','b','c','d','e','f','g','h','i','j','k','l','m','n'};
        try {
            CharArrayReader charArrayReader = new CharArrayReader(chars);
            // 从字符数组流中读取5个字符
            for (int i=0; i<len; i++) {
                //判断是否可以继续读取下一个字符
                if (charArrayReader.ready() == true) {
                    // 读取“字符流的下一个字符”
                    char c = (char)charArrayReader.read();
                    System.out.printf("%d : %c\n", i,c);
                }
            }

            // 标记字符流中下一个被读取的位置,即标记f(因为因为前面已经读取了5个字符,所以下一个被读取的位置是第6个字符)
            // 注:CharArrayReader类的mark(0)函数中的0没有实际意义;mark()与reset()配套,reset()会将字符流中下一个被读取的位置重置为mark()中所保存的位置
            charArrayReader.mark(0);

            //跳过5个字符后下一个被读取的是k
            charArrayReader.skip(5);
            //再读取5个数据,即klmno
            char[] buf = new char[len];
            charArrayReader.read(buf, 0, len);
            System.out.println("buf:"+String.valueOf(buf));

            // 重置“字符流”:即,将“字符流中下一个被读取的位置”重置到“mark()所标记的位置”,即f。
            charArrayReader.reset();
            // 从“重置后的字符流”中读取5个字符到buf中。即读取“fghij”
            charArrayReader.read(buf, 0, len);
            System.out.println("buf:"+String.valueOf(buf));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
四、字符数组输出流CharArrayWriter
1、继承关系、继承关系、构造方法、常用方法

public class CharArrayWriter extends Writer

在这里插入图片描述
在这里插入图片描述
说明:
CharArrayWriter实际上是将数据写入到“字符数组”中去。

(1) 通过CharArrayWriter()创建的CharArrayWriter对应的字符数组大小是32。
(2) 通过CharArrayWriter(int size) 创建的CharArrayWriter对应的字符数组大小是size。
(3) write(int oneChar)的作用将int类型的oneChar换成char类型,然后写入到CharArrayWriter中。
(4) write(char[] buffer, int offset, int len) 是将字符数组buffer写入到输出流中,offset是从buffer中读取数据的起始偏移位置,len是读取的长度。
(5) write(String str, int offset, int count) 是将字符串str写入到输出流中,offset是从str中读取数据的起始位置,count是读取的长度。
(6) append(char c)的作用将char类型的c写入到CharArrayWriter中,然后返回CharArrayWriter对象。
注意:append(char c)与write(int c)都是将单个字符写入到CharArrayWriter中。它们的区别是,append(char c)会返回CharArrayWriter对象,但是write(int c)返回void。
(7) append(CharSequence csq, int start, int end)的作用将csq从start开始(包括)到end结束(不包括)的数据,写入到CharArrayWriter中。
注意:该函数返回CharArrayWriter对象!
(8) append(CharSequence csq)的作用将csq写入到CharArrayWriter中。
注意:该函数返回CharArrayWriter对象!
(9) writeTo(OutputStream out) 将该“字符数组输出流”的数据全部写入到“输出流out”中。
示例:

import java.io.CharArrayWriter;
import java.io.IOException;

public class CharArrayWriterTest {
    public static void main(String[] args) {
        CharArrayWriter charArrayWriter = new CharArrayWriter(100);//构造函数,初始大小自定义参数,默认32

        //写入单个字符
        charArrayWriter.write(97);
        charArrayWriter.write('A');
        System.out.println(charArrayWriter.toString());

        //批量写入
        char[] c = {'a','b'};
        charArrayWriter.write(c,0,1);//读到a
        System.out.println(charArrayWriter.toString());
        String s = "cdef";
        charArrayWriter.write(s,0,3);//读到e
        System.out.println(charArrayWriter.toString());
        try {
            charArrayWriter.write("ghijk");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(charArrayWriter.toString());
        System.out.println(charArrayWriter.size());

        char[] c1 = charArrayWriter.toCharArray();//复制输入的char数组
        System.out.println(c1);

        charArrayWriter.append("append");//append
        charArrayWriter.append("helloworld",0,3);//包括start,不包括end
        System.out.println(charArrayWriter.toString());

        //将缓冲区的内容写入另一个字符流
        CharArrayWriter charArrayWriter1 = new CharArrayWriter();
        try {
            charArrayWriter.writeTo(charArrayWriter1);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(charArrayWriter1.toString());

        //刷新缓冲区
        charArrayWriter1.flush();
        //重置缓冲区
        charArrayWriter.reset();
        //关闭
        charArrayWriter.close();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值