1167839 流

流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。IO(Input/Output,输入/输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。

输入输出流

  1. 输入流有:InputStream(字节输入流)、Reader(字符输入流)。
  2. 输出流有:OutputStream(字节输出流)、Writer(字符输出流)。

他们的基本结构如下:

在这里插入图片描述

输入输出流根据分类包括:

在这里插入图片描述

InputStream

InputStream类是字节输入流的抽象类,是所有字节输入流的父类。

方法含义
public abstract int read()从输入流中读取数据的下一个字节,返回0~255范围的int字节值。
可以将返回值理解为无符号的byte值
public int read(byte b[])从输入流中读入一定长度的字节,返回字节数
public int read(byte b[], int off, int len)从指定位置读取指定长度的字节,返回字节数。
off不是指跳过字节流开始读,而是指读到b[]数组中开始的位置
如果len超过最大可读字节数,则抛出异常
public long skip(long n)跳过输入流上的n个字节,返回实际跳过的字节数
public int available()得到输入流中有多少字节。有时并不是准确,比如网络操作
public void close()关闭输入流
public synchronized void mark(int readlimit)在输入流的当前位置放置一个标记。readlimit表示允许读取的字节数
超过这个字节数可能会导致reset失败
public synchronized void reset()将输入指针返回标记处
public boolean markSupported()如果当前流支持mark()/reset()操作就返回true

注:并不是所有的InputStream子类都支持这些方法。

ByteArrayInputStream

字节数组输入流。

构造方法:

public ByteArrayInputStream(byte buf[]) {}
public ByteArrayInputStream(byte buf[], int offset, int length) {}
  1. 第一个构造方法根据字节数组构造一个字节输入流,offset=0,length=buf.length
  2. 第二个构造方法指定标记位置为offset,以及可读取的长度为length(实际长度是offset+length与字节数组长度的最小值,Math.min(offset + length, buf.length))

继承方法:

  1. close()方法没有任何效果
  2. mark(int readlimit)方法,标记会生效,但是参数readlimit没有任何效果

示例:

/**
 * @author zhengzewang on 2019/4/7.
 */
public class ByteArrayInputStreamTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = null;
        byteArrayInputStream = new ByteArrayInputStream(new byte[]{-128, -1, 0, 127});
        // read方法
        System.out.println(byteArrayInputStream.read()); // 可以使用强制类型转换为byte
        System.out.println(byteArrayInputStream.read());
        System.out.println(byteArrayInputStream.read());
        System.out.println(byteArrayInputStream.read());
        System.out.println();
        //
        read(byteArrayInputStream);
        //
        byteArrayInputStream = new ByteArrayInputStream(new byte[]{-128, -1, 0, 127});
        byte[] bytes = new byte[byteArrayInputStream.available()];
        System.out.println(byteArrayInputStream.read(bytes, 1, 2));
        System.out.println(Arrays.toString(bytes));
        System.out.println();
        // skip
        byteArrayInputStream = new ByteArrayInputStream(new byte[]{-128, -1, 0, 127});
        byteArrayInputStream.skip(1);
        read(byteArrayInputStream);
        // mark reset
        byteArrayInputStream = new ByteArrayInputStream(new byte[]{-128, -1, 0, 127});
        System.out.println(byteArrayInputStream.markSupported());
        byteArrayInputStream.mark(0); // 参数不会生效,随便填
        byteArrayInputStream.skip(1);
        byteArrayInputStream.reset();
        read(byteArrayInputStream);
        //
        byteArrayInputStream = new ByteArrayInputStream(new byte[]{-128, -1, 0, 127});
        byteArrayInputStream.close();
        read(byteArrayInputStream);
    }

    private static void read(ByteArrayInputStream byteArrayInputStream) throws IOException {
        byte[] bytes = new byte[byteArrayInputStream.available()];
        System.out.println(byteArrayInputStream.read(bytes));
        System.out.println(Arrays.toString(bytes));
        System.out.println();
    }

}

运行输出:

128
255
0
127

-1
[]

2
[0, -128, -1, 0]

3
[-1, 0, 127]

true
4
[-128, -1, 0, 127]

4
[-128, -1, 0, 127]

SequenceInputStream

SequenceInputStream是将多个流合并,其内部实现用的是java.util.Enumeration

构造方法:

public SequenceInputStream(Enumeration<? extends InputStream> e) {}
public SequenceInputStream(InputStream s1, InputStream s2)
  1. 无论是何种构造方法,最终都会将其存在SequenceInputStream中定义的一个Enumeration属性中

继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false
  2. read()方法将按顺序读取每个流的字节
  3. available()只返回当前流(Enumeration取出的当前元素)的字节数
  4. read(byte b[])与read(byte b[], int off, int len)默认读取当前流的数据。如果当前流没有数据,则读取下个流的数据
  5. available()方法与read(byte b[])结合只能读取到当前流数据(循环调用)。因为当前流数据读取完毕后,available()方法返回0
  6. 流只能读一次。
  7. 支持skip方法。
  8. 支持close方法。

综上:实际使用中,建议使用read()方法while循环的形式读取。

/**
 * @author zhengzewang on 2019/4/7.
 * <p>
 * 合并输入流
 */
public class SequenceInputStreamTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream1 = new ByteArrayInputStream(new byte[]{5, 6, 7});
        ByteArrayInputStream byteArrayInputStream2 = new ByteArrayInputStream(new byte[]{9, 10, 11});
        SequenceInputStream sequenceInputStream = new SequenceInputStream(byteArrayInputStream1, byteArrayInputStream2);
        read2(sequenceInputStream);
        byteArrayInputStream1.reset();
        byteArrayInputStream2.reset();
        sequenceInputStream = new SequenceInputStream(byteArrayInputStream1, byteArrayInputStream2);
        System.out.println(sequenceInputStream.markSupported());
        read(sequenceInputStream);
        read(sequenceInputStream);
        byteArrayInputStream1.reset();
        byteArrayInputStream2.reset();
        sequenceInputStream = new SequenceInputStream(byteArrayInputStream1, byteArrayInputStream2);
        sequenceInputStream.close();
        read2(sequenceInputStream); // 关闭后读取不到任何数据
    }

    private static void read(SequenceInputStream sequenceInputStream) throws IOException {
        byte[] bytes = new byte[sequenceInputStream.available()]; // 第二次读取时available返回0
        System.out.println(sequenceInputStream.read(bytes));
        System.out.println(Arrays.toString(bytes));
        System.out.println();
    }

    private static void read2(SequenceInputStream sequenceInputStream) throws IOException {
        int t = sequenceInputStream.read();
        while (t != -1) {
            System.out.println((byte) t);
            t = sequenceInputStream.read();
        }
        System.out.println();
    }

}

运行输出:

5
6
7
9
10
11

false
3
[5, 6, 7]

0
[]


FileInputStream

文件字节输入流

构造方法:

public FileInputStream(String name) {} // 参数为文件路径
public FileInputStream(File file) {}
public FileInputStream(FileDescriptor fdObj) {} // FileDescriptor是文件描述符

继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false
  2. 流只能读取一次
  3. 支持skip和close方法

用法同其他InputStream类似,示例省略

新增方法:

  • public final FileDescriptor getFD() throws IOException {} // 获取文件描述符,可用于创建文件输出流
  • public FileChannel getChannel() {} // 获取文件通道。通道能够用于读或写
PipedInputStream&PipedOutputStream

