前言
io 流在 Java 的常识体系中,属于很杂的那种了,次要是波及的类太多,输出流、输入流、文件输出流、文件输入流、字节输出流和输入流、字符输出流和输入流等等,常常把人弄晕。然而它的用处却十分宽泛,文件的操作、网络的操作等都离不开它,本文通过平时罕用操作来梳理一下 io 流中波及的重要类及办法。
什么是流
在 Java 中,文件个别不是独自解决的,而是视为输入输出设施的一种,Java 应用一个对立的概念来解决所有的输入输出,包含键盘输入和网络输入输出等。这个对立的概念就是 流,流又分为输出流和输入流,InputStream 和 OutputStream 别离示意输出流和输入流,但这两个类是抽象类,大部分办法都是子类来实现的,如:
- BufferedInputStream 和 BufferedOutputStream 对流起缓冲作用
- DataInputStream 和 DataOutputStream 能够按 8 种根本类型和字符串类型对流进行读写
- ZipInputStream 和 ZipOutputStream 能够对流进行压缩和解压缩
- printStream 能够对根本类型和对象输入为其字符串示意
以 InputStream 和 OutputStream 为基类的流根本都是以二进制模式解决数据的,不可能不便地解决文本文件,没有编码的概念,可能不便地解决文本数据的基类是 Reader 和 Writer,它也有很多子类,如:
- FileReader 和 FilerWriter 用来读写文件
- BufferedReader 和 BufferedWriter 起缓存装璜
- CharArrayReader 和 CharArrayWriter 能够将字符数组包装为 Reader 和 Writer
- StringReader 和 StringWriter 能够将字符串包装为 Reader 和 Writer
- InputStreamReader 和 OutputStreamWriter 能够将 InputStream 和 OutputStream 转换为 Reader 和 Writer
- printWriter 能够将根本类型和对象输入为其字符串示意
二进制文件和字节流
二进制文件:所有文件外面的数据都是通过二进制来保留的,但为了不便解决数据,引入了文件类型的概念,次要分为文本文件和二进制文件。文本文件是指那些文本编辑器能够间接查看和编辑的文件,如.txt、.html、.java 等文件;二进制文件是指一些非凡格局和性能的文件,如.zip、.jpg、.doc、.pdf 等文件
Java 次要通过 InputStream 和 OutputStream 来实现二进制字节模式解决文件。
OutputStream
OutputStream 次要有上面几个办法:
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException
public void close() throws IOException
public void flush() throws IOException
write(int b)
办法向流中写入一个字节,须要子类来实现。
write(byte b[])
办法外面调用 write(byte b[], int off, int len)
办法来实现,将 b[]数组从第 off 地位开始,len 长度的数据写入到流中。
close()
敞开流,下面几个办法就算流中的数据都操作完了,也会阻塞,期待流中持续增加数据,因而咱们操作完数据当前,须要在 finally 办法中通过 close()办法来敞开流。
flush()
将缓冲而未理论写入的数据进行理论写入,个别作用于带缓冲的流中,如 BufferedOutputStream。
举例
OutputStream outputStream = null;
try {outputStream = new FileOutputStream("hello.txt");
String data = "hello world";
outputStream.write(data.getBytes());
} catch (Exception e) {e.printStackTrace();
} finally {outputStream.close();
}
InputStream
InputStream 次要有上面几个办法:
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException
public int read(byte b[], int off, int len) throws IOException
public void close() throws IOException
read()
办法从流中读取下一个字节,须要子类来实现,返回这个字节的值,当读到流结尾时返回 -1。
read(byte b[])
外面也是调用 read(byte b[], int off, int len)
来实现,从流的第 off 地位开始,读取 len 长度放入 b[]数组中。
close()
敞开流,同 OutputStream。
举例:
InputStream inputStream = null;
try {inputStream = new FileInputStream("hello.txt");
byte[] arr = new byte[1024];
inputStream.read(arr);
System.out.println(new String(arr));
} catch (Exception e) {e.printStackTrace();
} finally {inputStream.close();
}
首先构建一个须要读取的文件的输出流,而后通过 read(byte b[])
办法将流中的数据读取到字节数组中,这样就简略实现了一个读取文件的性能。但这里有一个前提,文件中的字节长度不超过 1024,那如果超过这个长度,咱们怎么样能力将流中数据读取到一个字节数组中呢?
能够借助 ByteArrayOutputStream。通过名字能够看出是将流中数据输入到字节数组中。
InputStream inputStream = null;
try {inputStream = new FileInputStream("hello.txt");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] arr = new byte[4];
int len;
while ((len = inputStream.read(arr)) > -1) {byteArrayOutputStream.write(arr, 0, len);
}
String data = byteArrayOutputStream.toString();
System.out.println(data);
} catch (Exception e) {e.printStackTrace();
} finally {inputStream.close();
}
ByteArrayOutputStream 的输入指标是 byte 数组,这个数组的长度是依据数据内容动静扩大的,当调用 write 办法的过程中,如果数组大小不够,会进行扩大,每次扩大长度翻倍。
BufferedInputStream 和 BufferedOutputStream
FileInputStream 和 FileOutputStream 是没有缓冲的,按单个字节读写时性能比拟低,尽管能够按字节数组读取来进步性能,但有时必须按字节读写,这时就须要 将文件流包装到缓冲流中来进步性能。
应用办法只有在对应的流外层包上对应的缓冲类就行了,如下:
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("hello.txt"));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("hello.txt"));
文本文件和字符流
下面介绍了如何以字节流的形式解决文件,对于文本文件,字节流没有编码的概念,不能按行解决,应用不太不便,更适宜用字符流来解决。
字符流和字节流相似,也是有两个基类,Reader 和 Writer。Reader 和 InputStream 相似,Writer 和 OutputStream 相似,具体的操作都是通过子类来实现。
InputStreamReader 和 OutputStreamWriter
InputStreamReader 能够将 InputStream 转换为 Reader,构造方法如下:
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName)
一个重要的参数是编码类型,对于字符流来说,编码是很重要的,雷同二进制内容,不同编码状况下,可能会呈现不同后果。通过传入输出流和编码类型来结构出字符输出流,如果不传编码类型,则为零碎默认编码,默认编码能够通过 Charset.defaultCharset()失去。
应用举例
Reader reader = null;
try {reader = new InputStreamReader(new FileInputStream("hello.txt"));
char[] chars = new char[1024];
reader.read(chars);
System.out.println(new String(chars));
}finally {reader.close();
}
将文件中数据读取到内存中,这段代码一次 reader 就读取了所有的内容,前提是文件内容不超过数组长度,如果文件内容很长,能够应用 CharArrayWriter 或 StringWriter,前面会介绍。
OutputStreamWriter 能够将 OutputStream 转换为 Writer,构造方法如下:
public OutputStreamWriter(OutputStream out)
public OutputStreamWriter(OutputStream out, String charsetName)
构造方法和 InputStreamReader 相似
应用举例
Writer writer = null;
try {writer = new OutputStreamWriter(new FileOutputStream("hello.txt"),"UTF-8");
writer.write("hello pipi 蛋");
}finally {writer.close();
}
InputStreamReader 和 OutputStreamWriter 外部都有一个类型为 StreamDecoder 的解码器,能将字节依据编码转换为 char。
CharArrayReader 和 CharArrayWriter
CharArrayWriter 与 ByteArrayOutputStream 相似,它的输入指标是 char 数组,这个数组长度能够依据内容动静扩大。
下面通过字符流读取文件内容的代码,咱们能够优化一下,如下:
Reader reader = null;
try {reader = new InputStreamReader(new FileInputStream("hello.txt"));
CharArrayWriter arrayWriter = new CharArrayWriter();
char[] chars = new char[1024];
int len = 0;
while ((len = reader.read(chars))> -1) {arrayWriter.write(chars, 0, len);
}
System.out.println(arrayWriter.toString());
}finally {reader.close();
}
StringReader 和 StringWriter 与 CharArrayReader 和 CharArrayWriter 相似,只是输入指标是 StringBuffer,而且 String 和 StringBuffer 外部都是由 char 数组组成的,所以他们实质上是一样的。
BufferedReader 和 BufferedWriter
BufferedReader/BufferedWriter 与 BufferedInputStream/BufferedOutputStream 相似,提供缓冲性能,只须要在外层包装一层缓冲类就行了,而且能够按行读写。
按行写入举例:
BufferedWriter writer = null;
try {writer = new BufferedWriter(new FileWriter("hello.txt"));
writer.write("第一行");
writer.newLine();
writer.write("第二行");
writer.newLine();}finally {writer.close();
}
按行读取举例:
BufferedReader reader = null;
try {reader = new BufferedReader(new FileReader("hello.txt"));
String s = null;
while ((s = reader.readLine()) != null) {System.out.println(s);
}
}finally {reader.close();
}
PrintWriter
PrintWriter 有很多构造方法,能够承受文件路径名、文件对象、OutputStream、Writer 等。PrintWriter 是一个十分不便的类,能够间接指定文件名作为参数,能够指定编码类型,能够主动缓冲,能够字段将多种类型转换为字符串,在输入到文件时,能够优先选择该类
下面按行写入的代码,能够应用 PrintWriter 写为:
PrintWriter printWriter = null;
try {printWriter = new PrintWriter("hello.txt");
printWriter.println("第一行");
printWriter.println("第二行");
printWriter.println("第三行");
}finally {printWriter.close();
}
能够看出 printWriter 相对而言,不便很多。
封装办法
字节流
复制 InputSteam 到 OutputStream
public void copy(InputStream in, OutputStream out) throws Exception{byte[] arr = new byte[1024];
int len = 0;
while ((len = in.read(arr)) > -1) {out.write(arr, 0, len);
}
}
将文件内容读取到字节数组中
public byte[] file2Array(String fileName) throws Exception{
InputStream in = null;
try {in = new FileInputStream(fileName);
ByteArrayOutputStream out = new ByteArrayOutputStream();
copy(in,out);
return out.toByteArray();}finally {in.close();
}
}
再将字节数组写入到文件中
public void array2File(String fileName,byte[] bytes) throws Exception{
OutputStream out = null;
try {out = new FileOutputStream(fileName);
out.write(bytes);
}finally {out.close();
}
}
字符流
复制 Reader 到 Writer 中
public void copy(Reader reader, Writer writer) throws Exception{char[] chars = new char[1024];
int len = 0;
while ((len = reader.read(chars)) > -1) {writer.write(chars, 0, len);
}
}
将文件内容读入到一个字符串中
public String file2String(String fileName,String encoding) throws Exception {
BufferedReader reader = null;
try {reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), encoding));
StringWriter writer = new StringWriter();
copy(reader,writer);
return writer.toString();}finally {reader.close();
}
}
将字符串内容写入到文件中
public void string2File(String fileName,String encoding,String data) throws Exception{
Writer writer = null;
try {writer = new OutputStreamWriter(new FileOutputStream(fileName),encoding);
writer.write(data);
}finally {writer.close();
}
}
按行写入到文件中
public void writeLines(String fileName, String encoding, Collection lines) throws Exception{
PrintWriter writer = null;
try {writer = new PrintWriter(fileName,encoding);
for (Object line : lines) {writer.println(line);
}
}finally {writer.close();
}
}
按行读取文件中内容,返回内容汇合
public List<String> readLines(String fileName,String encoding)throws Exception {
BufferedReader reader = null;
try {reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),encoding));
List<String> list = new ArrayList<>();
String line = null;
while ((line = reader.readLine()) != null) {list.add(line);
}
return list;
}finally {reader.close();
}
}
总结
咱们平时开发的时候对文件或者网络内容应用流操作时,可能都是应用第三方类库,比方 Apache 有一个类库 Commons IO,外面提供了很多简略易用的办法,当然应用这些类库一点故障都没有,然而,如果有些时候不能应用第三方类库的时候,比方开发一些通用的 jar 包时,必定越少依赖越好,这时候就要相熟 jdk 中的一些罕用 io 操作了。