文章目录
- IO流 >> 读写数据的方案
- 1. 认识IO流
- 1)IO流的分类
- 2)IO流的体系
- 2. 文件字节输入流
- 2.1 创建文件字节流对象
- 2.2 读取文件
- 1)使用`read()`方法一个一个字节的读取
- 2)使用字节数组读取数据:`byte[]`
- 3)使用字节流读取中文,如何保证输出不乱码,怎么解决?
- 3. 文件字节输出流
- 3.1 创建输出流
- 3.2 使用write()写数据
- 4. 输入流和输出流用完后都要关掉流
- 5. 文件复制 -- 字节流
- 资源释放方案:
- 1)`try - catch - finally`
- 2)`try - with - resource`
- 6. 字符流FileReader - FileWriter
- 6.1 字符输入流FileReader
- 1)认识
- 2)FileReader读取步骤
- 6.2 字符输出流FileWriter
- 1)认识
- 2)write()方法写数据
- 7. 缓冲流
- 7.1 缓冲字节流
- 7.2 缓冲字符流
- 7.3 缓冲区vs不使用缓冲区
- 8. 其他流
- 8.1 字符输入转换流InputStreamReader:字节流->字符流
- 8.2 打印流:PrintStream 、PrintWriter
- 8.3 特殊数据流:DataInputStream/DataOutputStream -> 网络通信收发数据
- 9. IO框架
- 9.1 框架:
- 9.2 基本使用
IO流 >> 读写数据的方案
- 用于数据的读写(文件中的数据、网络中的数据…)
1. 认识IO流
I
:Input,输入流,负责把数据读到内存中去O
:Output,输出流,负责写数据
1)IO流的分类
2)IO流的体系
- 字节输入流
InputStream
(读字节数据) - 字节输出流
Output Stream
(写字节数据) - 字符输入流
Reader
(读字符数据) - 字符输出流
Writer
(写字符数据)
2. 文件字节输入流
💡以内存为基准,把磁盘文件中的数据以字节的形式读入到内存中去
2.1 创建文件字节流对象
File file = new File("F:/Users/Typora/input.txt");
InputStream ffis = null;
InputStream fis = null;
try {
// 方式一:
ffis = new FileInputStream(file);
// 方式二:
fis = new FileInputStream("F:/Users/Typora/input.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
🤔在try-catch中创建文件字节流对象,其变量为局部变量,外部不可用的 >>> 先在try-catch外创建空的字节流对象,然后在try-catch中复制
2.2 读取文件
1)使用read()
方法一个一个字节的读取
// 2、使用FileInputStream字节输入流管道中的read方法读取文件字节数组到内存中
// 定义一个变量记住每次读取的一个字节
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
⚠️一次只读取一个字节,对于纯英文合适,有多字节字符就不合适了
2)使用字节数组读取数据:byte[]
// 定义一个字节数组,用来装读取到的字节数组。
// 重新创建一个文件字节输入流管道与源文件接通:上面字节读取的已经读完了
fis = new FileInputStream("F:/Users/Typora/input.txt");
byte[] buffer = new byte[1024];
// 定义一个变量记录每次读取到的字节数组长度。
int len;
while((len = fis.read(buffer))!=-1){
System.out.println(new String(buffer,0,len));
// 把字节数组按照一定的编码格式,按照一定的格式,输出到控制台,默认UTF-8
}
💡当数组的容量很小时,可能因只读取了一个多字节字符的一部分,从而导致解码后出现乱码
3)使用字节流读取中文,如何保证输出不乱码,怎么解决?
-
小文件:一次读完全部字节
readAllBytes[]
-
如果文件过大,创建的字节数组也会过大,可能引起内存溢出
💡读取文本适合用字符流,字节流适合做数据的转移,比如文件赋值
3. 文件字节输出流
- 以字节为基准,把内存中的数据以字节的形式写出到文件中去
3.1 创建输出流
// 1、创建FileOutputStream对象,指定写入的目的地
OutputStream fos = null;
try {
fos = new FileOutputStream("F:/Users/Typora/output.txt"); // true表示追加内容,false表示覆盖内容
// fos = new FileOutputStream("F:/Users/Typora/output.txt" , true); // true表示追加内容,false表示覆盖内容
} catch (FileNotFoundException e) {
e.printStackTrace();
}
3.2 使用write()写数据
//2、使用FileOutputStream对象中的方法write,把数据写入到目的地中
fos.write('a');
fos.write(65);
fos.write('\n'); // 换行
// 写一个字符串
byte[] bytes = "我爱你中国666".getBytes(StandardCharsets.UTF_8); // getBytes()获取编码, 使用UTF-8编码
fos.write(bytes);
fos.write('\n'); // 换行
// 写字符串的部分
fos.write(bytes, 0, 3);
//刷新
fos.flush();
4. 输入流和输出流用完后都要关掉流
💡使用try-catch-finally
资源释放方案会自动关闭流,释放资源
fis.close();
fos.close();
5. 文件复制 – 字节流
public static void main(String[] args) {
// 目标:使用字节流完成文件的复制操作
// 源文件:F:/Users/Typora/input.txt
// 目标文件:F:/Users/Typora/output.txt(目标文件要带上文件名,无法自动生成文件名)
String srcPath = "F:/Users/Typora/input.txt";
String destPath = "F:/Users/Typora/output.txt";
copyFile(srcPath, destPath);
}
// 复制文件
public static void copyFile(String srcPath, String destPath) {
// 1、创建一个文件字节输入流管道与源文件接通,一个文件字节输出流管道与目标文件接通
try (
InputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
) {
// 2、读取一个字节数组,写入一个字节数组,循环读取,循环写入
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len); // 写入一个字节数组,从数组的哪个索引开始读,读几个字节
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("复制完成!");
}
资源释放方案:
1)try - catch - finally
try{
}catch(Exception e){
e.printStackTrace();
}finally{
}
finally
代码区:无论try中的程序是否正常执行,最后都会执行finally区
,除非JVM终止。
💡一般用于程序执行完成后进行资源的释放操作 >>> 但是有点臃肿,每一个流都要手写代码关闭
2)try - with - resource
try(定义资源1;定义资源2;....){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
💡资源使用完毕后,会自动调用其close() 方法,完成对资源的释放!
- try()中只能放置资源,否则报错
- 资源一般是指最终实现了AutoCloseable接口
6. 字符流FileReader - FileWriter
6.1 字符输入流FileReader
1)认识
- 以内存为基准,把文件中的数据以
字符的形式
读入到内存中
2)FileReader读取步骤
① 创建字符输入流 char[]
② 通过read方法获取文件内容
public static void main(String[] args) {
// 目标:掌握文件字符输入流读取字符内容到程序中来
// 1、创建文件字符输入流管道与源文件接通
try (
Reader fr = new FileReader("F:/Users/Typora/input.txt");
) {
// 2、定义一个字符数组,用于装读取到的字符内容
char[] buffer = new char[3];
int len; // 记录每次读取到的字符数组长度
while ((len = fr.read(buffer)) != -1) {
// 3、把字符数组中的内容按照一定的编码格式,按照一定的格式,输出到控制台
String str = new String(buffer, 0, len);
System.out.print(str);
}
// 扩展:文件字符输入流每次读取多个字符,减少系统调用,性能较好,而且读取中文是按照字符读取,不会出现乱码! ---- 一种读取中文很好的方案
} catch (Exception e) {
e.printStackTrace();
}
6.2 字符输出流FileWriter
1)认识
- 以内存为基准,把内存中的数据以
字符的形式
写出到文件中去
2)write()方法写数据
public static void main(String[] args) {
// 目标:搞清楚文件字符输出流的使用
try(
// 1、创建FileWriter对象,指定写入的目的地
FileWriter fw = new FileWriter("F:/Users/Typora/output.txt");
){
// 2、写一个字符出去
fw.write(65); // 65是ASCII码,A
fw.write("\r\n");
// 3、写一个字符串出去
fw.write("java");
fw.write("\r\n");
// 写字符串的一部分
fw.write("Hello", 1, 2);
fw.write("\r\n");
// 4、写一个字符数组出去
char[] chars = {'a', 'b', 'c'};
fw.write(chars);
fw.write("\r\n");
// 5、写一个字符数组的一部分出去
fw.write(chars, 1, 2);
fw.write("\r\n");
// fw.flush(); // 刷新缓冲区,将数据写入到目的地中
// 刷新后,流可以继续使用
fw.close(); // 关闭流,关闭包含了刷新!关闭后流不再可用!
}catch (Exception e){
e.printStackTrace();
}
}
⚠️ 刷新数据:fw.flush()
7. 缓冲流
7.1 缓冲字节流
💡提高字节输入流读取数据的性能
- 文件复制
public static void main(String[] args) {
// 目标:了解缓冲字节流的使用
String srcPath = "F:/Users/Typora/input.txt";
String destPath = "F:/Users/Typora/output.txt";
copyFile(srcPath, destPath);
}
public static void copyFile(String srcPath, String destPath) {
//1、创建一个文件字节输入流管道与源文件接通,一个文件字节输出流管道与目标文件接通
try (
// 这里只能放置资源对象,用完后,最终会自动调用close()方法关闭资源
InputStream fis = new FileInputStream(srcPath);
// 把低级的字节输入流转换成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(fis); // 多态
FileOutputStream fos = new FileOutputStream(destPath);
// 把低级的字节输出流转换成高级的缓冲字节输出流
OutputStream bos = new BufferedOutputStream(fos);
) {
// 2、读取一个字节数组,写入一个字节数组,循环读取,循环写入
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len); // 写入一个字节数组,从数组的哪个索引开始读,读几个字节
}
System.out.println("复制完成!");
}catch(Exception e){
e.printStackTrace();
}
}
7.2 缓冲字符流
public static void main(String[] args) {
// 目标:缓冲字符输入流/缓冲字符输出流
try(
Reader fr = new FileReader("F:/Users/Typora/input.txt");
Writer fw = new FileWriter("F:/Users/Typora/output.txt");
// readLine是BufferedReader新增的,不用Reader多态创建
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
){
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
bw.write(line); // 写入
bw.newLine(); // 换行
}
}catch (Exception e){
e.printStackTrace();
}
}
7.3 缓冲区vs不使用缓冲区
缓冲区的 read() 方法:
- 先获取一整块数据到缓冲池,然后在从缓冲池里一个一个字节获取,或者一个一个字节数组获取
不使用缓冲区:
- 每次都是从磁盘中获取
8. 其他流
8.1 字符输入转换流InputStreamReader:字节流->字符流
- 解决不同编码时,字符流读取文本内容乱码的问题
解决思路:
① 获取文件的原始字节流
② 再将其按真实的字符集编码转成字符输入流
③ 这样字符输入流中的字符就不乱码了
public static void main(String[] args) {
// 目标:认识字符输入转换流InputStreamReader的用法
try(
// 先提取文件的原始字节流
InputStream is = new FileInputStream("F:\\Study\\Java\\javaseprojectmax\\day03-file-io\\src\\com\\study\\intput");
// 再把字节输入流转换为字符输入流
Reader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
// 再把字符输入流转换为字符缓冲输入流
BufferedReader br = new BufferedReader(isr);
){
String line;
while((line = br.readLine())!= null){
System.out.println(line);
}
}catch(Exception e){
}
}
8.2 打印流:PrintStream 、PrintWriter
- 可以实现更方便、更高效的打印数据出去 >>> 写数据
使用字节输出流/字符输出流时,会将数字字符转成对应ASCII码的英文字符
8.3 特殊数据流:DataInputStream/DataOutputStream -> 网络通信收发数据
DataOutputStream:
- 允许把数据和其类型一并写出去
public static void main(String[] args) {
try(
DataOutputStream dos = new DataOutputStream(new FileOutputStream("f:/Users/Typora/output.txt"));
){
dos.writeBoolean(true);
dos.writeByte(127);
dos.writeChar('a');
dos.writeShort(32767);
dos.writeInt(2147483647);
dos.writeDouble(3.1415926);
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try(
DataInputStream dis = new DataInputStream(new FileInputStream("f:/Users/Typora/output.txt"));
){
System.out.println(dis.readBoolean());
System.out.println(dis.readByte());
System.out.println(dis.readChar());
System.out.println(dis.readShort());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
}catch(Exception e){
e.printStackTrace();
}
}
💡DataOutputStream
对象使用write方法写数据时,也包含其类型,在文本上是乱码,在读取时通过DataInputStream
读取数据即可以还原数据
9. IO框架
9.1 框架:
- 一个预先写好的代码库或一组工具,旨在简化和加速开发过程
- 形式:
- 一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去
IO框架: 下载链接
- 封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等
9.2 基本使用
File srcFile = new File("F:/Users/Typora/input.txt");
File destFile = new File("F:/Users/Typora/copy.txt");
FileUtils.copyFile(srcFile, destFile); // 复制文件,如果没有会新建文件