管道流。PipedInputStream与PipedOutputStream结合使用

PipedInputStream构造方法:

public PipedInputStream(PipedOutputStream src) {}
public PipedInputStream(PipedOutputStream src, int pipeSize) {}
public PipedInputStream() {}
public PipedInputStream(int pipeSize) {}

PipedOutputStream构造方法:

public PipedOutputStream(PipedInputStream snk) {}
public PipedOutputStream() {}

PipedInputStream继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false
  2. 流只能读取一次
  3. 支持skip和close方法

PipedOutputStream继承方法:

  1. 支持flush和close方法

如构造方法所示,PipedInputStream与PipedOutputStream可通过构造方法连接。如果不通过构造方法,也可以通过下列方法进行连接。

public void connect(PipedOutputStream src) {} // PipedInputStream
public synchronized void connect(PipedInputStream snk) {} // PipedOutputStream

实际上,构造方法也是通过调用connect方法进行连接。一个PipedInputStream或PipedOutputStream对象只能连接一次,多次连接会报Already connected异常。

PipedInputStream与PipedOutputStream的主要方法都是synchronized的,即线程安全的。它们的交互流程如下:

  1. PipedInputStream读取数据,判断是否存在连接,如果没有连接抛出异常。
  2. 存在连接,判断连接的PipedOutputStream是否有输出线程,如果没有,等待。
  3. 如果存在输出线程,判断线程是否已死,如果已死且数据已全部输入,则抛出异常,提示输出线程已死。
  4. 如果输出线程已死,但仍有待输入数据,则可继续输入。
  5. 如果输出线程未死,但无数据,等待。

由上可看出,以下情况容易造成死锁:

  1. 同一个线程内输出输入。
  2. 输出线程一直占据资源,且未有输出数据。

以下情况会造成死锁:

public static void lock1() throws IOException {
    PipedInputStream pipedInputStream = new PipedInputStream();
    PipedOutputStream pipedOutputStream = new PipedOutputStream();
    pipedInputStream.connect(pipedOutputStream);
    pipedInputStream.read(); // 会一直等待有数据输出到pipedOutputStream
}

