流
流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。IO(Input/Output,输入/输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。
输入输出流
- 输入流有:InputStream(字节输入流)、Reader(字符输入流)。
- 输出流有:OutputStream(字节输出流)、Writer(字符输出流)。
他们的基本结构如下:
输入输出流根据分类包括:
- Reader
- InputStreamReader:处理字节流的字符输入流
- FileReader:文件输入流
- BufferedReader:缓冲字符输入流
- LineNumberReader:可操作行号的缓冲字符输入流
- CharArrayReader:用于char的缓冲字符流
- StringReader:字符串输入流
- PipedReader:管道输入流
- FilterReader:字符过滤输入流
- PushBackReader:推回输入流
- InputStreamReader:处理字节流的字符输入流
- Writer
- CharArrayWriter:用于char的缓冲输出流
- OutputStreamWriter:处理字节流的字符输出流
- FileWriter:文件输出流
- FilterWriter:字符过滤输出流
- PrintWriter:打印输出流
- BufferedWriter:缓冲字符输出流
- StringWriter:字符串输出流
- PipedWriter:管道输出流
- InputStream
- ByteArrayInputStream:字节数组输入流
- SequenceInputStream:合并输入流
- FileInputStream:文件输入流
- PipedInputStream:管道输入流
- StringBufferInputStream:字符输入流(已废弃)
- FilterInputStream:字节过滤输入流
- BufferedInputStream:缓冲字节输入流
- LineNumberInputStream:可操作行号的缓冲字节输入流(已废弃)
- PushBackInputStream:推回输入流
- DataInputStream:数据输入流
- ObjectInputStream:对象输入流
- OutputStream
- ByteArrayOutputStream:字节数组输出流
- FileOutputStream:文件输出流
- PipedOutputStream:管道输出流
- FilterOutputStream:过滤器字节输出流
- BufferedOutputStream:缓冲输出流
- DataOutputStream:数据输出流
- PrintStream:打印输出流
- ObjectOutputStream:对象输出流
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) {}
- 第一个构造方法根据字节数组构造一个字节输入流,offset=0,length=buf.length
- 第二个构造方法指定标记位置为offset,以及可读取的长度为length(实际长度是offset+length与字节数组长度的最小值,Math.min(offset + length, buf.length))
继承方法:
- close()方法没有任何效果
- 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)
- 无论是何种构造方法,最终都会将其存在SequenceInputStream中定义的一个Enumeration属性中
继承方法:
- 不支持mark与reset方法,即
markSupported()
返回false - read()方法将按顺序读取每个流的字节
- available()只返回当前流(Enumeration取出的当前元素)的字节数
- read(byte b[])与read(byte b[], int off, int len)默认读取当前流的数据。如果当前流没有数据,则读取下个流的数据
- available()方法与read(byte b[])结合只能读取到当前流数据(循环调用)。因为当前流数据读取完毕后,available()方法返回0
- 流只能读一次。
- 支持skip方法。
- 支持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是文件描述符
继承方法:
- 不支持mark与reset方法,即markSupported()返回false
- 流只能读取一次
- 支持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继承方法:
- 不支持mark与reset方法,即markSupported()返回false
- 流只能读取一次
- 支持skip和close方法
PipedOutputStream继承方法:
- 支持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
的,即线程安全的。它们的交互流程如下:
- PipedInputStream读取数据,判断是否存在连接,如果没有连接抛出异常。
- 存在连接,判断连接的PipedOutputStream是否有输出线程,如果没有,等待。
- 如果存在输出线程,判断线程是否已死,如果已死且数据已全部输入,则抛出异常,提示输出线程已死。
- 如果输出线程已死,但仍有待输入数据,则可继续输入。
- 如果输出线程未死,但无数据,等待。
由上可看出,以下情况容易造成死锁:
- 同一个线程内输出输入。
- 输出线程一直占据资源,且未有输出数据。
以下情况会造成死锁:
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) {} // 看到构造方法大概就明白什么意思了
继承方法:
- 不支持mark与reset方法,即markSupported()返回false
- 支付skip方法
- 不支持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;
}
其常见的子类有:
- BufferedInputStream
- LineNumberInputStream(已废弃)
- PushBackInputStream
- DataInputStream
BufferedInputStream
字节缓冲流
构造方法
public BufferedInputStream(InputStream in) {}
public BufferedInputStream(InputStream in, int size) {}
第一个构造方法是传入一个InputStream,第二个构造方法则是指定了大小。(注意,这里指定的大小是内存缓冲的大小,第一个构造方法默认大小是8192)
继承方法:
- 支持所有的父类方法
- 对于方法
mark(int readlimit)
的参数readlimit也是支持的。也就是readlimit表示此后允许读取的字节数,超过这个字节数可能会导致reset失败。
这里我们着重讲解一下readlimit。
BufferedInputStream,顾名思义,是一个包装了InputStream的缓冲流。它的size可能比被包装流小很多,这也就是缓冲的意义。BufferedInputStream可以不用将被包装流中数据全部取出,而是动态的将其加载到缓冲中。简单地说:
- BufferedInputStream从被包装流中读取size字节数据
- 当这些数据被读完时,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) {}
这个类在包装了字节流的基础上提供了获取行号的功能。而行号则是由换行符决定的。
继承方法:
- 基本上取决于被包装类。
新增方法:
- public int getLineNumber():获取当前行号
- public void setLineNumber(int lineNumber):设置当前行号
换行用\r
或\r\n
表示
PushBackInputStream
回退流支持将读取的字节重新放回流中。也可以放入其他任意字节数据。
构造方法:
public PushbackInputStream(InputStream in) {} // size = 1
public PushbackInputStream(InputStream in, int size) {}
size是指可支持的回退的流的长度。
继承方法:
- 不支持mark与reset方法,即markSupported()返回false。意味着流只能读取一次。
- 支持skip方法和close方法
新增方法:
- public void unread(int b) throws IOException {} //
- public void unread(byte[] b, int off, int len) throws IOException {} //
- 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) {} //
继承方法:
- 所有继承方法的实现均取决于被包装流
新增方法:
- public final void readFully(byte b[]) throws IOException {} // 读满缓冲区b,读不满则等待。没有数据可读了且没满抛出异常
- public final void readFully(byte b[], int off, int len) throws IOException {} //
- public final int skipBytes(int n) throws IOException {} // 和skip的区别在于skip只尝试一次,有可能跳过的数并不是n,而skipBytes尽量保证为跳过的数为n,除非真的没有了
- public final boolean readBoolean() throws IOException {} // 读取一个字节,如果是0(无符号),则返回true,否则为false
- public final byte readByte() throws IOException {} // 读取一个字节,并返回byte类型(-128~127)
- public final int readUnsignedByte() throws IOException {} // 读取一个字节,返回无符号数值(0~255)
- public final short readShort() throws IOException {} // 读取两个字节,并返回short类型(-32768~32767)
- public final int readUnsignedShort() throws IOException {} // 读取两个字节,并返回无符号数值(0~65535)
- public final char readChar() throws IOException {} // 读取两个字节,并返回一个char类型
- public final int readInt() throws IOException {} // 读取4个字节,并返回int类型
- public final long readLong() throws IOException {} // 读取8个字节,并返回long类型
- public final float readFloat() throws IOException {} // 读取4个字节,并返回short类型
- public final double readDouble() throws IOException {} // 读取8个字节,并返回double类型
- public final String readLine() throws IOException {} // 已废弃。读取一行,直到换行符
- public final String readUTF() throws IOException {} // 以UTF-8形式读取数据
- 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 {} //
继承方法:
- 不支持mark与reset方法,即markSupported()返回false
- 支持skip和close方法
新增方法:
- public final Object readObject() throws IOException, ClassNotFoundException {} // 读取对象。
- public Object readUnshared() throws IOException, ClassNotFoundException {} // 读取“非共享”对象。?
- public void defaultReadObject() throws IOException, ClassNotFoundException {} // ?
- public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException {} // ?
- public void registerValidation(ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException {} // ?
- public boolean readBoolean() throws IOException {} // 读取bool型数据。(包括包装类)
- public byte readByte() throws IOException {} // 读取byte型数据。(包括包装类)
- public int readUnsignedByte() throws IOException {} // 读取无符号byte型数据。(包括包装类)
- public char readChar() throws IOException {} // 读取字符型数据。(包括包装类)
- public short readShort() throws IOException {} // 读取Short型数据。(包括包装类)
- public int readUnsignedShort() throws IOException {} // 读取无符号Short型数据。(包括包装类)
- public int readInt() throws IOException {} // 读取int型数据。(包括包装类)
- public long readLong() throws IOException {} // 读取long型数据。(包括包装类)
- public float readFloat() throws IOException {} // 读取float型数据。(包括包装类)
- public double readDouble() throws IOException {} // 读取double型数据。(包括包装类)
- public void readFully(byte[] buf) throws IOException {} // 读满缓冲区b,读不满则等待。没有数据可读了且没满抛出异常
- public void readFully(byte[] buf, int off, int len) throws IOException {} //
- public int skipBytes(int len) throws IOException {} // 和skip的区别在于skip只尝试一次,有可能跳过的数并不是n,而skipBytes尽量保证为跳过的数为n,除非真的没有了
- public String readLine() throws IOException {} // 已废弃
- 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;
}
其常见的子类有:
- BufferedOutputStream
- DataOutputStream
- 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继承方法:
- 不支持markSupported,即不支持mark和reset方法。
- 支持重写了close方法
PipedWriter继承方法:
- 实现了flush方法
- 实现了close方法
新增方法:
如构造方法所示,PipedReader与PipedWriter可通过构造方法连接。如果不通过构造方法,也可以通过下列方法进行连接。
public void connect(PipedWriter src) throws IOException {} // PipedReader
public synchronized void connect(PipedReader snk) throws IOException {} // PipedWriter
PipedReader与PipedWriter的主要方法都是synchronized
线程安全的,它们交互流程如下:
- PipedReader读取数据,判断是否存在连接,如果没有拦截抛出异常。
- 存在连接,判断连接的PipedWriter是否存在输出线程,如果没有,等待。
- 如果存在输出线程,判断线程是否已死,如果已死其数据已全部输入,则抛出异常,提示输出线程已死。
- 如果线程已死,但仍有待输入数据,则可继续输入。
- 如果输出线程未死,但无数据,等待。
以下情况容易造成死锁:
- 同一个线程内输出输入
- 输出线程一直占有资源,且未有输出数据。
以下是造成阻塞的两个示例
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) {} //
常见的子类有:
- PushbackReader
PushBackReader
推回输入流
构造方法:
public PushbackReader(Reader in, int size) {} // 指可回退的数量
public PushbackReader(Reader in) {} // 默认大小为1
继承方法:
- 不支持markSupported方法,即不支持mark和reset方法
- 支持重写了close方法
新增方法:
- public void unread(int c) throws IOException {} // 回写一个字符
- public void unread(char cbuf[], int off, int len) throws IOException {} //
- 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) {} //
继承方法:
- flush方法什么都不做。
- close方法什么都不做。
新增方法:
- public void reset() {} // 重置
- public char toCharArray()[] {} // 输出字符数组
- public int size() {} //
- 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) {} //
继承方法:
- 实现了flush方法
- 实现了close方法
新增方法:
- 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) {} //
继承方法:
- 同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 {}
继承方法:
- 实现了flush方法
- 实现了close方法
新增方法:
- public boolean checkError() {} //
- public void print(boolean b) {} // true or false
- 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) {} // obj.toString
- 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 PrintWriter printf(String format, Object … args) {} //
- public PrintWriter printf(Locale l, String format, Object … args) {}
- public PrintWriter format(String format, Object … args) {}
- 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
继承方法:
- 实现了flush方法
- 实现了close方法
新增方法:
- 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
继承方法:
- flush方法什么都不做。
- close方法什么都不做。
- 重写了Object的toString方法
新增方法:
- 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大同小异。
- BufferedOutputStream用法同FileOutputStream
- DataOutputStream在于需要小心的控制编码,实际上如果不在乎文件可读性,与DataInputStream配合实际,推荐这个
- 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可直接写入数据,故该方法没必要使用。
- BufferedWriter提供输出换行的功能,无序用户考虑不同系统换行兼容的问题
- 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流包括ZipInputStream
和ZipOutputStream
,分别用于解压和压缩文件。
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! 顶部 底部 **