定义
JavaIO流,是一种计算机用语。主要是用于处理数据的传输。
背景
I/O 流众多,在学习I/O流时,流与流之间关系理不清楚,什么场景下该选择使用什么样的流?成为了难点,因此,文本根据笔者的学习情况,梳理了常用的几种流
流的分类
按方向划分:
按处理的数据单位划分:
按功能划分:
四大抽象类
| 类定义 | 说明 |
|---|
| InputStream | 字节输入流,特点:凡是继承它的子类,类命名均以 InputStream 结尾,都是字节输入流 |
| OutputStream | 字节输出流,特点:凡是继承它的子类,类命名均以 OutputStream 结尾,都是字节输出流 |
| Reader | 字符输入流,特点:凡是继承它的子类,类命名均以 Reader 结尾 ,都是字符输入流 |
| Writer | 字符输出流,特点:凡是继承它的子类,类命名均以 Writer 结尾,都是字符输出流 |
生活场景类比计算机输入输出流操作
抽水机抽水到水塔的A区:相当于输入流 InputStram
水塔对水进行消毒、过滤、净化后装入水塔B区:相对于对输入流里的数据写入到输出流
水塔将B区的净化水供给到居民家里:相当于将输出流进行输出
浅谈文件拷贝过程内存中的对象
常用流说明
字节流
FileInputStream 对文件操作的字节输入流
| 构造器 | 方法说明 |
|---|
| FileInputStream(File file) | 有参构造器,传入 File 对象 |
| FileInputStream(String name) | 有参构造器,传入文件的路径(含文件名) |
| 返回类型 | 方法 | 方法说明 |
|---|
| int | read() | 从该文件字节输入流中读取 下一个字节,并返回 该字节 对应的 整型数值,且游标自动指向下一个字节的位置 |
| int | read(byte[] b) | 从该文件字节输入流读取最多 b.length 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| int | read(byte[] b, int off, int len) | 从该文件字节输入流读取最多 len 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| void | close() | 关闭此文件字节输入流并释放与流相关联的任何系统资源 |
package com.wei.java.basic.io;
import java.io.FileInputStream;
public class FileInputStream_my {
public static void main(String[] args) {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
try (
FileInputStream fis = new FileInputStream(filePath);
) {
// .read() 读取一个字节,并返回
int byteInt = fis.read();
System.out.println(byteInt +" -> " + (char)byteInt);
// .read(bytes),每次只从流中读取2个字节,UTF-8编码,一个汉字占3个字节
byte[] bytes = new byte[2];
int len;
while ((len = fis.read(bytes)) != -1){
// new String() 强行将一个汉字的 2 个字节转为字符串,会乱码
System.out.print(new String(bytes));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
FileOutputStream 对文件操作的字节输出流
| 构造器 | 方法说明 |
|---|
| FileOutputStream(File file) | 有参构造器,传入 File 对象,创建文件输出流以写入到指定的 File对象表示的文件 |
| FileOutputStream(File file, boolean append) | 有参构造器,传入 File 对象,创建文件输出流以写入到指定的 File对象表示的文件,append参数控制覆盖或追加数据到文件,true-追加,false-覆盖,默认false |
| FileOutputStream(String name) | 有参构造器,传入文件的路径+文件名,将数据输出到指定的文件 |
| FileOutputStream(String name, boolean append) | 有参构造器,传入文件的路径+文件名,将数据输出到指定的文件,append参数控制覆盖或追加数据到文件,true-追加,false-覆盖,默认false |
| 返回类型 | 方法 | 方法说明 |
|---|
| void | write(int b) | 将一个字节 的 整型数字,写入该文件字节输出流 |
| void | write(byte[] b) | 将 b.length 个字节从 字节数组b 写入该文件字节输出流 |
| void | write(byte[] b, int off, int len) | 将 字节数组b中,从 偏移量off 开始的 len个字节 数据,写入到该文件字节输出流 |
| void | close() | 关闭此文件字节输出流并释放与流相关联的任何系统资源,不需要close(),数据也会输出到磁盘 |
package com.wei.java.basic.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileOutputStream_my {
public static void main(String[] args) {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
String outFilePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\out_01.txt";
try (
FileInputStream fis = new FileInputStream(filePath);
FileOutputStream fos = new FileOutputStream(outFilePath);
) {
// fos.write(bytes, 0, len); 不需要flush(),数据也写到磁盘了
byte[] bytes = new byte[2]; // 数组设2,只为调试
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ByteArrayInputStream 对字节数组操作的字节输入流
| 构造器 | 方法说明 |
|---|
| ByteArrayInputStream(byte[] buf) | 有参构造, 传入字节数组 |
| 返回类型 | 方法 | 方法说明 |
|---|
| int | read() | 从该字节数组输入流中读取 下一个字节,并返回 该字节 对应的 整型数值 |
| int | read(byte[] b) | 从该字节数组输入流读取最多 b.length 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| int | read(byte[] b, int off, int len) | 从该字节数组输入流读取最多 len 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| void | close() | 关闭 ByteArrayInputStream没有任何效果,该流中的方法仍可以在流关闭后调用,而不生成IOException |
package com.wei.java.basic.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class ByteArrayInputStream_my {
public static void main(String[] args) throws IOException {
byte[] bytes = "wddd".getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
int byteInt = bais.read();
System.out.println((char)byteInt);
// close() 不会起作用
bais.close();
int byteInt2 = bais.read();
System.out.println((char)byteInt2);
byte[] buff = new byte[2];
int read = bais.read(buff);
System.out.println(new String(buff));
}
}
ByteArrayOutputStream 对字节数组操作的字节输出流
| 构造器 | 方法说明 |
|---|
| ByteArrayOutputStream() | 无参构造器 |
| 返回类型 | 方法 | 方法说明 |
|---|
| void | write(int b) | 将一个字节 的 整型数字,写入该字节数组输出流 |
| void | write(byte[] b, int off, int len) | 将 字节数组b中,从 偏移量off 开始的 len个字节 数据,写入到该字节数组输出流 |
| byte[] | toByteArray() | 将字节数组输出流中的数据,转为新的字节数组,并返回该新的字节数组的完整数据 |
| String | toString(String charsetName) | 将字节数组输出流中的数据转换为字符串,并指定编码类型 |
| void | close() | 关闭 ByteArrayOutputStream 没有任何效果,该流中的方法可以在流关闭后调用,而不生成IOException |
package com.wei.java.basic.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayOutputStream_my {
public static void main(String[] args) throws IOException {
byte[] bytes = "qwertyuiopa我的观点".getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int byteInt = bais.read();
System.out.println((char) byteInt);
// close() 不会起作用
bais.close();
byte[] buff = new byte[2];
int len;
while ((len = bais.read(buff)) != -1) {
baos.write(buff, 0, len);
}
// close() 也是不起作用的
baos.close();
baos.flush();
// 转数组
byte[] resultBytes = baos.toByteArray();
System.out.println(new String(resultBytes));
// 转字符串
String resultStr = baos.toString("utf-8");
System.out.println(resultStr);
}
}
BufferedInputStream 带缓冲区的字节输入流
| 构造器 | 方法说明 |
|---|
| BufferedInputStream(InputStream in) | 有参构造器,传入 InputStream 对象 |
| 返回类型 | 方法 | 方法说明 |
|---|
| int | read() | 从该带缓冲区的字节输入流中读取 下一个字节,并返回 该字节 对应的 整型数值 |
| int | read(byte[] b) | 从该带缓冲区的字节输入流读取最多 b.length 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| int | read(byte[] b, int off, int len) | 从该带缓冲区的字节输入流读取最多 len 个字节的数据放入 字节数组b,并返回读取到的字节个数 |
| void | close() | 关闭此带缓冲区的字节输入流并释放与流相关联的任何系统资源 |
package com.wei.java.basic.io;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
public class BufferedInputStream_my {
public static void main(String[] args) {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
try (
FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis);
) {
// .read() 读取一个字节,并返回
int byteInt = bis.read();
System.out.println(byteInt +" -> " + (char)byteInt);
// fis 流已经传入 bis 流,并且已经开始读取 bis 流后,再从 fis 流里读取,是读取不到数据的
byte[] temp = new byte[3];
int fisReadByteInt = fis.read(temp); // 这里是读取不到数据的
System.out.println(fisReadByteInt);
// .read(bytes),每次只从流中读取2个字节,UTF-8编码,一个汉字占3个字节
byte[] bytes = new byte[2];
int len;
while ((len = bis.read(bytes)) != -1){
// new String() 强行将一个汉字的 2 个字节转为字符串,会乱码
System.out.print(new String(bytes));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BufferedOutputStream 带缓冲区的字节输出流
| 构造器 | 方法说明 |
|---|
| BufferedOutputStream(OutputStream out) | 有参构造器,传入 OutputStream 对象 |
| 返回类型 | 方法 | 方法说明 |
|---|
| void | write(int b) | 将一个字节 的 整型数字,写入该带缓冲区的字节输出流 |
| void | write(byte[] b) | 将 b.length 个字节从 字节数组b 写入该带缓冲区的字节输出流 |
| void | write(byte[] b, int off, int len) | 将 字节数组b中,从 偏移量off 开始的 len个字节 数据,写入到该带缓冲区的字节输出流 |
| void | close() | 关闭此带缓冲区的字节输出流并释放与流相关联的任何系统资源,只有 close() 后,数据才会真正写到磁盘 |
package com.wei.java.basic.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class BufferedOutputStream_my {
public static void main(String[] args) {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
String outFilePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\out_01.txt";
try (
FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(outFilePath);
) {
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] bytes = new byte[2];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
// 只有 执行了 flush(),才会输出到磁盘,close()内调用了 flush() 方法
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
ObjectInputStream 对对象操作的字节输入流、ObjectInputStream 对对象操作的字节输出流
- 构造方法
- 常用方法
用得少,主要用于序列化与反序列化
字符流
FileReader 对文件操作的字符输入流
| 构造器 | 方法说明 |
|---|
| FileReader(File file) | 有参构造,传入 File 对象 |
| FileReader(String fileName) | 有参构造,传入文件路径(含文件名) |
| 返回类型 | 方法 | 方法说明 |
|---|
| int | read() | 从该字符输入流中读取 下一个字符,并返回 该字符 对应的 整型数值 |
| int | read(char[] cbuf) | 从该字符输入流读取最多 cbuf.length 个字符的数据放入 字符数组cbuf,并返回读取到的字符个数 |
| int | read(char[] cbuf, int offset, int length) | 从该字符输入流读取最多 length 个字节的数据放入 字符数组cbuf,并返回读取到的字符个数 |
| void | close() | 关闭流并释放与之相关联的任何系统资源 |
package com.wei.java.basic.io;
import java.io.FileReader;
import java.io.IOException;
public class FileReader_my {
public static void main(String[] args) throws Exception {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
try (
FileReader fr = new FileReader(filePath);
) {
int charInt = fr.read();
System.out.println((char) charInt);
char[] bytes = new char[2];
int len;
while ((len = fr.read(bytes)) != -1) {
System.out.print(new String(bytes));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileWriter 对文件操作的字符输出流
| 构造器 | 方法说明 |
|---|
| FileWriter(File file) | 有参构造,传入 File 对象,给一个File对象构造一个FileWriter对象。 |
| FileWriter(File file, boolean append) | |
| FileWriter(String fileName) | 有参构造,传入文件路径(含文件名) |
| FileWriter(String fileName, boolean append) | 有参构造,传入文件路径(含文件名),append 设置是否追加写入数据,true-追加,false-覆盖,默认false |
| 返回类型 | 方法 | 方法说明 |
|---|
| void | write(int c) | 写一个字符 |
| void | write(char[] cbuf, int off, int len) | 将 字符数组cbuf 中,从 偏移量off 开始的 len个字符 数据,写入到该文件字符输出流 |
| void | write(String str, int off, int len) | 将 字符串str 中,从 偏移量off 开始的 len个字符 数据,写入到该文件字符输出流 |
| void | flush() | 刷新流,需要刷新流,数据才会真正写入到文件 |
| void | close() | 内部调用刷新流方法,再关闭流,释放资源 |
package com.wei.java.basic.io;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriter_my {
public static void main(String[] args) throws IOException {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
String outFilePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\out_01.txt";
try (
FileReader fr = new FileReader(filePath);
){
FileWriter fw = new FileWriter(outFilePath);
// 写入单个字符
fw.write("w");
// 循环 读取 -> 写入
char[] chars = new char[2];
int len;
while ((len = fr.read(chars)) != -1){
fw.write(chars, 0, len);
}
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader 带缓冲区的字符输入流
| 构造器 | 方法说明 |
|---|
| BufferedReader(Reader in) | 有参构造,传入 Reader 对象 |
| 返回类型 | 方法 | 方法说明 |
|---|
| int | read() | 从该带缓冲区的字符输入流中读取 下一个字节,并返回 该字节 对应的 整型数值 |
| int | read(char[] cbuf) | 从该带缓冲区的字符输入流读取最多 cbuf.length 个字符的数据放入 字符数组cbuf,并返回读取到的字符个数 |
| int | read(char[] cbuf, int offset, int length) | 从该带缓冲区的字符输入流读取最多 length 个字节的数据放入 字符数组cbuf,并返回读取到的字符个数 |
| String | readLine() | 读取一行字符串,并返回该行字符串 |
| void | close() | 关闭流并释放与之相关联的任何系统资源 |
package com.wei.java.basic.io;
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferedReader_my {
public static void main(String[] args) throws Exception {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
FileReader fr = new FileReader(filePath);
BufferedReader br = new BufferedReader(fr);
// 读取单个字符
int charInt = br.read();
System.out.println((char) charInt);
// 读取一行字符串
String line = br.readLine();
System.out.println(line);
// 按行,循环读取
String src;
while ((src = br.readLine()) != null) {
System.out.println(src);
}
// 只需要关闭缓冲流,缓冲流内部会把它包装的流都关掉
br.close();
}
}
BufferedWriter 带缓冲区的字符输出流
| 构造器 | 方法说明 |
|---|
| BufferedWriter(Writer out) | 有参构造,传入 Writer 对象 |
| 返回类型 | 方法 | 方法说明 |
|---|
| void | write(int c) | 写一个字符 |
| void | write(char[] cbuf, int off, int len) | 将 字符数组cbuf 中,从 偏移量off 开始的 len个字符 数据,写入到该带缓冲区的字符输出流 |
| void | write(String str, int off, int len) | 将 字符串str 中,从 偏移量off 开始的 len个字符 数据,写入到该带缓冲区的字符输出流 |
| void | newLine() | 写入一换行符 |
| void | flush() | 刷新流,需要刷新流,数据才会真正写入到文件 |
| void | close() | 内部调用刷新流方法,再关闭流,释放资源 |
package com.wei.java.basic.io;
import java.io.*;
public class BufferedWriter_my {
public static void main(String[] args) throws Exception {
String filePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\text_01.txt";
String outFilePath = "D:\IdeaProjects\my-study\java-basic\java-basic-begin\src\main\resources\io\out_01.txt";
try (
// 在 try() 括号里创建的流,方法运行结束,会自动调用 close() 方法,
// 因为JDK8 及以上版本 编译后代码会在 finally 块里加上 close() 方法
FileReader fr = new FileReader(filePath);
BufferedReader bf = new BufferedReader(fr);
FileWriter fw = new FileWriter(outFilePath);
) {
BufferedWriter bw = new BufferedWriter(fw);
// 方式1:
//String line;
//while ((line = bf.readLine()) != null) {
// bw.write(line, 0, line.length());
//}
//bw.close();
// 方式2:
char[] chars = new char[2];
int len;
while ((len = bf.read(chars)) != -1) {
bw.write(chars, 0, len);
}
// 只有关闭流,数据才会输出到磁盘
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
常用流的分类与选择
小结:
- 字符流只适用于操作纯文本文件
- 字节流可以操作任何文件