public static void lock2() throws IOException {
    PipedInputStream pipedInputStream = new PipedInputStream();
    PipedOutputStream pipedOutputStream = new PipedOutputStream();
    pipedInputStream.connect(pipedOutputStream);
    new Thread(() -> {
        try {
            pipedOutputStream.write(2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    System.out.println(pipedInputStream.read());
    System.out.println("开始第二次读入数据");
    System.out.println(pipedInputStream.read());
    System.out.println("程序走不到这里");
}

推荐的使用方式:

/**
 * @author zhengzewang on 2019/4/7.
 */
public class PipedInputStreamTest {

    public static void main(String[] args) throws IOException {
        test();
    }

    private static void test() throws IOException {
        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream();
        pipedInputStream.connect(pipedOutputStream);
        new Thread(() -> {
            while (true) {
                try {
                    System.out.println("读取到数据:" + (byte) pipedInputStream.read());
                } catch (IOException e) {
                    System.out.println("读取数据结束"); // 当输出线程关闭,抛出异常
                    break;
                }
            }
        }).start();
        new Thread(() -> {
            int i = '0';
            while (i <= '9') {
                try {
                    pipedOutputStream.write(i++);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}
StringBufferInputStream(已废弃)

这是一个已废弃的类,主要是对字符串进行字节流读取

构造方法:

public StringBufferInputStream(String s) {} // 看到构造方法大概就明白什么意思了

继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false
  2. 支付skip方法
  3. 不支持close方法
/**
 * @author zhengzewang on 2019/4/9.
 */
public class StringBufferInputStreamTest {

    public static void main(String[] args) throws IOException {
        StringBufferInputStream stringBufferInputStream = new StringBufferInputStream("abc");
        read(stringBufferInputStream); // 依次输出abc
    }

    public static void read(InputStream inputStream) throws IOException {
        int i = inputStream.read();
        while (i != -1) {
            System.out.println((char)i);
            i = inputStream.read();
        }
    }

}
FilterInputStream

FilterInputStream是对InputStream做了一层包装,其内部包含了一个InputStream。FilterInputStream提供的构造方法是protected修饰的:

protected FilterInputStream(InputStream in) {
    this.in = in;
}

其常见的子类有:

  1. BufferedInputStream
  2. LineNumberInputStream(已废弃)
  3. PushBackInputStream
  4. DataInputStream
BufferedInputStream

字节缓冲流

构造方法

public BufferedInputStream(InputStream in) {}
public BufferedInputStream(InputStream in, int size) {}

第一个构造方法是传入一个InputStream,第二个构造方法则是指定了大小。(注意,这里指定的大小是内存缓冲的大小,第一个构造方法默认大小是8192)

继承方法:

  1. 支持所有的父类方法
  2. 对于方法mark(int readlimit)的参数readlimit也是支持的。也就是readlimit表示此后允许读取的字节数,超过这个字节数可能会导致reset失败。

这里我们着重讲解一下readlimit。

BufferedInputStream,顾名思义,是一个包装了InputStream的缓冲流。它的size可能比被包装流小很多,这也就是缓冲的意义。BufferedInputStream可以不用将被包装流中数据全部取出,而是动态的将其加载到缓冲中。简单地说:

  1. BufferedInputStream从被包装流中读取size字节数据
  2. 当这些数据被读完时,BufferedInputStream刷新数据,重新从被包装流中读取size字节数据。

看到这里,大概就有个疑问了。假设size=10(被包装流的数据远大于10),在pos=1的时候调用了mark方法,接下来一直读数据,直到将缓冲流中的数据刷新。此时再调用reset方法,就该是得不到期望的结果了。因为此前mark的地方数据已经被刷新了。

实际上:缓冲数据被刷新后,再调用reset方法是会抛出异常的。

那么readlimit有什么作用呢?

它的作用就是支持扩容,当readlimit被指定为20时,即表示读取了20个字节后,mark才可能失效。也就是读取了20个字节之内是不会失效了,是仍然支持reset方法。

而前面说了,读取了10字节就会失效了,那又是怎么回事?

原来,当指定了readlimit,那么read方法就会自动扩容,将size根据readlimit值进行扩大,从而支持mark的readlimit参数。直到读取超过了readlimit字节,才会将标记废弃掉。

看到这里,想必也明白了前面说的可能会导致reset失败的可能是什么含义了。明显的,如果将readlimit设置为5,即使读取超过了readlimit,但是因为没有达到刷新的限制,所以reset还是有效的。

上面部分的讲解可以参考BufferedInputStream源码中fill()方法。

看一个例子:

/**
 * @author zhengzewang on 2019/4/9.
 */
public class FilterInputStreamTest {

    public static void main(String[] args) throws IOException {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(new byte[]{'a', 'b', 'c', 'd', 'e', 'f'}));
        System.out.println((char) bufferedInputStream.read());
        bufferedInputStream.mark(3);
        int i = 0;
        while (i++ < 4) { // 读取已超过readlimit
            System.out.println(bufferedInputStream.read());
        }
        bufferedInputStream.reset();
    }
}

因为缓冲流默认大小8192,所以即使读取超出了readlimit,也不会导致reset失效。

而如果将缓冲流的大小改为2,则调用reset方法时就会抛出异常了。

BufferedInputStream bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(new byte[]{'a', 'b', 'c', 'd', 'e', 'f'}),2);
LineNumberInputStream(已废弃)

这是一个已废弃的类,主要是增加了读取行号的功能。

构造方法

public LineNumberInputStream(InputStream in) {}

这个类在包装了字节流的基础上提供了获取行号的功能。而行号则是由换行符决定的。

继承方法:

  • 基本上取决于被包装类。

新增方法:

  1. public int getLineNumber():获取当前行号
  2. public void setLineNumber(int lineNumber):设置当前行号

换行用\r\r\n表示

PushBackInputStream

回退流支持将读取的字节重新放回流中。也可以放入其他任意字节数据。

构造方法:

public PushbackInputStream(InputStream in) {} // size = 1
public PushbackInputStream(InputStream in, int size) {} 

size是指可支持的回退的流的长度。

继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false。意味着流只能读取一次。
  2. 支持skip方法和close方法

新增方法:

  1. public void unread(int b) throws IOException {} //
  2. public void unread(byte[] b, int off, int len) throws IOException {} //
  3. public void unread(byte[] b) throws IOException {} //

回退流有什么作用呢?可能我们有这种场景,读取流的数据,当读到某个字节时,停止读取,但该字节依然需要被保留在流中。

public class PushBackInputStreamTest {

    public static void main(String[] args) throws IOException {
        PushbackInputStream pushbackInputStream = new PushbackInputStream(new ByteArrayInputStream(new byte[]{'a', 'b', 'c', 'h', 'e', 'l', 'l', 'o'}));
        //
        int read = pushbackInputStream.read();
        while (read != 'h') {
            read = pushbackInputStream.read();
        }
        pushbackInputStream.unread(read);
        read(pushbackInputStream);
    }

    private static void read(InputStream inputStream) throws IOException {
        int read = inputStream.read();
        while (read != -1) {
            System.out.print((char) read);
            read = inputStream.read();
        }
    }

}

运行输出:

hello
DataInputStream

DataInputStream对字节流进行了一层包装,以便其能够读取基本Java数据类型。

构造方法:

public DataInputStream(InputStream in) {} //

继承方法:

  1. 所有继承方法的实现均取决于被包装流

新增方法:

  1. public final void readFully(byte b[]) throws IOException {} // 读满缓冲区b,读不满则等待。没有数据可读了且没满抛出异常
  2. public final void readFully(byte b[], int off, int len) throws IOException {} //
  3. public final int skipBytes(int n) throws IOException {} // 和skip的区别在于skip只尝试一次,有可能跳过的数并不是n,而skipBytes尽量保证为跳过的数为n,除非真的没有了
  4. public final boolean readBoolean() throws IOException {} // 读取一个字节,如果是0(无符号),则返回true,否则为false
  5. public final byte readByte() throws IOException {} // 读取一个字节,并返回byte类型(-128~127)
  6. public final int readUnsignedByte() throws IOException {} // 读取一个字节,返回无符号数值(0~255)
  7. public final short readShort() throws IOException {} // 读取两个字节,并返回short类型(-32768~32767)
  8. public final int readUnsignedShort() throws IOException {} // 读取两个字节,并返回无符号数值(0~65535)
  9. public final char readChar() throws IOException {} // 读取两个字节,并返回一个char类型
  10. public final int readInt() throws IOException {} // 读取4个字节,并返回int类型
  11. public final long readLong() throws IOException {} // 读取8个字节,并返回long类型
  12. public final float readFloat() throws IOException {} // 读取4个字节,并返回short类型
  13. public final double readDouble() throws IOException {} // 读取8个字节,并返回double类型
  14. public final String readLine() throws IOException {} // 已废弃。读取一行,直到换行符
  15. public final String readUTF() throws IOException {} // 以UTF-8形式读取数据
  16. public final static String readUTF(DataInput in) throws IOException {} //

Byte&UnsignedByte

byte在java中用一个字节存储,同样一个字节也可以存储一个字符。但是只能存储0~255范围内的字符。

以字符a为例,在ASCII编码中为:97,转换成二进制为:01100001。如果这个二进制表示的是一个byte,那它对应的数值为:97。

看着好像没什么问题。看一个例子

public static void main(String[] args) {
    System.out.println((byte)(char) 127);
    System.out.println((byte)(char) 128);
}

运行输出:

127
-128

这是因为:

  • java存储字符的方法仅存储字符对应编码的二进制数据。1个字节能表达的范围为0~255
  • java存储数值型的方式则是带有符号位的补码方式。1个字节表达的范围为-128~127

以128号为字符为例,其对应的二进制:10000000。而通过补码的解析规则,该二进制对应的byte数值为:-128。

很显然,得到这种结果,会对开发人员使用流产生一种很不好的体验。这也是为什么流的read方法返回的是int,而不是byte的原因。因为int为4个字节,对于上述的二进制,补齐4字节为:00000000 00000000 00000000 10000000。该二进制对应的数值为:128。

这不正是UnsignedByte?

同理,UnsignedShort亦是如此。

利用UnsignedShort证明java内存采用的编码方式为UTF-16

public static void main(String[] args) {
    int i = '郑'; // 不要使用0~255之间的字符测试
    String a = "郑";
    byte[] bytes = a.getBytes("UTF-16");
    DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    dataInputStream.readChar(); // 去掉前面的标识位
    System.out.println(dataInputStream.readUnsignedShort() == i); // true
}

如上例,如果使用UTF-16之外的任何编码都得不到结果true。

readUTF:使用UTF-8编码读取字符数。但需要注意的是,流的前两个字节(UnsignedShort)表示需要读取数的长度。

public static void main(String[] args) {
	byte[] bytes = new byte[]{0, 3, 'a', 'b', 'c'}; // 前两位标识待读取的长度 UnsignedShort
    DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    System.out.println(dataInputStream.available());
    System.out.println(dataInputStream.readUTF());
    //
    bytes = new byte[]{0, (byte) "郑泽旺".getBytes().length};
    byte[] bytes_utf = "郑泽旺".getBytes();
    int length1 = bytes.length;
    int length2 = bytes_utf.length;
    bytes = Arrays.copyOf(bytes, length1 + length2);
    System.arraycopy(bytes_utf, 0, bytes, length1, length2);
    dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    System.out.println(dataInputStream.available());
    System.out.println(dataInputStream.readUTF());
}

运行输出:

5
abc
11
郑泽旺

如果没有前面两个字节标识长度怎么办?很简单,可以利用上面讲到的回退流:

public static void main(String[] args) {
    DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream("郑泽旺".getBytes()));
    //
    PushbackInputStream pushbackInputStream = new PushbackInputStream(dataInputStream,2);
    pushbackInputStream.unread(dataInputStream.available());
    pushbackInputStream.unread(0);
    //
    DataInputStream dataInputStream_utf = new DataInputStream(pushbackInputStream);
    System.out.println(dataInputStream_utf.readUTF());
}
ObjectInputStream

ObjectInputStream用于对象的序列化操作。一般用ObjectOutputStream同时使用。

构造方法:

public ObjectInputStream(InputStream in) throws IOException {} // 

继承方法:

  1. 不支持mark与reset方法,即markSupported()返回false
  2. 支持skip和close方法

新增方法:

  1. public final Object readObject() throws IOException, ClassNotFoundException {} // 读取对象。
  2. public Object readUnshared() throws IOException, ClassNotFoundException {} // 读取“非共享”对象。?
  3. public void defaultReadObject() throws IOException, ClassNotFoundException {} // ?
  4. public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException {} // ?
  5. public void registerValidation(ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException {} // ?
  6. public boolean readBoolean() throws IOException {} // 读取bool型数据。(包括包装类)
  7. public byte readByte() throws IOException {} // 读取byte型数据。(包括包装类)
  8. public int readUnsignedByte() throws IOException {} // 读取无符号byte型数据。(包括包装类)
  9. public char readChar() throws IOException {} // 读取字符型数据。(包括包装类)
  10. public short readShort() throws IOException {} // 读取Short型数据。(包括包装类)
  11. public int readUnsignedShort() throws IOException {} // 读取无符号Short型数据。(包括包装类)
  12. public int readInt() throws IOException {} // 读取int型数据。(包括包装类)
  13. public long readLong() throws IOException {} // 读取long型数据。(包括包装类)
  14. public float readFloat() throws IOException {} // 读取float型数据。(包括包装类)
  15. public double readDouble() throws IOException {} // 读取double型数据。(包括包装类)
  16. public void readFully(byte[] buf) throws IOException {} // 读满缓冲区b,读不满则等待。没有数据可读了且没满抛出异常
  17. public void readFully(byte[] buf, int off, int len) throws IOException {} //
  18. public int skipBytes(int len) throws IOException {} // 和skip的区别在于skip只尝试一次,有可能跳过的数并不是n,而skipBytes尽量保证为跳过的数为n,除非真的没有了
  19. public String readLine() throws IOException {} // 已废弃
  20. public String readUTF() throws IOException {} // ?
/**
 * @author zhengzewang on 2019/4/24.
 */
public class User implements Serializable {

    private String a;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }
}
/**
 * @author zhengzewang on 2019/4/23.
 */
public class ObjectInputStreamTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        PipedOutputStream pipedOutputStream = new PipedOutputStream();
        PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
        //
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(pipedOutputStream);
        ObjectInputStream objectInputStream = new ObjectInputStream(pipedInputStream);
        //
        User user = new User();
        user.setA("test");
        objectOutputStream.writeObject(user); // 这种通过管道流的方式只适合对象。其他的基本类型必须在写完后关流。
        //
        user = (User) objectInputStream.readObject();
        System.out.println(user.getA());
    }
}

OutputStream

OutputStream类是字节输出流的抽象类,是所有字节输出流的父类。

方法含义
public abstract void write(int b)写入一个字节,传入值0~255范围。
超过这个范围的会被忽略(即只读取低八位字节)
public void write(byte b[])向输出流写入一定长度的字节
public void write(byte b[], int off, int len)向输出流写入一定长度的字节
public void flush()刷新,即将缓冲区的数据写入到实际目标。默认什么都不做
public void close()关闭输出流

不是所有的子类都支持flush和close

ByteArrayOutputStream

字节数组输出流

构造方法:

public ByteArrayOutputStream() {} //
public ByteArrayOutputStream(int size) {} // 

继承方法:

  • 没有重写flush方法,该方法不做任何事
  • 不支持close方法

新增方法:

  • public synchronized void writeTo(OutputStream out) throws IOException {} // 将数据写入到指定的输出流
  • public synchronized void reset() {} // 重置输出流,类似于清空数据。但实际是将下标变为0
  • public synchronized byte toByteArray()[] {} // 得到输出流的数据
  • public synchronized int size() {} // 输出流的长度
  • public synchronized String toString() {} // 将字节转为String
  • public synchronized String toString(String charsetName) throws UnsupportedEncodingException {} // 指定编码的toString
  • public synchronized String toString(int hibyte) {} // 废弃的方法
/**
 * @author zhengzewang on 2019/4/25.
 */
public class ByteArrayOutputStreamTest {

    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 默认size=32
        byteArrayOutputStream.write('a');
        byteArrayOutputStream.write(new byte[]{'b','c'});
        byteArrayOutputStream.write(new byte[]{'d','e','f','g'},1,2);
        byteArrayOutputStream.flush(); // 什么都不做
        byteArrayOutputStream.close(); // 什么都不做
        System.out.println(byteArrayOutputStream.toString());
        System.out.println(Arrays.toString(byteArrayOutputStream.toByteArray()));
        byteArrayOutputStream.reset();
        byteArrayOutputStream.write('a');
        System.out.println(byteArrayOutputStream.toString());
    }

}

运行输出:

abcef
[97, 98, 99, 101, 102]
a
FileOutputStream

文件字节输出流

构造方法:

public FileOutputStream(String name) throws FileNotFoundException {} //
public FileOutputStream(String name, boolean append) throws FileNotFoundException {} // 为true表示从文件末尾写入
public FileOutputStream(File file) throws FileNotFoundException {} //
public FileOutputStream(File file, boolean append) throws FileNotFoundException {} //
public FileOutputStream(FileDescriptor fdObj) {} // FileDescriptor是文件描述符

继承方法:

  • 没有重写flush方法,该方法不做任何事
  • 支持close方法

新增方法:

  • public final FileDescriptor getFD() throws IOException {} // 获取文件描述符
  • public FileChannel getChannel() {} // 获取文件通道
/**
 * @author zhengzewang on 2019/5/9.
 */
public class FileOutputStreamTest {

    public static void main(String[] args) throws IOException {
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\test.txt");
        fileOutputStream.write('a');
        fileOutputStream.flush(); // 啥都不做
        FileDescriptor fileDescriptor = fileOutputStream.getFD(); // 获取文件描述符
        FileChannel fileChannel = fileOutputStream.getChannel(); // 通过文件输出流打开的只能写,不能读
        ByteBuffer buf = ByteBuffer.allocate(32);
        buf.put((byte) 'b');
        buf.flip();
        fileChannel.write(buf);
        FileOutputStream fileOutputStream2 = new FileOutputStream(fileDescriptor); // 不需要boolean参数,因为它与上一个就是一个流
        fileOutputStream2.write('c');
        //
        fileOutputStream.close(); // 关闭后文件描述符和文件通道都会关闭
    }

}

执行代码后,会在文件test.txt中插入abc字符。

PipedOutputStream

PipedInputStream&PipedOutputStream

FilterOutputStream

FilterOutputStream是对OutputStream做了一层包装,其内部包含了一个OutputStream。FilterOutputStream提供的构造方法是需要保证OutpuStream:

public FilterOutputStream(OutputStream out) {
    this.out = out;
}

其常见的子类有:

  1. BufferedOutputStream
  2. DataOutputStream
  3. PrintStream
BufferedOutputStream

字节缓冲流

构造方法:

public BufferedOutputStream(OutputStream out) {} //
public BufferedOutputStream(OutputStream out, int size) {} //

继承方法:

  • write方法默认写入缓存中,将缓存满了之后会写入到被包装流中
  • flush方法会将缓冲中的数据写入到被包装流中,然后再调用被包装流的flush方法。
  • close方法先flush,再调用被包装流的close方法
/**
 * @author zhengzewang on 2019/5/10.
 */
public class BufferedOutputStreamTest {

    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 默认size=32
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream,4);
        //
        bufferedOutputStream.write(new byte[]{'a','b','c'});
        System.out.println(byteArrayOutputStream.toString());
        bufferedOutputStream.flush();
        System.out.println(byteArrayOutputStream.toString());
    }

}

运行输出:


abc
DataOutputStream

DataOutputStream对字节流进行了一层包装,以便其能够读取基本java数据类型。

构造方法:

public DataOutputStream(OutputStream out) {} //

继承方法:

  • wirte方法调用被包装流的write方法。并在DataOutputStream标记+1。
  • flush方法调用被包装流的flush方法。
  • close方法先flush,再调用被包装流的close方法

新增方法:

  • public final void writeBoolean(boolean v) throws IOException {} // 表示输出一个boolean数据
  • public final void writeByte(int v) throws IOException {} // 输出byte
  • public final void writeShort(int v) throws IOException {} // 输出short
  • public final void writeChar(int v) throws IOException {} // 输出char
  • public final void writeInt(int v) throws IOException {} // 输出int
  • public final void writeLong(long v) throws IOException {} // 输出long
  • public final void writeFloat(float v) throws IOException {} // 输出float
  • public final void writeDouble(double v) throws IOException {} // 输出double
  • public final void writeBytes(String s) throws IOException {} // 输出String(这方法仅适合输出字符串每个字符均占一个字节的情况)
  • public final void writeChars(String s) throws IOException {} // 输出String(注意,输出是以java内存使用的编码输出的,即Unicode)
  • public final void writeUTF(String str) throws IOException {} // 以UTF-8格式输出数据
  • public final int size() {} // 输出数据的长度

下面的例子讨论下writeChars和writeUTF的区别

/**
 * @author zhengzewang on 2019/5/19.
 */
public class DataOutputStreamTest {

    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 默认size=32
        DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
        //
        dataOutputStream.writeChars("郑泽旺");
        dataOutputStream.flush();
        System.out.println(byteArrayOutputStream.toString());
        System.out.println(byteArrayOutputStream.toString("Unicode"));
        //
        byteArrayOutputStream = new ByteArrayOutputStream(); // 默认size=32
        dataOutputStream = new DataOutputStream(byteArrayOutputStream);
        dataOutputStream.writeUTF("郑泽旺");
        dataOutputStream.flush();
        System.out.println(byteArrayOutputStream.toString());
        System.out.println(byteArrayOutputStream.toString("Unicode"));
    }

}
PrintStream

