深入理解JAVA IO系列二:字节流详解

字节流简介

public abstract class InputStreamextends Objectimplements Closeable
此抽象类是表示字节输入流的所有类的超类。
需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法。

InputSteam的部分子类
《深入理解JAVA IO系列二:字节流详解》
其中颜色较深的是节点流,剩下的是过滤流,其中FilterInputStream在JDK中的定义为:包含其他一些输入流,它将这些流用作其基本数据源,可以直接传输数据或提供一些额外的功能,这个类本身并不经常被我们使用,常用的是它的子类。

读写数据的逻辑步骤为:

1、打开流
2、有信息需要操作
3、读写信息
4、关闭流

FileInputStream和FileOutputStream

FileInputStream 从文件系统中的某个文件中获取输入字节。哪些文件可用取决于主机环境。
FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。

FileInputStream单独读取文件等时容易发生编码错误,一般用来读取图片,视频
如下代码,虽然输出没有问题,但是如果将缓存数组改为5个字节,就会出现控制台输出乱码,但对于文件的复制是没哟影响的

图片复制与文件复制

/**
 * 
 */
package io.input;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.junit.Test;

/**
 * @author Administrator
 *
 */
public class FileInputStreamTest {
    static void p(Object s) {
    System.out.println(s);
    }

    @Test
    /**
     * FileInputStream复制图片
     */
    public void test1() {
    InputStream is;
    OutputStream os;
    try {
        is = new FileInputStream("C:\\Users\\Administrator\\Desktop\\md\\1.png");
        os = new FileOutputStream("E:\\workspace\\fileTest\\copy.jpg");
        int length = 0;
        byte[] buffer = new byte[512];
        while (-1 != (length=is.read(buffer))) {
        os.write(buffer,0,length);
        }
        is.close();
        os.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    }

    @Test
    /**
     * FileInputStream复制文件
     */
    public void test2() {
    InputStream is;
    OutputStream os;
    try {
        is = new FileInputStream("E:\\workspace\\fileTest\\a.txt");
        os = new FileOutputStream("E:\\workspace\\fileTest\\test2\\b.txt");
        int length = 0;
        byte[] buffer = new byte[512];
        while (-1 != (length=is.read(buffer))) {
        String str = new String(buffer, "gbk");
                p(str);
        os.write(buffer,0,length);
        }
        is.close();
        os.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    }
}
0.
0

a.txt保存为gbk编码,运行环境为utf-8编码,故需要进行转码
输出

这是流测试文件abcde
大河向东流
天上的星星参北斗

如果文件本来就存在会覆盖原文件内容

将输出流如下定义文件如果存在即可追加文件内容
os = new FileOutputStream(“E:\workspace\fileTest\test2\b.txt”,true);

1、每次读取的内容存放在buffer这个字节数组中,然后转换成String字符串打印在控制台中。
2、再将数组内容输出到指定文件中
3、因为一个文件内容有多少我们事先并不知道,所以在这里只能分批次读取,每次最多读取512个字节(即buffer数组定义的长度)。
4、length的作用是表示在最后一次读取的时候,读取的长度小于等于buffer数组的长度,while循环体执行结束后,下一次再来读取已经没有内容了,read方法在这个时候会返   回-1,然后跳出循环。
5、最后关闭流

以上代码虽然没有问题,但文件内容大于数组大小,很可能会出现读取的字符中文乱码,因为一种中文汉字不止一个字节,读取时可能会被数组截断,具体看下面的InputStreamReader

FilterInputStream和FilterOutputStream

FilterInputStream

FilterInputStream 的作用是用来“封装其它的输入流,并为它们提供额外的功能”。它的常用的子类有BufferedInputStream和DataInputStream。

(01) BufferedInputStream的作用就是为“输入流提供缓冲功能,以及mark()和reset()功能”。

(02) DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。

FilterOutputStream

FilterOutputStream 的作用是用来“封装其它的输出流,并为它们提供额外的功能”。它主要包括BufferedOutputStream, DataOutputStream和PrintStream。

(01) BufferedOutputStream的作用就是为“输出流提供缓冲功能”。

(02) DataOutputStream 是用来装饰其它输出流,将DataOutputStream和DataInputStream输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。

(03) PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

BufferedInputStream和BufferedOutputStream

BufferedInputStream(InputStream in, int size)
BufferedInputStream就是为了创建个指定缓冲区大小的 BufferedInputStream 并保存其参数

介绍FileInputStream和FileOutputStream的例子中,使用了一个byte数组来作为数据读入的缓冲区,以文件存取为例,硬盘存取的速度远低于内存中的数据存取速度。为了减少对硬盘的存取,通常从文件中一次读入一定长度的数据,而写入时也是一次写入一定长度的数据,这可以增加文件存取的效率。

java.io.BufferedInputStream与java.io.BufferedOutputStream可以为InputStream、OutputStream类的对象增加缓冲区功能。

BufferedInputStream的数据成员buf是一个位数组,默认为2048字节。当读取数据来源时,例如文件,BufferedInputStream会尽量将buf填满。当使用read()方法时,实际上是先读取buf中的数据,而不是直接对数据来源作读取。当buf中的数据不足时,BufferedInputStream才会再实现给定的InputStream对象的read()方法,从指定的装置中提取数据。

BufferedOutputStream的数据成员buf是一个位数组,默认为512字节。当使用write()方法写入数据时,实际上会先将数据写至buf中,当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写至目的地,而不是每次都对目的地作写入的动作。

  @Test
    /**
     * BufferedInputStream和BufferedOutputStream
     */
    public void test3() {
     OutputStream os;
     InputStream is;
    try {
        is =  new FileInputStream("E:\\workspace\\fileTest\\a.txt");
        os = new FileOutputStream("E:\\workspace\\fileTest\\test2\\b.txt");
        InputStream bis = new BufferedInputStream(is);
        OutputStream bos = new BufferedOutputStream(os);

              byte[] buffer = new byte[512];
              while(bis.read(buffer)!=-1){
                  bos.write(buffer);
              }

              bos.close();
              os.close();
              bis.close();
              is.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    }

看到以上代码,不少人可能有疑问,既然缓存流已经有缓存数组了,怎么还需要定义字节数组?

这是不同的,以前是每次你读取时,系统执行读取操作把数据读出来,你指定读一个字节那就读一个字节,指定一个数组那就读一个数组的,程序和数据的读取效率有非常大的关系

当你创建bufferedinputstream,后他会立即去读数据然后放到jvm管理的内存里,
每次你读取时,不会再去系统中读取,而是去内存中读取数据,之后再判断内存中剩的还多不,不多就再由它去指挥系统读取操作,不论你是读一个字节还是一个数组,系统都不一定会去操作硬盘。程序和数据读取的效率关联较小了

两个数组的存在都是为了提高读写效率

流的close问题

@Test
    /**
     * BufferedInputStream和BufferedOutputStream
     * 没有调用close方法
     * 结果为,复制文件的内容为空,因为在缓存数组中没有close调用时flush掉
     */
    public void test4() {
     OutputStream os;
     InputStream is;
    try {
        is = new FileInputStream("E:\\workspace\\fileTest\\a.txt");
        os = new FileOutputStream("E:\\workspace\\fileTest\\test2\\b.txt");
        InputStream bis = new BufferedInputStream(is);
        OutputStream bos = new BufferedOutputStream(os);

              byte[] buffer = new byte[512];
              while(bis.read(buffer)!=-1){
                  bos.write(buffer);
              }


    } catch (Exception e) {
        e.printStackTrace();
    }

    }

当不关闭流时,文件内容还在缓存中,没有输出出去,当调用close方法时会调用flush方法来刷新
而flush在JDK中的定义为:刷新此缓冲的输出流。这迫使所有缓冲的输出字节被写出到底层输出流中

DataInputStream和DataOutputStream

数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。

 @Test
    /**
     *  DataInputStream和 DataOutputStream
     * 直接读写数据类型
     */
    public void test5() {
     OutputStream os;
     InputStream is;
    try {
        is = new FileInputStream("E:\\workspace\\fileTest\\test2\\b.txt");
        os = new FileOutputStream("E:\\workspace\\fileTest\\test2\\b.txt");
        DataInputStream dis = new DataInputStream(is);
        DataOutputStream dos = new DataOutputStream(os);
        byte by = 1;
        String str = "String中";
        int i=100;
        dos.writeByte(by);
        dos.writeUTF(str);
        dos.writeInt(i);

        //读取的顺序不能乱,必须和写入的数据类型一致
        p(dis.readByte());
        p(dis.readUTF());
        p(dis.readInt());


    } catch (Exception e) {
        e.printStackTrace();
    }

    }

writeUTF(Stirng str)将字符串以utf-8的编码写入
DataInputStream和 DataOutputStream的写入和读取的数据类型不能乱,不然会出错

ByteArrayInputStream和ByteArrayOutputStream

流的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组。java.io.ByteArrayInputStream、java.io.ByteArrayOutputStream就是将字节数组当作流输入来源、输出目的地的类。
java.io.ByteArrayInputStream将一个字节数组当作流输入的来源,而java.io.ByteArrayOutputStream则可以将一个字节数组当作流输出目的地。

ByteArrayInputStream与ByteArrayOutputStream类用于以IO流的方式来完成对字节数组的内容的读写,来支持类似内存虚拟文件或者内存映像文件的功能。
例如:


public static void transform(InputStream in, OutputStream out) { int ch = 0; try { while ((ch = in.read()) != -1) { out.write(ch); } // close while } catch (Exception e) { e.printStackTrace(); } } @Test /** * ByteArrayInputStream和ByteArrayOutputStream 直接读写数据类型 */ public void test6() { String str = "abcdef"; ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream out = new ByteArrayOutputStream(); transform(in, out); byte[] result = out.toByteArray(); System.out.println(out); System.out.println(new String(result)); transform(System.in, System.out); // 从键盘读,输出到显示器 }

以上代码完成了将字符串以字节方式输出,且可以在控制台输入并复制输出

有时候我们需要对同一个InputStream对象使用多次。比如,既要把数据显示到前台(第一次读取),又想把数据写进文件缓存到本地(第二次读取)。
但第一次读取InputStream对象后,第二次再读取时可能已经到Stream的结尾了(EOFException)或者Stream已经close掉了。
而InputStream对象本身不能复制,因为它没有实现Cloneable接口。此时,可以先把InputStream转化成ByteArrayOutputStream,后面要使用InputStream对象时,再从ByteArrayOutputStream转化回来就好了。代码实现如下:

 @Test
    /**
     * ByteArrayInputStream和ByteArrayOutputStream 
     * 真正的巧妙用法
     */
    public void test7() {
    try{
    InputStream input =  new FileInputStream("E:\\workspace\\fileTest\\test2\\b.txt");

      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] buffer = new byte[1024];
      int len;
      while ((len = input.read(buffer)) != -1 ) {
         baos.write(buffer, 0, len);
     }
     baos.flush();               

     //TODO:显示到前台
     InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
     OutputStream out = System.out;
     byte[] buffer2 = new byte[1024];
     int count=0;

     while((count=stream1.read(buffer2))!=-1){
         out.write(buffer2,0,count);
     }

     //TODO:本地缓存
     InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
     OutputStream out2 = new FileOutputStream("E:\\workspace\\fileTest\\a.txt");
     byte[] buffer3 = new byte[1024];
     int count2=0;

     while((count2=stream2.read(buffer2))!=-1){
         out2.write(buffer2,0,count2);
     }
     out2.close();
     out.close();

    }catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
    }
    }

同一个流,进行了两次的输出,不必进行两次读取,通过ByteArrayInputStream和ByteArrayOutputStream的转换

//TODO:显示到前台
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
//TODO:本地缓存
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());

BufferedOutputStream 和 ByteArrayOutputStream的区别

1、BufferedOutputStream会首先创建一个默认的容器量, capacity = 8192 = 8KB, 每次在写的时候都会去比对capacity是否还够用, 如果不够用的时候, 就flushBuffer(), 把buf中的数据写入对应的outputStream中, 然后将buf清空, 一直这样等到把内容写完. 在这过程中主要起到了一个数据缓冲的功能.

2、普通的OutputStream, 例如ByteArrayOutputStream也会首先创建一个默认的容器量, capacity = 32 = 32b, 每次在写的时候都会去比对capacity是否还够用, 如果不够用的时候, 就重新创建buf的容量, 一直等到内容写完, 这些数据都会一直处于内存中.

总结 : 当你资源不足够用时,选择BufferedOutputStream是最佳的选择, 当你选择快速完成一个作业时,可以选择ByteArrayOutputStream之类的输出流

write(byte b[], int off, int len)与write(byte b[])的区别

write(byte b[], int off, int len)表示:

b 这一次写的数据
off 这次从b的第off开始写
len 这次写的长度。

在进行写文件的时候有时候返现,通过write(byte b[])方式写文件比原来的文件大一些。因为
write(byte b[])的实现是

     public void write(byte b[]) throws IOException {
            write(b, 0, b.length);
        }

其本质会获取字节数组的长度,最后一次如果内容不足填充数组会造成写入数据还是数组的长度
一般我们会这样写入数据

        byte input[] = new byte[1024];
        while ((brin.read(input))!= -1) {
            bout.write(input);
        }

但应该改为有多少数据写多少数据

 byte input[] = new byte[1024*5];
        int count;
        while ((count=brin.read(input))!= -1) {
            bout.write(input,0,count);
        }

管道(PipedOutputStream和PipedInputStream)

/**
 * 
 */
package io.input;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * @author Administrator
 *
 */
public class PipedStreamTest {
    public static void main(String[] args) {
    Sender sender = new Sender();
    Receiver receiver = new Receiver();

    PipedOutputStream outStream = sender.getOutStream();
    PipedInputStream inStream = receiver.getInStream();

    try {
        // inStream.connect(outStream); // 与下一句一样
        outStream.connect(inStream);
    } catch (Exception e) {
        e.printStackTrace();
    }
    receiver.start();
    sender.start();

    }
}

class Sender extends Thread {
    private PipedOutputStream outStream = new PipedOutputStream();

    public PipedOutputStream getOutStream() {
    return outStream;
    }

    public void run() {
      writeShortMessage();
    //writeLongMessage();
    }

    // 向“管道输出流”中写入一则较简短的消息:"this is a short message"
    private void writeShortMessage() {
    String strInfo = "this is a short message";
    try {
        outStream.write(strInfo.getBytes());
        outStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }

    // 向“管道输出流”中写入一则较长的消息
    private void writeLongMessage() {
    StringBuilder sb = new StringBuilder();
    // 通过for循环写入1020个字节
    for (int i = 0; i < 102; i++)
        sb.append("0123456789");
    // 再写入26个字节。
    sb.append("abcdefghijklmnopqrstuvwxyz");
    // str的总长度是1020+26=1046个字节
    String str = sb.toString();
    try {
        // 将1046个字节写入到“管道输出流”中
        outStream.write(str.getBytes());
        outStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }
}

class Receiver extends Thread {
    private PipedInputStream inStream = new PipedInputStream();

    public PipedInputStream getInStream() {
    return inStream;
    }

    public void run() {
     readMessageOnce() ;
     //readMessageContinued() ;
    }

    // 从“管道输入流”中读取1次数据
    public void readMessageOnce() {
    // 虽然buf的大小是2048个字节,但最多只会从“管道输入流”中读取1024个字节。
    // 因为,“管道输入流”的缓冲区大小默认只有1024个字节。
    byte[] buf = new byte[2048];
    try {
        int len = inStream.read(buf);
        System.out.println(new String(buf, 0, len));
        inStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }

    // 从“管道输入流”读取>1024个字节时,就停止读取
    public void readMessageContinued() {
    int total = 0;
    while (true) {
        byte[] buf = new byte[1024];
        try {
        int len = inStream.read(buf);
        total += len;
        System.out.println(new String(buf, 0, len));
        // 若读取的字节总数>1024,则退出循环。
        if (total > 1024)
            break;
        } catch (IOException e) {
        e.printStackTrace();
        }
    }

    try {
        inStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }

// 从“管道输入流”读取所有内容
    public void readMessageContinue() {
    int total = 0;

        byte[] buf = new byte[1024];
        try {
        int len ;
        while((len=inStream.read(buf))!=-1){
            System.out.println(new String(buf, 0, len));
        }

        } catch (IOException e) {
        e.printStackTrace();
        }


    try {
        inStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    }
}

输出

this is a short message

说明:

(01)
in.connect(out);
将“管道输入流”和“管道输出流”关联起来。查看PipedOutputStream.java和PipedInputStream.java中connect()的源码;我们知道 outStream.connect(inStream); 等价于 inStream.connect(outStream);

(02)
sender.start(); // 启动“Sender”线程
receiver.start(); // 启动“Receiver”线程

注意管道输入流的缓冲区默认大小是1024个字节。所以,最多只能写入1024个字节。

private static final int DEFAULT_PIPE_SIZE = 1024;
public PipedInputStream() {
    initPipe(DEFAULT_PIPE_SIZE);
}

详细了解请阅读java io系列04之 管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例

退回输入流PushbackInputStream

PushBackInputStream的实现逻辑比较清晰
内部有一个缓冲区,你要是回退了我就往里面写入数据
每次的数据读取都是先看看缓冲区里面有没有数据,有的话就先读取回退缓冲区的
否则,就再去使用实际的流去进行读取
如果从来不曾回退过,那么好像什么都一样,还是使用原来的InputStream 进行读取

PushBackInputStream 不支持标记点相关的操作

    void unread(int b)
    void unread(byte[] buffer)
    void unread(byte[] buffer,int offset,int numBytes)

第一种形式回推b的低字节,这会使得后续的read()调用会把这个字节再次读取出来。第二种形式回推buffer中的字节。第三种形式回推buffer中从offset开始的numBytes个字节。当回推缓存已满时,如果试图回推字节,就会抛出IOException异常。

第一种测试:

 @Test
    /**
     * 退回输入流PushbackInputStream
     */
    public void test8() {
    String str = "abcdef";


    try {
        ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes());

                PushbackInputStream pbin = new PushbackInputStream(in) ;
                int n;
                int i=0;
                while ((n = pbin.read()) != -1) {
                    i++;
                    System.out.println((char) n+":"+i);
                    if('b' == n) pbin.unread('U');    
                }
    } catch (Exception e) {
        // TODO: handle exception
    }
    }

输出

a:1
b:2
U:3
c:4
d:5
e:6
f:7

第二种测试:

 @Test
    /**
     * 退回输入流PushbackInputStream
     */
    public void test9() {
    String str = "abcdefg";

    try {
        /*
             * PushbackInputStream pbin = new PushbackInputStream(in,3)
             * 这个构造函数创建的对象一次可以回推一个缓存
             */
        ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes());
        PushbackInputStream pbin = new PushbackInputStream(in,3);
        int n;
        int i = 0;
        byte[] buffer = new byte[3];
        while ((n = pbin.read(buffer)) != -1) {
        i++;
        String read = new String(buffer);
        System.out.println(read + ":" + i);
        if (read.equals("abc")){
            pbin.unread(new byte[]{'M','N','O'});
        }
                buffer = new byte[3];
        }
    } catch (Exception e) {
        // TODO: handle exception
    }
    }

输出

abc:1
MNO:2
def:3
g  :4

注释循环中的 buffer = new byte[3];
输出为

abc:1
MNO:2
def:3
gef:4

造成这种情况原因是缓存区数组是从后往前填充,当不重新在循环中定义新的数组对象时,循环中使用的一直是同一个数组对象,最后一次读取的内容覆盖上一次读取的内容,但是,由于数据无法填充整个数组,所以后面的数据就没有被覆盖,故保留为上一次读取的内容

第三种种测试:

 @Test
    /**
     * 退回输入流PushbackInputStream
     */
    public void test10() {
    String s = "abcdefg";
    /*
     * PushbackInputStream pbin = new PushbackInputStream(in,4)
     * 这个构造函数创建的对象一次可以回推一个缓存
     */
    try {
        ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
        PushbackInputStream pbin = new PushbackInputStream(in, 4);
        int n;
        int i = 0;
        byte[] buffer = new byte[4];
        while ((n = pbin.read(buffer)) != -1) {
        i++;
        System.out.println(new String(buffer)+":"+i);
        // 取回推缓存中的一部分数据
        if (new String(buffer).equals("abcd")) {
            pbin.unread(buffer, 0, 3);
        }
        buffer = new byte[4];
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    }
}

abce:1
abce:2
fg  :3

PushbackInputStream pbin = new PushbackInputStream(in,4)如果将4改为2,就是将回推数组的大小改为2个字节,那么pbin.unread(buffer, 0, 3);时就会出错:
java.io.IOException: Push back buffer is full
回推数组已满,默认回推数组大小为1

public PushbackInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("size <= 0");
        }
        this.buf = new byte[size];
        this.pos = size;
    }

 public PushbackInputStream(InputStream in) {
        this(in, 1);
    }

注:PushbackInputStream对象会使得InputStream对象(用于创建PushbackInputStream对象)的mark()或reset()方法无效。对于准备使用mark()或reset()方法的任何流来说,都应当使用markSupported()方法进行检查

合并流SequenceInputStream

输入合并流,将多个流进行逻辑串联(合并变成一个流,操作起来很方便)

public SequenceInputStream(Enumeration<? extends InputStream> e)

    通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。将按顺序读取由该枚举生成的输入流,以提供从此 SequenceInputStream 读取的字节。在用尽枚举中的每个输入流之后,将通过调用该流的 close 方法将其关闭。

    参数:
        e - 输入流的一个枚举。
    另请参见:
        Enumeration

public SequenceInputStream(InputStream s1, InputStream s2)

    通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。

    参数:
        s1 - 要读取的第一个输入流。
        s2 - 要读取的第二个输入流。
 @Test
    /**
     * 合并流SequenceInputStream进而SequenceOutputStream
     */
    public void test11() {
    String str1 = "你好,";
    String str2 = "中国,";
    String str3 = "我爱你";

    ByteArrayInputStream bis1 = new ByteArrayInputStream(str1.getBytes());
    ByteArrayInputStream bis2 = new ByteArrayInputStream(str2.getBytes());
    ByteArrayInputStream bis3 = new ByteArrayInputStream(str3.getBytes());
    SequenceInputStream seq = new SequenceInputStream(bis1, bis2);
    SequenceInputStream seq2 = new SequenceInputStream(seq, bis3);
    try {
        int len = 0;
        byte buf[] = new byte[10];
        while ((len = seq2.read(buf)) != -1) {
        System.out.println(new String(buf));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    }
@Test
    /**
     * 合并流SequenceInputStream进而SequenceOutputStream
     */
    public void test12() {
    String str1 = "你好,";
    String str2 = "中国,";
    String str3 = "我爱你";

    ByteArrayInputStream bis1 = new ByteArrayInputStream(str1.getBytes());
    ByteArrayInputStream bis2 = new ByteArrayInputStream(str2.getBytes());
    ByteArrayInputStream bis3 = new ByteArrayInputStream(str3.getBytes());
    ArrayList<InputStream> c = new ArrayList<InputStream>();
    c.add(bis1);
    c.add(bis2);
    c.add(bis3);
    Enumeration<InputStream> em = Collections.enumeration(c);

    SequenceInputStream seq = new SequenceInputStream(em);
    try {
        int len = 0;
        byte buf[] = new byte[10];
        while ((len = seq.read(buf)) != -1) {
        System.out.println(new String(buf));
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    }
    @Test
    /**
     * 合并流SequenceInputStream进而SequenceOutputStream
     */
    public void test13() {
    String str1 = "你好,";
    String str2 = "中国,";
    String str3 = "我爱你";

    ByteArrayInputStream bis1 = new ByteArrayInputStream(str1.getBytes());
    ByteArrayInputStream bis2 = new ByteArrayInputStream(str2.getBytes());
    ByteArrayInputStream bis3 = new ByteArrayInputStream(str3.getBytes());

    Vector<InputStream> v = new Vector<InputStream>();

    v.add(bis1);
    v.add(bis2);
    v.add(bis3);

    Enumeration<InputStream> en = v.elements();

    SequenceInputStream seq = new SequenceInputStream(en);
    try {
        int len = 0;
        byte buf[] = new byte[10];
        while ((len = seq.read(buf)) != -1) {
        System.out.println(new String(buf));
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    }

闲言碎语

本文章代码仅供流功能测试,存在很多问题,比如流的关闭,异常的捕获,参考代码时请注意

参考

深入理解JAVA I/O系列二:字节流详解

java io系列14之 DataInputStream(数据输入流)的认知、源码和示例

java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)

java io系列04之 管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例

JAVA基础—PushbackInputStream类简单介绍

点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注