打印输出流

构造方法:

public PrintStream(OutputStream out) {} // 
public PrintStream(OutputStream out, boolean autoFlush) {} // 自动flush
public PrintStream(OutputStream out, boolean autoFlush, String encoding)  throws UnsupportedEncodingException {} //
public PrintStream(String fileName) throws FileNotFoundException {} //
public PrintStream(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException {} //
public PrintStream(File file) throws FileNotFoundException {} //
public PrintStream(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException {} //

继承方法:

  • 支持flush方法,调用被包装流的flush方法
  • 支持close方法

新增方法:

  • public boolean checkError() {} // 判断是否有错误
  • public void print(boolean b) {} // 打印bool
  • public void print(char c) {}
  • public void print(int i) {}
  • public void print(long l) {}
  • public void print(float f) {}
  • public void print(double d) {}
  • public void print(char s[]) {}
  • public void print(String s) {}
  • public void print(Object obj) {}
  • public void println() {}
  • public void println(boolean x) {}
  • public void println(char x) {}
  • public void println(int x) {}
  • public void println(long x) {}
  • public void println(float x) {}
  • public void println(double x) {}
  • public void println(char x[]) {}
  • public void println(String x) {}
  • public void println(Object x) {}
  • public PrintStream printf(String format, Object … args) {}
  • public PrintStream printf(Locale l, String format, Object … args) {}
  • public PrintStream format(String format, Object … args) {}
  • public PrintStream format(Locale l, String format, Object … args) {}
  • public PrintStream append(CharSequence csq) {}
  • public PrintStream append(CharSequence csq, int start, int end) {}
  • public PrintStream append(char c) {}
ObjectOutputStream

对象输出流

构造方法:

protected ObjectOutputStream() throws IOException, SecurityException {} // protected
public ObjectOutputStream(OutputStream out) throws IOException {} //

继承方法:

  • 支持flush方法和close,但依赖于被包装流的实现

新增方法:

  • public void useProtocolVersion(int version) throws IOException {} // 指定写入流时要使用的流协议版本
  • public final void writeObject(Object obj) throws IOException {} // 输出一个对象
  • public void writeUnshared(Object obj) throws IOException {} // 输出非共享对象?
  • public void defaultWriteObject() throws IOException {} // ?
  • public ObjectOutputStream.PutField putFields() throws IOException {} // ?
  • public void writeFields() throws IOException {} //
  • public void reset() throws IOException {}

其他剩余不介绍了,一般也用不上

Reader

Reader类是字符输入流的抽象类,是所有字符输入流的父类。

方法含义
public int read(java.nio.CharBuffer target)尝试将字符读入指定的字符缓冲区
public int read()读取单个字符。
public int read(char cbuf[])将字符读取数组中
abstract public int read(char cbuf[], int off, int len)将字符读入到数组中
public long skip(long n)跳过指定长度
public boolean ready()是否准备好
public boolean markSupported()是否支持标记。默认不支持
public void mark(int readAheadLimit)标记。默认抛异常
public void reset()重置。默认抛异常
abstract public void close()关闭流

markSupported、mark、reset、close方法是否支持由子类决定

InputStreamReader

处理字节流的字符输入流

构造方法:

public InputStreamReader(InputStream in) {} //
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException {} //
public InputStreamReader(InputStream in, Charset cs) {} //
public InputStreamReader(InputStream in, CharsetDecoder dec) {} //

继承方法:

  • 不支持markSupported、mark、reset等方法
  • 支持重写了close方法
/**
 * @author zhengzewang on 2019/5/21.
 */
public class InputStreamReaderTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'a', 'b'});
        InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);
        //
        System.out.println(inputStreamReader.markSupported());
        //
        System.out.println(inputStreamReader.read());
        char[] chars = new char[1];
        inputStreamReader.read(chars);
        System.out.println(chars[0]);
    }

}

运行输出:

false
97
b
FileReader

文件输入流,是InputStreamReader的子类。

构造方法:

public FileReader(String fileName) throws FileNotFoundException {} //
public FileReader(File file) throws FileNotFoundException {} //
public FileReader(File file) throws FileNotFoundException {} //

继承方法:

  • 同InputStreamReader一样
/**
 * @author zhengzewang on 2019/5/21.
 */
public class FileReaderTest {

    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("D:\\test\\test.txt");
        System.out.println(fileReader.markSupported());
        System.out.println(fileReader.read());
        char[] chars = new char[1];
        fileReader.read(chars);
        System.out.println(chars[0]);
    }

}
BufferedReader

缓冲字符输入流,增加了readLine方法,从而更好读取字符串

构造方法:

public BufferedReader(Reader in, int sz) {} // sz为缓冲大小
public BufferedReader(Reader in) {} // 默认大小为8192

继承方法:

  • 支持markSupported,即意味着支持mark、reset等方法
  • 支持重写了close方法。

新增方法:

  • public String readLine() throws IOException {} // 用于读取一行数据
/**
 * @author zhengzewang on 2019/5/21.
 */
public class BufferedReaderTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'a', 'b', 'c', 'd', 'e'});
        InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader, 3);
        System.out.println(bufferedReader.readLine());
        //
//        mark(bufferedReader);
        //
    }

}

运行输出

abcde

再来谈谈,mark(int readAheadLimit),同inputStream是一样的。

/**
 * @author zhengzewang on 2019/5/21.
 */
public class BufferedReaderTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'a', 'b','c', 'd', 'e'});
        InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader,3);
        //
        System.out.println(bufferedReader.markSupported());
        bufferedReader.mark(2); // 如果将2改为大于等于4,则第二次reset也不会报错。
        System.out.println(bufferedReader.read());
        System.out.println(bufferedReader.read());
        System.out.println(bufferedReader.read());
        bufferedReader.reset(); // 不报错
        bufferedReader.mark(3);
        System.out.println(bufferedReader.read());
        System.out.println(bufferedReader.read());
        System.out.println(bufferedReader.read());
        System.out.println(bufferedReader.read());
        bufferedReader.reset(); // 报错
        //
    }

}

如上,当第二次reset的时候会报错。但是第一mark时,将2改为4或4以上,则第二次也不会报错。因为第一次mark会触发扩容。

LineNumberReader

可操作行号的缓冲字符输入流,继承于BufferedReader,增加了读取行的功能

构造方法:

public LineNumberReader(Reader in) {} //
public LineNumberReader(Reader in, int sz) {} //

继承方法:

  • 继承自BufferedReader,支持markSupported,即支持mark和reset方法
  • 继承自BufferedReader,支持close方法。

新增方法:

  • public String readLine() throws IOException {} // 读取一行,以换行符结尾
  • public int getLineNumber() {} // 获取当前行
  • public void setLineNumber(int lineNumber) {} // 设置当前行的行(并不是跳到指定行)
/**
 * @author zhengzewang on 2019/5/25.
 */
public class LineNumberReaderTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'a', 'b', '\n', 'c', 'd', '\n', 'e', 'f', '\n', 'g', 'h', '\n', 'i'});
        InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);
        LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);
        //
        System.out.println(lineNumberReader.markSupported()); // true
        System.out.println(lineNumberReader.getLineNumber()); // 0
        System.out.println(lineNumberReader.readLine()); // ab
        System.out.println(lineNumberReader.getLineNumber()); // 1
        lineNumberReader.setLineNumber(3);
        System.out.println(lineNumberReader.getLineNumber()); // 3
        System.out.println(lineNumberReader.readLine()); // cd
        System.out.println(lineNumberReader.getLineNumber()); // 4
    }

}
CharArrayReader

用于char的缓冲字符流

构造方法:

public CharArrayReader(char buf[]) {} //
public CharArrayReader(char buf[], int offset, int length) {} // 

继承方法:

  • 支持markSupported,即支持mark和reset方法。但mark方法的readAheadLimit参数被忽略
  • 支持重写了close方法
StringReader

字符串输入流

构造方法:

public StringReader(String s) {} //

继承方法:

  • 支持markSupported,即支持mark和reset方法。但mark方法的readAheadLimit参数被忽略,但不能小于0
  • 支持重写了close方法
/**
 * @author zhengzewang on 2019/5/25.
 */
public class StringReaderTest {

    public static void main(String[] args) throws IOException {
        StringReader stringReader = new StringReader("郑泽旺");
        System.out.println(stringReader.markSupported()); // true
        System.out.println((char) stringReader.read()); // 郑
        stringReader.close();
    }

}
PipedReader&PipedWriter

管道输入流。PipedReader与PipedWriter结合使用

PipedReader构造方法:

public PipedReader(PipedWriter src) throws IOException {} // 默认大小1024
public PipedReader(PipedWriter src, int pipeSize) throws IOException {} //
public PipedReader() {} //
public PipedReader(int pipeSize) {} //

PipedWriter构造方法:

public PipedWriter(PipedReader snk)  throws IOException {}
public PipedWriter() {}

PipedReader继承方法:

  1. 不支持markSupported,即不支持mark和reset方法。
  2. 支持重写了close方法

PipedWriter继承方法:

  1. 实现了flush方法
  2. 实现了close方法

新增方法:

如构造方法所示,PipedReader与PipedWriter可通过构造方法连接。如果不通过构造方法,也可以通过下列方法进行连接。

public void connect(PipedWriter src) throws IOException {} // PipedReader 
public synchronized void connect(PipedReader snk) throws IOException {} // PipedWriter

PipedReader与PipedWriter的主要方法都是synchronized线程安全的,它们交互流程如下:

  1. PipedReader读取数据,判断是否存在连接,如果没有拦截抛出异常。
  2. 存在连接,判断连接的PipedWriter是否存在输出线程,如果没有,等待。
  3. 如果存在输出线程,判断线程是否已死,如果已死其数据已全部输入,则抛出异常,提示输出线程已死。
  4. 如果线程已死,但仍有待输入数据,则可继续输入。
  5. 如果输出线程未死,但无数据,等待。

以下情况容易造成死锁:

  1. 同一个线程内输出输入
  2. 输出线程一直占有资源,且未有输出数据。

以下是造成阻塞的两个示例

private static void lock1() throws IOException {
    PipedWriter pipedWriter = new PipedWriter();
    PipedReader pipedReader = new PipedReader(pipedWriter);
    pipedReader.read(); // 一直等待
}

private static void lock2() throws IOException {
    PipedWriter pipedWriter = new PipedWriter();
    PipedReader pipedReader = new PipedReader(pipedWriter);
    //
    new Thread(() -> {
        try {
            pipedWriter.write(97);
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    System.out.println(pipedReader.read());
    System.out.println("已读取一次数据");
    System.out.println(pipedReader.read()); // 一直等待
    System.out.println("已读取两次数据");
}

推荐的使用方法:

/**
 * @author zhengzewang on 2019/5/26.
 */
public class PipedReaderTest {

    public static void main(String[] args) throws IOException {
        PipedWriter pipedWriter = new PipedWriter();
        PipedReader pipedReader = new PipedReader(pipedWriter);
        new Thread(() -> {
            while (true) {
                try {
                    System.out.print((char) pipedReader.read());
                } catch (IOException e) {
                    // TODO 异常被吃掉了,为什么不提供一个方法判断连接是否关闭
                    break;
                }
            }
        }).start();
        new Thread(() -> {
            String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
            for (char c : str.toCharArray()) {
                try {
                    pipedWriter.write(c);
                    Thread.sleep(2000);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
FilterReader

字符过滤输入流

FilterReader是对Reader做了一层包装,其内部包含了一个Reader。FilterReader是一个抽象类,默认的构造方法是protected修饰的:

protected FilterReader(Reader in) {} //

常见的子类有:

  1. PushbackReader
PushBackReader

推回输入流

构造方法:

public PushbackReader(Reader in, int size) {} // 指可回退的数量
public PushbackReader(Reader in) {} // 默认大小为1

继承方法:

  1. 不支持markSupported方法,即不支持mark和reset方法
  2. 支持重写了close方法

新增方法:

  1. public void unread(int c) throws IOException {} // 回写一个字符
  2. public void unread(char cbuf[], int off, int len) throws IOException {} //
  3. public void unread(char cbuf[]) throws IOException {} //
/**
 * @author zhengzewang on 2019/5/26.
 */
public class PushbackReaderTest {

    public static void main(String[] args) throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'a', 'b', 'c', 'd', 'e'});
        InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);
        PushbackReader pushbackReader = new PushbackReader(inputStreamReader, 3);
        //
        System.out.println(pushbackReader.markSupported());
        pushbackReader.unread(97);
        pushbackReader.unread(0xffff);
        pushbackReader.unread(0xffff + 1);
        System.out.println((char) pushbackReader.read());
        System.out.println((char) pushbackReader.read());
        System.out.println((char) pushbackReader.read());
        pushbackReader.unread(97);
        pushbackReader.close();
    }
    
}

运行输出:

false
 

a

Writer

Reader类是字符输出流的抽象类,是所有字符输出流的父类。

方法含义
public void write(int c)输出一个字符
public void write(char cbuf[])输出字符数组
abstract public void write(char cbuf[], int off, int len)输出字符数组
public void write(String str)输出字符串
public void write(String str, int off, int len)输出字符串
public Writer append(CharSequence csq)添加字符序列
public Writer append(CharSequence csq, int start, int end)添加字符序列
public Writer append(char c)添加字符
abstract public void flush()刷新。一般用于将数据从缓冲中刷新到实际输出流中
abstract public void close()关闭流

不是所有的子类都支持flush和close

CharArrayWriter

用于char的缓冲输出流

构造方法:

public CharArrayWriter() {} //
public CharArrayWriter(int initialSize) {} //

继承方法:

  1. flush方法什么都不做。
  2. close方法什么都不做。

新增方法:

  1. public void reset() {} // 重置
  2. public char toCharArray()[] {} // 输出字符数组
  3. public int size() {} //
  4. public String toString() {} //
/**
 * @author zhengzewang on 2019/5/26.
 */
public class CharArrayWriterTest {

    public static void main(String[] args) throws IOException {
        CharArrayWriter charArrayWriter = new CharArrayWriter();
        charArrayWriter.write("为实现中华民族伟大复兴的中国梦不懈奋斗");
        System.out.println(charArrayWriter.toString());
    }

}
OutputStreamWriter

处理字节流的字符输出流

构造方法:

public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException {} //
public OutputStreamWriter(OutputStream out) {} //
public OutputStreamWriter(OutputStream out, Charset cs) {} //
public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {} //

继承方法:

  1. 实现了flush方法
  2. 实现了close方法

新增方法:

  1. public String getEncoding() {} // 获取编码方法
/**
 * @author zhengzewang on 2019/5/26.
 */
public class OutputStreamWriterTest {

    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream);
        outputStreamWriter.write("为实现中华民族伟大复兴的中国梦不懈奋斗");
        System.out.println(outputStreamWriter.getEncoding());
        System.out.println(byteArrayOutputStream.toString());
        outputStreamWriter.flush();
        System.out.println(byteArrayOutputStream.toString());
        outputStreamWriter.close();
    }

}
FileWriter

文件输出流

构造方法:

public FileWriter(String fileName) throws IOException {} 
public FileWriter(String fileName, boolean append) throws IOException {} // append指文本后继续输出
public FileWriter(File file) throws IOException {} //
public FileWriter(File file, boolean append) throws IOException {} //
public FileWriter(FileDescriptor fd) {} //

继承方法:

  1. 同OutputStreamWriter
/**
 * @author zhengzewang on 2019/5/26.
 */
public class FileWriterTest {

    public static void main(String[] args) throws IOException {
        FileWriter fileWriter = new FileWriter("D:\\test\\test.txt");
        fileWriter.write("为实现中华民族伟大复兴的中国梦不懈奋斗");
        fileWriter.flush(); // flush或close后,字符串才会被写入文本中
        fileWriter.close();
    }

}
FilterWriter

字符过滤输出流

FilterWriter是对Writer做了一层包装,其内部包含了一个Writer。FilterWriter是一个抽象类,默认的构造方法是protected修饰的:

protected FilterWriter(Writer out) {} //

io包下面没有常用的子类

PrintWriter

打印输出流。打印输出流 包装了Writer或OutputStream,以打印的方式支持输出不同的数据类型

构造方法:

public PrintWriter (Writer out) {} //
public PrintWriter(Writer out, boolean autoFlush) {}
public PrintWriter(OutputStream out) {} //
public PrintWriter(OutputStream out, boolean autoFlush) {} 
public PrintWriter(String fileName) throws FileNotFoundException {} //
public PrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException {}
public PrintWriter(File file) throws FileNotFoundException {}
public PrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException {}

继承方法:

  1. 实现了flush方法
  2. 实现了close方法

新增方法:

  1. public boolean checkError() {} //
  2. public void print(boolean b) {} // true or false
  3. public void print(char c) {} //
  4. public void print(int i) {} //
  5. public void print(long l) {} //
  6. public void print(float f) {}
  7. public void print(double d) {}
  8. public void print(char s[]) {}
  9. public void print(String s) {}
  10. public void print(Object obj) {} // obj.toString
  11. public void println() {} // 打印换行
  12. public void println(boolean x) {} // 先打印后换行
  13. public void println(char x) {}
  14. public void println(int x) {}
  15. public void println(long x) {}
  16. public void println(float x) {}
  17. public void println(double x) {}
  18. public void println(char x[]) {}
  19. public void println(String x) {}
  20. public void println(Object x) {} //
  21. public PrintWriter printf(String format, Object … args) {} //
  22. public PrintWriter printf(Locale l, String format, Object … args) {}
  23. public PrintWriter format(String format, Object … args) {}
  24. public PrintWriter format(Locale l, String format, Object … args) {}
/**
 * @author zhengzewang on 2019/5/26.
 */
public class PrintWriterTest {

    public static void main(String[] args) {
        CharArrayWriter charArrayWriter = new CharArrayWriter();
        PrintWriter printWriter = new PrintWriter(charArrayWriter);
        //
        printWriter.print("为实现中华民族伟大复兴的中国梦不懈奋斗");
        System.out.println(charArrayWriter.toString());
    }

}
BufferedWriter

缓冲字符输出流

构造方法:

public BufferedWriter(Writer out, int sz) {} 
public BufferedWriter(Writer out) {} // 默认大小 8192

继承方法:

  1. 实现了flush方法
  2. 实现了close方法

新增方法:

  1. public void newLine() throws IOException {} // 增加换行

/**
 * @author zhengzewang on 2019/5/26.
 */
public class BufferedWriterTest {

    public static void main(String[] args) throws IOException {
        CharArrayWriter charArrayWriter = new CharArrayWriter();
        BufferedWriter bufferedWriter = new BufferedWriter(charArrayWriter);
        //
        bufferedWriter.newLine();
        bufferedWriter.write("为实现中华民族伟大复兴的中国梦不懈奋斗");
        System.out.println(charArrayWriter.toString());
        bufferedWriter.flush();
        System.out.println(charArrayWriter.toString());
        bufferedWriter.close();
    }

}

运行输出:



为实现中华民族伟大复兴的中国梦不懈奋斗
StringWriter

字符串输出流。其内部是使用StringBuffer实现的

构造方法:

public StringWriter(int initialSize) {} // 
public StringWriter() {} // 默认大小16

继承方法:

  1. flush方法什么都不做。
  2. close方法什么都不做。
  3. 重写了Object的toString方法

新增方法:

  1. public StringBuffer getBuffer() {}
/**
 * @author zhengzewang on 2019/5/26.
 */
public class StringWriterTest {

    public static void main(String[] args) throws IOException {
        StringWriter stringWriter = new StringWriter();
        stringWriter.write("为实现中华民族伟大复兴的中国梦不懈奋斗");
        System.out.println(stringWriter.toString());
        System.out.println(stringWriter.getBuffer());
        stringWriter.flush();
        stringWriter.close();
    }

}
PipedWriter

管道输出流。见 PipedReader&PipedWriter

读取和写入文件的几种方式

用FileInputStream读取。

因为FileInputStream是字节流,所以每次读的都是一个字节,实际用途可能不大。

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("D:\\test\\test.txt");
        byte[] bytes = new byte[fileInputStream.available()];
        fileInputStream.read(bytes);
        System.out.println(new String(bytes));
    }

}
用BufferInputStream读取

使用BufferInputStream将FileInputStream包装一层。其本质FileInputStream没多大区别

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("D:\\test\\test.txt");
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
        byte[] bytes = new byte[bufferedInputStream.available()];
        bufferedInputStream.read(bytes);
        System.out.println(new String(bytes));
    }

}
用FileReader读取

FileReader字符流,每次可读取一个字符。

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("D:\\test\\test.txt");
        int read = fileReader.read();
        while (read != -1) {
            System.out.print((char)read);
            read = fileReader.read();
        }
        System.out.println();
    }
}    
用BufferedReader读取,推荐

使用BufferedReader包装FileReader读取文件。由于BufferedReader提供readLine方法,推荐。

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("D:\\test\\test.txt");
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String s = bufferedReader.readLine();
        while (s != null) {
            System.out.println(s);
            s = bufferedReader.readLine();
        }
    }
}
用FileOutputStream写入
/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\test.txt");
        byte[] bytes = str.getBytes();
        fileOutputStream.write(bytes);
        fileOutputStream.close();
    }
}
用BufferedOutputStream、DataOutputStream、PrintStream写入

这三个均属于FilterOutputStream,其中DataOutputStream和PrintStream可以写入字符串,而BufferedOutputStream同FileOutputStream大同小异。

  1. BufferedOutputStream用法同FileOutputStream
  2. DataOutputStream在于需要小心的控制编码,实际上如果不在乎文件可读性,与DataInputStream配合实际,推荐这个
  3. PrintStream采用默认编码,推荐使用
/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\test.txt");
        PrintStream printStream = new PrintStream(fileOutputStream); // 其他FilterOutputStream类似
        printStream.print(str);
        printStream.close();
    }
}
用FileWriter写入,推荐

直接使用FileWriter往文件写入数据。推荐使用

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
        FileWriter writer = new FileWriter("D:\\test\\test.txt");
        writer.write(str);
        writer.close();
    }
}
用PrintWriter、BufferedWriter等写入

BufferedWriter和BufferedWriter包装FileWriter,可直接往文件中写入数据。由于FileWriter可直接写入数据,故该方法没必要使用。

  1. BufferedWriter提供输出换行的功能,无序用户考虑不同系统换行兼容的问题
  2. PrintWriter提供各种格式化的方法,特殊情况可使用

/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
        FileWriter writer = new FileWriter("D:\\test\\test.txt");
        BufferedWriter bufferedWriter = new BufferedWriter(writer);
        bufferedWriter.write(str);
        bufferedWriter.close();
    }
}
/**
 * @author zhengzewang on 2019/5/26.
 */
public class WRFileTest {

    public static void main(String[] args) throws IOException {
        String str = "为实现中华民族伟大复兴的中国梦不懈奋斗";
        FileWriter writer = new FileWriter("D:\\test\\test.txt");
        PrintWriter printWriter = new PrintWriter(writer);
        printWriter.format("%s",str);
        printWriter.close();
    }
}

Zip流

Zip流包括ZipInputStreamZipOutputStream,分别用于解压和压缩文件。

ZipInputStream:

构造方法:

public ZipInputStream(InputStream in) {} //
public ZipInputStream(InputStream in, Charset charset) {} //
方法含义
public ZipEntry getNextEntry()读取下一个ZipEntry,并将流内的位置移至该entry所指数据的开头
public void closeEntry()关闭当前ZipEntry
public int available()判断是否已读完当前ZipEntry
public int read(byte[] b, int off, int len)读取数据
public long skip(long n)跳过当前ZipEntry指定的字节数
public void close()关闭
ZipOutputStream:

构造方法:

public ZipOutputStream(OutputStream out) {} 
public ZipOutputStream(OutputStream out, Charset charset) {}
方法含义
public void setComment(String comment)设置此Zip文件的注释文字
public void setMethod(int method)使用的压缩方法?
public void setLevel(int level)压缩级别。共有0-9十个级别。从低到高,时间越长,空间越小。
public void putNextEntry(ZipEntry e)开始一个新的ZipEntry,并将流内的位置移至此entry所指数据的开头
public void closeEntry()关闭当前ZipEntry
public synchronized void write(byte[] b, int off, int len)输出数据到当前ZipEntry
public void finish()完成当前写入Zip输出流的内容,无须关闭它所配合的OutputStream
public void close()关闭流
用法示例:
/**
 * @author zhengzewang on 2019/5/26.
 */
public class ZipTest {

    public static void main(String[] args) throws IOException {
        String compressDir = "D:\\test\\compress";
        String decompressDir = "D:\\test\\decompress";
        String zipName = "D:\\test\\test.zip";
        compress(compressDir, zipName); // zip/rar/jar 等均支持
        decompress(decompressDir, zipName);
        //
    }

    private static void decompress(String dir, String zipName) throws IOException {
        File file = new File(zipName);
        FileInputStream fileInputStream = new FileInputStream(file);
        ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
        ZipEntry zipEntry = zipInputStream.getNextEntry();
        while (zipEntry != null) {
            String entryName = zipEntry.getName();
            File temp = new File(dir + File.separator + entryName);
            if (zipEntry.isDirectory() || entryName.endsWith("\\") || entryName.endsWith(File.separator)) {
                if (!temp.exists()) {
                    temp.mkdirs();
                }
            } else {
                if (!temp.exists()) {
                    temp.getParentFile().mkdirs();
                }
                FileOutputStream fileOutputStream = new FileOutputStream(temp);
                int read = zipInputStream.read();
                while (read != -1) {
                    fileOutputStream.write(read);
                    read = zipInputStream.read();
                }
                fileOutputStream.close();
                zipInputStream.closeEntry();
            }
            zipEntry = zipInputStream.getNextEntry();
        }
        zipInputStream.close();
    }

    private static void compress(String dir, String zipName) throws IOException {
        File zip = new File(zipName);
        FileOutputStream fileOutputStream = new FileOutputStream(zip);
        ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
        //
        zip(zipOutputStream, new File(dir), "", zip);
        zipOutputStream.close();
    }

    private static void zip(ZipOutputStream zipOutputStream, File file, String base, File zip) throws IOException {
        if (file.getPath().equals(zip.getPath())) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            base = (base == null || base.trim().isEmpty()) ? "" : base + File.separator;
            if (files.length == 0) {
                zipOutputStream.putNextEntry(new ZipEntry(base));
            } else {
                for (File f : files) {
                    base = (base == null || base.trim().isEmpty()) ? "" : base + File.separator;
                    zip(zipOutputStream, f, base + f.getName(), zip);
                }
            }
        } else {
            zipOutputStream.putNextEntry(new ZipEntry(base));
            FileInputStream fileInputStream = new FileInputStream(file);
            int b = fileInputStream.read();
            while (b != -1) {
                zipOutputStream.write(b);
                b = fileInputStream.read();
            }
            fileInputStream.close();
        }
    }

}
JarInputStream&JarOutputStream

TODO

参考文档

Java里的管道输入流 PipedInputStream与管道输出流 PipedOutputStream




thanks! 顶部 底部 **
--郑泽旺
**
2019-04-06
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值