当前位置: 首页 > backend >正文

Java-IO流

目录

1.IO流概述

 2.IO流-字节流

 2.1 字节读取方法

2.2 字节流每次读取多个字节

2.3 一次读取完全部字节

2.4 字节输出流

2.5  字节流复制文件

 2.6 资源释放的方式

2.7 复制文件夹案例

2.8 删除文件夹案例

3.IO流-字符流

3.1 FileReader(文件字符输入流)

3.2 FileWrite(文件字符输出流)

4.IO流-缓冲流

 4.1 BufferedInputStream(字节缓冲输入流)

4.2  BufferedReader(字符缓冲输入流)

4.4 BufferedWriter(字符缓冲输出流)

4.5 出师表案例

4.6 缓冲流,字节流性能分析

5.IO流-转换流

5.1 不同编码读取时会乱码的问题

5.2 字符输入转换流

5.3 字符输出转换流

6.IO流-打印流

7.IO流-数据流

 7.1 数据输出流

7.2 数据输入流

8.IO流-序列化流

8.1 对象序列化

8.2 对象反序列化

9.补充:IO框架


1.IO流概述

IO流的作用?

IO流用来读写文件数据

我们对于IO流的学习分两个步骤:

  1.  先搞清楚IO流的分类、体系
  2. 再挨个学习每个IO流的作用、用法

 2.IO流-字节流

 2.1 字节读取方法

package com.itheima.d2_byte_stream;import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;public class FileInputStreamDemo1 {public static void main(String[] args) throws Exception {// 目标:掌握文件字节输入流每次读取一个字节的形式。// 1、创建一个文件字节输入流管道(对象)与源文件接通。//会报一个文件找不到异常提示,所以要把异常抛出//InputStream is = new FileInputStream(new File("day09-charset-io\\src\\dlei01.txt"));//完整写法//简洁写法,不需要写new File创建对象,在源码中已经帮助我们创建InputStream is = new FileInputStream("D:\\IDEAProjects\\javasepromax\\day09-charset-io\\src\\dlei01.txt");// 2、public int read() 每次读取一个字节返回,如果没有字节可读了,返回-1
//        int b1 = is.read();
//        //返回字节数
//        System.out.println(b1);
//        //强转为char查看内容
//        System.out.println((char) b1);
//
//        int b2 = is.read();
//        System.out.println((char) b2);
//
//        int b3 = is.read();
//        System.out.println(b3);is.close();// 3、使用循环改进每次读取一个字节。InputStream is2 = new FileInputStream(new File("day09-charset-io\\src\\dlei02.txt"));int b; // 定义一个变量用于记住每次读取的字节。while ((b = is2.read()) != -1) {System.out.print((char) b);}// 拓展了:每次读取一个字节存在很多问题。// 1、无法避免读取汉字输出乱码的问题,会截断汉字的字节。// 2、每次读取一个字节,性能极差!is2.close();}
}

2.2 字节流每次读取多个字节

package com.itheima.d2_byte_stream;import java.io.FileInputStream;
import java.io.InputStream;public class FileInputStreamDemo2 {public static void main(String[] args) throws Exception {// 目标:掌握文件字节输入流每次读取多个字节。// 1、创建一个文件字节输入流管道与源文件接通// InputStream is = new FileInputStream(new File("day09-charset-io\\src\\dlei03.txt"));InputStream is = new FileInputStream("day09-charset-io\\src\\dlei03.txt"); // 简化写法// 2、每次读取一个字节数组的字节,会返回读取的字节个数,没有字节可读返回-1:public int read(byte[] buffer)byte[] buffer = new byte[3];int len = is.read(buffer);
//        String rs = new String(buffer);
//        System.out.println("读取的内容:" + rs);System.out.println("读取的内容" + new String(buffer));System.out.println("读取了几个字节:" + len);// 第一桶水: buffer = [a, b, c]// 第二桶水:  buffer = [3, m, c]int len2 = is.read(buffer);// 规范:读取多少就倒出多少。String rs2 = new String(buffer, 0, len2);System.out.println("读取了几个字节:" + len2);System.out.println("读取的内容:" + rs2);// 没有字节可读返回-1int len3 = is.read(buffer);System.out.println(len3);System.out.println("----------------------------------------------------------------");// 使用循环改进读取方案。InputStream is2 = new FileInputStream("day09-charset-io\\src\\dlei04.txt"); // 简化写法byte[] buffer2 = new byte[3];int len4; // 用于记录每次读取的字节个数while ((len4 = is2.read(buffer2)) != -1){String rs3 = new String(buffer2, 0, len4);System.out.print(rs3);}// 拓展:每次读取多个字节的优势和问题// 优势:性能得到提升。读取性能较好!// 缺点:读取汉字输出依然无法避免乱码!!//用于关闭输入流,释放与该流关联的系统资源,确保系统资源被正确回收is2.close();}
}

 对于这段代码,我们需要了解以下几个问题:
1.read(byte[] buffer)的工作机制

  • 缓冲区重用:buffer数组是重复使用的。每次调用read(buffer)时,新读取的字节会覆盖数组中的旧内容,但未被覆盖的部分会保留上次的数据
  • 返回值len:表示本次实际读取的字节数。当文件剩余内容不足数组大小时,len会小于数组长度。可以用作下面new String(buffer,0,len2)的一个参数

2. 为什么需要new String(buffer,0,len2)?

假设文件内容为"abc3m",分两次读取,上面代码中定义byte[ ]  buffer = new byte[3]; 每次读取三个字节的内容。

  • 第一次读取:读取3个字节[a,b,c],此时len2 = 3。
  • 第二次读取:仅剩余2个字节[3,m],此时len2 = 2。
  • 若直接转换为整个数组:new String(buffer)会将数组中的所有内容(包括上次残留的c)转换为字符串,得到“3mc”,导致结果错误。
  • 正确做法:new String(buffer,0,len2)仅转换有效部分[3,m],得到正确结果“3m”。

 3.len2表示需要转换的有效字节数,即实际从文件中读取的字节数量,避免将数组中的残留部分包含进来,这也就是为什么说:读取多少倒出多少

2.3 一次读取完全部字节

package com.itheima.d2_byte_stream;import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;public class FileInputStreamDemo3 {public static void main(String[] args) throws Exception {// 目标:学会如何使用文件字节输入流一次性读取完文本的全部字节,以此避免读取汉字乱码的问题。// 1、创建一个文件字节输入流管道与源文件接通InputStream is = new FileInputStream("day09-charset-io\\src\\dlei05.txt");//        // 2、定义一个字节数组与源文件一模一样大。File f = new File("day09-charset-io\\src\\dlei05.txt");
//        long size = f.length();
//        byte[] buffer = new byte[(int) size];
//        System.out.println("文件大小:" + size);//        // 3、一次性读取
//        int len = is.read(buffer);
//        System.out.println("读取的字节数:" + len);
//
//        String rs = new String(buffer);
//        System.out.println(rs);System.out.println("--------------------------------------------------");// Java官方提供了API;readAllBytes :一次性读取完文件的全部字节//readAllBytes()方法不适合读取大文件,小文件(几十MB以内)更加高效//大文件(几百MB以上):必须使用缓冲流分块读取,避免内存溢出byte[] buffer = is.readAllBytes();String rs = new String(buffer);System.out.println(rs);is.close();}
}

为什么这个语句 byte[] buffer = new byte[(int) size];为什么要用int来强制转换?

在Java中,数组的长度必须是int类型,而File.length()返回的是long类型。这是因为文件大小可能超过int的最大值(2^31-1,约 2GB),但数组索引必须在int范围内。

  • 类型不匹配:File.length()返回long类型,而数组初始化需要int。
  • Java语法限制:数组长度必须是int,即使文件大小不超过int范围也需要显式转换

2.4 字节输出流

package com.itheima.d2_byte_stream;import java.io.FileOutputStream;
import java.io.OutputStream;public class FileOutputStreamDemo4 {public static void main(String[] args) throws Exception {// 目标:文件字节输出流的使用。// 1、创建一个文件字节输出流管道与目标文件接通。// OutputStream os = new FileOutputStream("day09-charset-io/src/dlei06-out.txt"); // 覆盖管道OutputStream os = new FileOutputStream("day09-charset-io/src/dlei06-out.txt", true); // 追加管道// 2、开始写字节数据出去。// public void write(int a): 每次写出去一个字节 。os.write(97);os.write('a');// os.write('徐'); // 汉字是三个字节 [ooo] 只写出去第一个字节,会乱码!os.write("\r\n".getBytes()); // 换行符号// public void write(byte[] buffer): 每次写一个字节数组的数据出去。byte[] bytes = "abc我爱你中国".getBytes();os.write(bytes);os.write("\r\n".getBytes()); // 换行符号// public void write(byte[] buffer, int pos, int len): 写一个字节数组的一部分出去。// 参数一:字节数组// 参数二:写出去的第一个字节的索引// 参数三:总共写出去多少个字节os.write(bytes, 3, 9);os.write("\r\n".getBytes()); // 换行符号//IO流管道属于系统资源,会占用内存和相应的IO资源// 用完必须关闭,以释放占用的系统资源。// 为了节省系统资源,提升计算机性能。//os.flush();//刷新缓存中的数据到磁盘文件中去os.close();//关闭包含了刷新}
}

Java文件缓存机制

在Java IO操作中,文件缓存机制是一种通过内存临时存储数据,减少磁盘IO次数以提升性能的技术,当程序进行文件读写时,数据不会直接写入磁盘或从磁盘读取,而是先经过内存缓冲区过渡。

核心原理

写入数据:数据先写入内存缓冲区,当缓冲区满或手动刷新时,才批量写入磁盘。

读取数据:从磁盘读取一批数据到缓冲区,后续读取直接从缓冲区获取。

为什么需要缓存?

  • 磁盘IO速度(约100MB/s)远低于内存操作速度(约 25GB/s)
  • 减少磁盘寻道和数据传输的开销
  • 批量操作比单次操作效率更高

 关键方法与缓存控制

1.flush () 方法

  • 强制将缓冲区数据写入磁盘
  • 适用于需要立即看到写入效果的场景(如日志文件)

2. close () 方法

  • 先执行 flush (),再关闭流释放资源,仅刷新数据,流仍可继续使用
  • 必须调用,否则缓冲区数据可能丢失,刷新并释放资源,流不可再使用

2.5  字节流复制文件

package com.itheima.d2_byte_stream;import java.io.*;public class CopyTest5 {public static void main(String[] args) {try {// 目标:使用字节流完成文件的复制。// E:\resource\aaa.png  ===> D:\resource\meinv.png// 1、创建一个文件字节输入流管道与源文件接通InputStream is = new FileInputStream("D:\\resource\\aaa.png");// 2、创建一个文件字节输出流管道与目标文件接通。OutputStream os = new FileOutputStream("D:\\resource\\meinv.png");// 3、准备一个字节数组(一个桶)byte[] buffer = new byte[1024]; // 1KB// 4、开始转移数据  1024 + 1024 + 3int len; // 记录每次读取的字节个数。while ((len = is.read(buffer)) != -1) {// 5、把读取到的源文件字节,读取多少写出去到目标文件多少os.write(buffer, 0, len);}// 6、释放资源os.close();is.close();System.out.println("复制完成!");} catch (Exception e) {e.printStackTrace();}}
}

 2.6 资源释放的方式

package com.itheima.d3_finally;public class Test1 {public static void main(String[] args) {// 目标:认识finally的作用。//try后或者catch后,最终一定要跑一次,除非JVM挂了。try {int c = 10 / 2;System.out.println(c);// return;}catch (Exception e){e.printStackTrace();}finally {// 一定要执行一次,除非干掉JVM!System.out.println("=======finally=========");}System.out.println(add(110, 20));}public static int add(int a, int b){try {return a + b;} catch (Exception e) {e.printStackTrace();return 0;} finally {// finally中不要return数据,会覆盖前面的return数据// return 666;}}}
package com.itheima.d3_finally;import java.io.*;public class Test2 {public static void main(String[] args) {InputStream  is = null;OutputStream os = null;try {// System.out.println(10 / 0);// 目标:使用try-catch-finally释放资源// E:\resource\aaa.png  ===> D:\resource\meinv.png// 1、创建一个文件字节输入流管道与源文件接通is = new FileInputStream("E:\\resource\\aaa.png");// 2、创建一个文件字节输出流管道与目标文件接通。os = new FileOutputStream("D:\\resource\\meinv.png");// 3、准备一个字节数组(一个桶)byte[] buffer = new byte[1024]; // 1KB// 4、开始转移数据  1024 + 1024 + 3int len; // 记录每次读取的字节个数。while ((len = is.read(buffer)) != -1) {// 5、把读取到的源文件字节,读取多少写出去到目标文件多少os.write(buffer, 0, len);}System.out.println("复制完成!");} catch (Exception e) {e.printStackTrace();} finally {// 6、释放资源(最终一定会执行)try {if(os != null) os.close();} catch (Exception e) {e.printStackTrace();}try {if(is != null) is.close();} catch (IOException e) {e.printStackTrace();}}}
}

package com.itheima.d3_finally;import java.io.*;public class Test3 {public static void main(String[] args) {//目标:JDK 7 开始资源释放的新方式:try-with-resourcetry (// 特点:这里只能放资源,并且程序执行完毕后,自动调用他们的close方法关闭!// int age = 12;// 1、创建一个文件字节输入流管道与源文件接通InputStream is = new FileInputStream("E:\\resource\\aaa.png");// 2、创建一个文件字节输出流管道与目标文件接通。OutputStream os = new FileOutputStream("D:\\resource\\meinv.png");//造一个资源实现AutoCloseable接口,验证是否真的会自动调用close()方法MyConnection conn = new MyConnection("数据库1号链接");){// 目标:使用try-with-resource释放资源// 3、准备一个字节数组(一个桶)byte[] buffer = new byte[1024]; // 1KB// 4、开始转移数据  1024 + 1024 + 3int len; // 记录每次读取的字节个数。while ((len = is.read(buffer)) != -1) {// 5、把读取到的源文件字节,读取多少写出去到目标文件多少os.write(buffer, 0, len);}System.out.println("复制完成!");} catch (Exception e) {e.printStackTrace();}}
}//造一个资源
class MyConnection implements AutoCloseable{private String name;public MyConnection(String name) {this.name = name;}@Overridepublic void close() throws Exception {System.out.println(name + "被自动关闭了~~");}
}

2.7 复制文件夹案例

能够使用递归调用定义方法,复制文件夹。

package work.Test1;import java.io.*;public class test1 {public static void main(String[] args) throws IOException {//创建File对象srcDir,代表源文件夹File srcDir = new File("C:\\Users\\Lenovo\\Desktop\\aaa\\day09-字符集、IO流(一)");//创建File对象desDir,代表目标文件夹File desDir = new File("C:\\Users\\Lenovo\\Desktop\\aaa\\新建文件夹");//调用copyDir2Dir方法,传递源文件夹和目标文件夹,完成文件夹的复制copyDir2Dir(srcDir,desDir);System.out.println("文件夹复制完毕!!!");}//1.定义复制文件到文件夹的方法copyFile2Dirpublic static void copyFile2Dir(File file,File dir) throws IOException {//1.1 创建文件字节输入流FileInputStream类的对象,绑定文件资源FileInputStream is = new FileInputStream(file);//1.2创建输出流FileOutputStream类的对象,绑定目标文件FileOutputStream os = new FileOutputStream(dir);//1.3定义一个byte数组,保存每次读取到的字节的内容byte[] bytes = new byte[1024 * 10];//1.4定义int变量,保存每次读取到的字节数量int len;//1.5根据目标文件夹和源文件,创建目标文件,确保是在目标文件夹里创建和源文件同名的文件//创建一个新的File对象,表示目标文件的路径//dir这是一个File对象,表示目标文件夹。//file.getName()这是源文件的文件名,此方法会返回源文件的名称部分//这段代码的作用就是把目标文件夹和源文件的名称动态组合起来,形成完整的目标文件路径File destFile = new File(dir,file.getName());//1.6循环读取源文件并写入到目标文件while ((len = is.read(bytes)) != -1) {os.write(bytes,0,len);}//1.7 关闭流释放资源is.close();os.close();}//2.定义复制文件夹到文件夹的方法copyDir2Dirpublic static void copyDir2Dir(File srcDir, File desDir) throws IOException {//2.1 在目标文件夹中创建源文件夹File newDir = new File(desDir, srcDir.getName());if (!newDir.exists()) {newDir.mkdirs();}//2.2获取文件夹中的所有文件和文件夹对应的File对象数组File[] files = srcDir.listFiles();//2.3判断,如果File对象数组是null或者没有内容,结束方法if (files == null || files.length == 0) {return;}//2.4遍历File对象数组for (File file : files) {//2.5判断,如果当前File对象是文件,调用copyFile2Dir方法,完成文件夹复制if (file.isFile()) {copyFile2Dir(file,newDir);}else {//2.6如果当前File对象是文件夹,递归调用当前方法,完成文件夹的复制copyDir2Dir(file,newDir);}}}
}

与上面复制图片到文件夹对比:

当前案例为什么需要 new File(dir, file.getName())

  1. 目标路径不包含文件名
    你虽然指定了目标文件夹(如 D:\destination\),但文件夹路径本身不包含文件名。例如:

    • 源文件是 C:\source\test.txt

    • 目标文件夹是 D:\destination\

    • 最终需要生成的目标文件是 D:\destination\test.txt

  2. 动态生成文件名
    代码需要根据源文件的名称(test.txt),在目标文件夹中创建同名文件。因此必须通过 file.getName() 动态获取文件名,并与目标文件夹组合。

上面案例复制单个文件案例(直接指定完整路径包括文件名称  .png ):

为什么不需要动态生成文件名?

  • 完整路径已硬编码
    在创建 FileOutputStream 时,你已经直接指定了完整的目标路径(D:\resource\meinv.png),包括文件夹路径文件名。因此不需要额外的步骤来组合路径。

总结:

  • 当前案例 :目标路径是文件夹,需要动态补全文件名(否则系统不知道该把文件复制成什么名字)。

  • 上面案例:目标路径已经是完整的文件路径(含文件名),直接写入即可。

2.8 删除文件夹案例

package com.itheima.d8_demo;import java.io.File;public class DeleteDirDemo2 {public static void main(String[] args) {// 目标:删除文件夹。deleteDir(new File("D:/resource"));}public static void deleteDir(File dir){// 1、不删除的情况if(dir == null || !dir.exists()) return;// 2、如果是文件直接删除。if(dir.isFile()) {dir.delete();return;}File[] files = dir.listFiles();// 3、文件夹没有权限拿文件,直接不删除!if(files == null){return;}// 4、空文件夹 直接删除if(files.length == 0){// 空文件夹 直接删除// Java提供的File.delete()方法只能删除空文件夹dir.delete();return;}// 5、遍历全部一级文件对象,删除他们,再删除文件夹自己。for (File file : files) {if(file.isFile()){file.delete();}else {deleteDir(file);}}dir.delete(); // 删除自己!}
}

下面解释一下这段代码的删除逻辑:

边界条件检查:

  • 如果传入的目录为null或不存在,直接返回
  • 如果传入的是文件而非目录,直接删除并返回

在遍历目录中的子项时:

  • 遇到一个文件,直接调用File.delete()删除文件
  • 遇到一个空文件夹时,直接调用File.delete()删除空文件夹
  • 遇到非空文件夹时,调用自身方法进行递归
  • 递归删除子项后,文件夹变为空
  • 最后用File.delete()删除自身

3.IO流-字符流

字节流:适合复制文件等,不适合读写文本文件

字符流:适合读写文本文件内容

3.1 FileReader(文件字符输入流)

 文件字符输入流的好处是可以避免读汉字的时候乱码

package com.itheima.d1_char_stream;import java.io.FileReader;
import java.io.Reader;public class FileReaderDemo1 {public static void main(String[] args) throws Exception {// 目标:文件字符输入流的使用:每次读取一个字符。// 1、创建文件字符输入流管道与源文件接通Reader fr = new FileReader("day10-io-code\\src\\dlei01.txt");// 2、读取一个字符编号回来,没有字符可读返回-1: public int read()\
//        int c1 = fr.read();
//        System.out.println((char) c1);
//
//        int c2 = fr.read();
//        System.out.println((char) c2);
//
//        int c3 = fr.read();
//        System.out.println(c3);// 3、使用循环解决int c;while ((c = fr.read()) != -1){char ch = (char) c;System.out.print(ch);}fr.close();// 拓展://    是否解决了乱码问题:解决了!//    性能较差,依然是个垃圾代码!}
}

上面这段代码性能并不好,我们如何一次读多个字符呢?用字符数组来读取文本内容

package com.itheima.d1_char_stream;import java.io.FileReader;
import java.io.Reader;public class FileReaderDemo2 {public static void main(String[] args) {// 目标:文件字符输入流的使用:每次读取多个字符。try (// 1、创建文件字符输入流管道与源文件接通//注意:字符输入流读的就是字符,并不是字节Reader fr = new FileReader("day10-io-code\\src\\dlei01.txt");){// 2、定义一个字符数组用于读取多个字符char[] buffer = new char[1024];int len; // 记住每次读取了多少个字符while ((len = fr.read(buffer)) != -1){String rs = new String(buffer, 0, len);System.out.print(rs);}// 拓展://     可以避免乱码吗? 可以//     性能可以吗? 可以//     这是截至此刻我们学过的读取文本内容最好的方案。}catch (Exception e){e.printStackTrace();}}
}

3.2 FileWrite(文件字符输出流)

package com.itheima.d1_char_stream;import java.io.FileWriter;public class FileWriteDemo3 {public static void main(String[] args) {try (// 目标:掌握文件字符输出流的使用。// 1、创建一个文件字符输出流管道与目标文件接通// Writer fw = new FileWriter("day10-io-code/src/dlei02-out.txt"); // 覆盖FileWriter fw = new FileWriter("day10-io-code/src/dlei02-out.txt", true); // 追加) {// 2、写字符数据出去。// a.写一个字符出去 : public void write(int c)fw.write(97);fw.write('磊');fw.write("\r\n"); // 换行// b.写一个字符串出去:public void write(String s)fw.write("abc我爱你中国666");fw.write("\r\n"); // 换行// c、写字符串的一部分出去 : public void write(String s, int pos ,int len)fw.write("abc我爱你中国666",3, 3);fw.write("\r\n"); // 换行// d.写一个字符数组出去:public void write(char[] chars)//转换为字符数组用的是toCharArray(),而字节数组用的是getByte()char[] chars = "abc我爱你中国666".toCharArray();fw.write(chars);fw.write("\r\n"); // 换行// e、写一个字符数组的一部分出去:public void write(char[] chars, int pos ,int len)fw.write(chars, 6, 2);fw.write("\r\n"); // 换行//            fw.flush(); // 刷新
//            fw.close(); // 关闭包含刷新}catch (Exception e){e.printStackTrace();}}
}

在Java代码里使用\r\n进行换行,是和不同操作系统的文本文件换行符规范相关的,是为了不同平台之间的兼容性。

4.IO流-缓冲流

 缓冲流的作用是对原始流进行包装,以提高原始流读写数据的性能

 4.1 BufferedInputStream(字节缓冲输入流)

package com.itheima.d2_buffer_stream;import java.io.*;public class BufferedInputStreamDemo1 {public static void main(String[] args) {// 目标:使用字节缓冲流提升原始字节流读写数据的性能的。try (// 1、创建字节输入流管道与源文件接通InputStream is = new FileInputStream("E:\\resource\\meinv2.jpg");// 使用高级的缓冲字节输入流包装低级的字节输入流InputStream bis = new BufferedInputStream(is);//多态// 2、创建一个字节输出流管道与目标文件接通OutputStream os = new FileOutputStream("E:\\resource\\meinv2-bak2.txt");// 使用高级的缓存字节输出流包装低级的字节输出流OutputStream bos = new BufferedOutputStream(os);//多态) {// 3、准备一个字节数组byte[] buffer = new byte[1024];// 4、转移数据int len;while ((len = bis.read(buffer)) != -1) {bos.write(buffer, 0, len);}} catch (Exception e) {e.printStackTrace();}}
}

4.2  BufferedReader(字符缓冲输入流)

4.4 BufferedWriter(字符缓冲输出流)

package com.itheima.d2_buffer_stream;import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;public class BufferedWriterDemo3 {public static void main(String[] args) {try (// 目标:掌握缓冲字符输出流的使用。// 1、创建一个文件字符输出流管道与目标文件接通Writer fw = new FileWriter("day10-io-code/src/dlei05-out.txt"); // 覆盖BufferedWriter bw = new BufferedWriter(fw);) {// 2、写字符数据出去。// a.写一个字符出去 : public void write(int c)bw.write(97);bw.write('磊');bw.newLine(); // 换行,与bw.write("\r\n")作用相同// b.写一个字符串出去:public void write(String s)bw.write("abc我爱你中国666");bw.newLine(); // 换行// c、写字符串的一部分出去 : public void write(String s, int pos ,int len)bw.write("abc我爱你中国666",3, 3);bw.newLine(); // 换行// d.写一个字符数组出去:public void write(char[] chars)char[] chars = "abc我爱你中国666".toCharArray();bw.write(chars);bw.newLine(); // 换行// e、写一个字符数组的一部分出去:public void write(char[] chars, int pos ,int len)bw.write(chars, 6, 2);bw.newLine(); // 换行}catch (Exception e){e.printStackTrace();}}
}

4.5 出师表案例

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

package com.itheima.d2_buffer_stream;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class Test4 {public static void main(String[] args) {// 目标:完成出师表案例。try (// 2、创建缓冲字符输入流与源文件接通BufferedReader br = new BufferedReader(new FileReader("day10-io-code\\src\\csb.txt"));// 6、创建缓冲字符输出流与目标文件接通BufferedWriter bw = new BufferedWriter(new FileWriter("day10-io-code\\src\\csb-new.txt"));){// 1、准备一个集合,存储原文每段落。List<String> data = new ArrayList<>();// 3、开始按照行读取,并存入到集合中去。String line;while ((line = br.readLine()) != null){data.add(line);}System.out.println(data);// 4、对每段按照首字符编号进行排序//排序是默认按照首字符的编号进行排序的Collections.sort(data);System.out.println(data);// 5、把排序好的集合中的每段文章写出到新文件中去,每行都要换行。for (String ln : data) {bw.write(ln);bw.newLine();}System.out.println("完成了!");} catch (Exception e) {e.printStackTrace();}}
}

 这段代码执行完成之后会把排序好的出师表输出到一个名称为csb-new.txt文件当中去。

4.6 缓冲流,字节流性能分析

package com.itheima.d2_buffer_stream;import java.io.*;public class TimeTest5 {public static final String SRC_VIDEO = "D:\\【黑马】2024年11月AI版Java全栈开发V15课程\\02阶段:java基础进阶\\day02-面向对象进阶(二)\\视频\\11、接口的综合案例.mp4";public static final String DEST_PATH = "C:\\Users\\Lenovo\\Desktop\\aaa\\";public static void main(String[] args) {// 目标:原始流和缓冲流的性能分析。// copy01(); // 使用低级的字节流按照一个一个字节的形式复制文件: 简直慢的让人无法忍受,直接淘汰,禁止使用!copy02(); // 使用低级的字节流按照字节数组的形式复制文件:还可以,但是相对较慢!把桶加大性能就变好了// 性能不一定就慢,把桶加大性能就变好了,但是桶加大到一定程度后影响就微乎其微了。适中就好!// copy03(); // 使用高级的缓冲字节流按照一个一个字节的形式复制文件:还是特别慢,不推荐使用!copy04(); // 使用高级的缓冲字节流按照字节数组的形式复制文件:极快!推荐使用!}// 4、使用高级的缓冲字节流按照字节数组的形式复制文件。public static void copy04(){long start = System.currentTimeMillis();try (// 1、创建字节输入流管道与源文件接通InputStream is = new FileInputStream(SRC_VIDEO);InputStream bis = new BufferedInputStream(is);// 2、创建一个字节输出流管道与目标文件接通OutputStream os = new FileOutputStream(DEST_PATH + "04.avi");OutputStream bos = new BufferedOutputStream(os);) {// 3、转移数据byte[] buffer = new byte[32*1024]; // 1KBint len;while ((len = bis.read(buffer)) != -1){bos.write(buffer,0,len);}} catch (Exception e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用高级的缓冲字节流按照字节数组的形式复制文件:" +  (end - start) / 1000.0 + "s");}// 3、使用高级的缓冲字节流按照一个一个字节的形式复制文件。public static void copy03(){long start = System.currentTimeMillis();try (// 1、创建字节输入流管道与源文件接通InputStream is = new FileInputStream(SRC_VIDEO);InputStream bis = new BufferedInputStream(is);// 2、创建一个字节输出流管道与目标文件接通OutputStream os = new FileOutputStream(DEST_PATH + "03.avi");OutputStream bos = new BufferedOutputStream(os);) {// 3、转移数据int b;while ((b = bis.read()) != -1){bos.write(b);}} catch (Exception e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用高级的缓冲字节流按照一个一个字节的形式复制文件:" +  (end - start) / 1000.0 + "s");}// 2、使用低级的字节流按照字节数组的形式复制文件。public static void copy02(){long start = System.currentTimeMillis();try (// 1、创建字节输入流管道与源文件接通InputStream is = new FileInputStream(SRC_VIDEO);// 2、创建一个字节输出流管道与目标文件接通OutputStream os = new FileOutputStream(DEST_PATH + "02.avi");) {// 3、转移数据byte[] buffer = new byte[32*1024];int len;while ((len = is.read(buffer)) != -1){os.write(buffer,0,len);}} catch (Exception e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用低级的字节流按照字节数组的形式复制文件:" +  (end - start) / 1000.0 + "s");}// 1、使用低级的字节流按照一个一个字节的形式复制文件。public static void copy01(){long start = System.currentTimeMillis();try (// 1、创建字节输入流管道与源文件接通InputStream is = new FileInputStream(SRC_VIDEO);// 2、创建一个字节输出流管道与目标文件接通OutputStream os = new FileOutputStream(DEST_PATH + "01.avi");) {// 3、转移数据int b;while ((b = is.read()) != -1){os.write(b);}} catch (Exception e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("使用低级的字节流按照一个一个字节的形式复制文件耗时:" +  (end - start) / 1000.0 + "s");}
}

5.IO流-转换流

5.1 不同编码读取时会乱码的问题

 如果用代码编码UTF-8读取文本文件中GBK的编码文件就会出现乱码的问题。

package com.itheima.d3_transfer_stream;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Reader;public class Test1 {public static void main(String[] args) {// 目标:不同编码下,字符流读取文本内容的问题。try (// 代码编码:UTF-8   文件编码:UTF-8   a我m ==> o [ooo] o  不乱码// 代码编码:UTF-8   文件编码:GBK     a我m ==> o [oo] o   乱码// 1、创建一个文件字符输入流与源文件接通。Reader fr = new FileReader("E:\\resource\\abc.txt");// 把原始的字符输入流包装成高级的缓冲字符输入流BufferedReader br = new BufferedReader(fr);){String line; // 记住每次读取的一行数据。while ((line = br.readLine()) != null){System.out.println(line);}}catch (Exception e){e.printStackTrace();}}
}

5.2 字符输入转换流

package com.itheima.d3_transfer_stream;import java.io.*;public class InputStreamReaderDemo2 {public static void main(String[] args) {// 目标:字符输入转换流。try (// 1、得到GBK文件的原始字节输入流(GBK文件中文有两个字节)InputStream is = new FileInputStream("E:\\resource\\abc.txt");// 2、通过字符输入转换流把原始字节流按照指定编码转换成字符输入流Reader isr = new InputStreamReader(is, "GBK");// 3、把字符输入流包装成高级的缓冲字符输入流。BufferedReader br = new BufferedReader(isr);){// 4、按照行读取String line;while ((line = br.readLine()) != null) {System.out.println(line);}}catch (Exception e){e.printStackTrace();}}}

5.3 字符输出转换流

package com.itheima.d3_transfer_stream;import java.io.*;public class OutputStreamWriterDemo3 {public static void main(String[] args) {try (// 目标:掌握字符输出转换流:可以指定写出去的字符编码。// 1、创建一个文件字节输出流通向目标文件。OutputStream os = new FileOutputStream("day10-io-code/src/dlei06-out.txt");// 指定写出去的编码是GBKWriter osw = new OutputStreamWriter(os, "GBK");// 2、把字符输出流包装成缓冲输出流BufferedWriter bw = new BufferedWriter(osw);){bw.write("莫道桑榆晚");bw.newLine();bw.write("为霞尚满天");bw.newLine();bw.write("abc666");bw.newLine();} catch (Exception e) {e.printStackTrace();}}
}

实际开发中,可以通过字节转换的方式实现GBK编码转换,可以直接操作字节数组:
核心代码:

String text = "字节编码测试";
byte[] gbkBytes = text.getBytes("GBK"); //字符串->GBK字节数组
String text = new String(gbkBytes,"GBK");//GBK字节数组->字符串
fos.write(gbkBytes);//直接写入字节数组

6.IO流-打印流

package com.itheima.d4_printstream;import java.io.PrintStream;public class PrintStreamDemo1 {public static void main(String[] args) {try (// 目标:打印流:方便,高效的写数据出去。PrintStream ps = new PrintStream("day10-io-code/src/ps.txt");//只能覆盖// PrintWriter ps = new PrintWriter("day10-io-code/src/ps.txt");//要追加的话就要包原来的低级管道
//                PrintWriter ps = new PrintWriter(new FileWriter("day10-io-code/src/ps.txt", true));){ps.println(666);ps.println(97);ps.println(97.9);ps.println('a');ps.println(true);ps.println("我爱学Java");} catch (Exception e) {e.printStackTrace();}}
}

 打印流的一种应用:输出语句的重定向,把输出在控制台的语句修改到文档中输出,用System.setOut( )

package com.itheima.d4_printstream;import java.io.FileOutputStream;
import java.io.PrintStream;public class PrintStreamDemo2 {public static void main(String[] args) throws Exception {// 目标:输出语句的重定向。System.out.println("红豆生南国");System.out.println("春来发几枝");PrintStream ps = new PrintStream(new FileOutputStream("day10-io-code/src/ps2.txt", true));System.setOut(ps); // 把系统类的打印流改成我的打印流!!System.out.println("愿君多采撷");System.out.println("此物最相思");}
}

7.IO流-数据流

 7.1 数据输出流

package com.itheima.d5_dataOutputStream;import java.io.DataOutputStream;
import java.io.FileOutputStream;public class DataOutputStreamDemo1 {public static void main(String[] args) {try (// 目标:掌握数据输出流写数据出去的特点:可以写数据和其类型,而且后面可以读取出来。DataOutputStream dos = new DataOutputStream(new FileOutputStream("day10-io-code/src/data.txt"));){// 2、写数据和其类型出去dos.writeByte(97);dos.writeBoolean(true);dos.writeInt(1000);dos.writeChar('a');dos.writeUTF("我在北京天安门666");} catch (Exception e) {e.printStackTrace();}}
}

7.2 数据输入流

package com.itheima.d5_dataOutputStream;import java.io.DataInputStream;
import java.io.FileInputStream;public class DataInputStreamDemo2 {public static void main(String[] args) {// 目标:掌握特殊数据输入流读取特殊数据输出流写出去的数据。try (// 1、创建高级的特殊数据输入流管道包装低级的字节输入流管道。DataInputStream dis = new DataInputStream(new FileInputStream("day10-io-code/src/data.txt"));){// 2、开始读取byte b = dis.readByte();System.out.println(b);boolean b1 = dis.readBoolean();System.out.println(b1);int i = dis.readInt();System.out.println(i);char c = dis.readChar();System.out.println(c);String s = dis.readUTF();System.out.println(s);} catch (Exception e) {e.printStackTrace();}}
}

8.IO流-序列化流

8.1 对象序列化

对象序列化:把Java对象写入到文件中去

如果有一些对象不希望被序序列化,例如Student中的passWord,那么我们可以用transient。transient修饰的成员变量将不参与序列化。

package com.itheima.d6_object;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@NoArgsConstructor
@AllArgsConstructor
// 注意:如果学生对象要参与序列化,必须实现序列化接口
public class Student implements Serializable {private String name;private int age;// transient 修饰的成员变量将不参与序列化private transient String passWord;private double height;
}
package com.itheima.d6_object;import java.io.FileOutputStream;
import java.io.ObjectOutputStream;public class ObjectOutputStreamDemo1 {public static void main(String[] args) throws Exception {// 目标:完成对象的序列化:把Java对象存储到文件中去// 1、准备一个对象。Student s = new Student("风驴子", 27, "mazi666", 160);// 2、创建对象字节输出流管道与目标文件接通ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("day10-io-code/src/obj.txt"));// 3、开始写对象出去oos.writeObject(s);// 4、关闭资源oos.close();}
}

 序列化之后的文件中不是乱码,是对象序列化特殊的存储方式

8.2 对象反序列化

对象反序列化:把文件里的Java对象读出来

package com.itheima.d6_object;import java.io.FileInputStream;
import java.io.ObjectInputStream;public class ObjectInputStreamDemo2 {public static void main(String[] args) throws Exception {// 目标:对象反序列化 使用对象字节输入流管道把文件中的Java对象又读入到内存中来。// 1、创建对象字节输入流管道ObjectInputStream ois =new ObjectInputStream(new FileInputStream("day10-io-code\\src\\obj.txt"));// 2、读取对象进来(对象反序列化)// 实际上Java读取的对象都是Object类型的,因为Java无法判断文件中的具体类型// 但是我们可以将对象强转为实际类型Student s = (Student) ois.readObject();System.out.println(s);ois.close();}
}

如何如果要一次序列化多个对象?

用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可

注意:ArrayList集合已经实现了序列化接口!

9.补充:IO框架

package com.itheima.d7_commons_io;import java.nio.file.Files;
import java.nio.file.Path;/*** 目标:使用CommonsIO框架进行IO相关的操作。*/
public class CommonsIOTest1 {public static void main(String[] args) throws Exception {
//           FileUtils.copyFile(new File("day10-io-code/src/dlei04.txt"), new File("day10-io-code/src/dlei04-new.txt"));
//           FileUtils.copyDirectory(new File("E:\\resource"), new File("E:\\resource-dlei-bak"));
//           FileUtils.deleteDirectory(new File("E:\\resource-dlei-bak"));// JDK 7开始也新增了单行复制相关的计数。Files.copy(Path.of("day10-io-code/src/dlei04.txt"), Path.of("day10-io-code/src/dlei04-dlei-666.txt"));//Files.delete(Path.of("day10-io-code/src/dlei04-new.txt"));}
}

http://www.xdnf.cn/news/15443.html

相关文章:

  • Redis单线程详解
  • 多线程--单例模式and工厂模式
  • 2025-7-14-C++ 学习 排序(2)
  • C#——数据与变量
  • 力扣454.四数相加Ⅱ
  • ELK、Loki、Kafka 三种日志告警联动方案全解析(附实战 Demo)
  • AI香烟检测实战:YOLO11模型训练全过程解析
  • 【EM算法】三硬币模型
  • 机器学习/深度学习训练day1
  • 人工智能正逐步商品化,而“理解力”才是开发者的真正超能力
  • 【第一章编辑器开发基础第二节编辑器布局_1水平与垂直布局(1/4)】
  • Sharding-Sphere学习专题(二)案例和分片策略
  • 动态规划题解——乘积最大子数组【LeetCode】
  • 暑期自学嵌入式——Day02(C语言阶段)
  • 【论文阅读】Masked Autoencoders Are Effective Tokenizers for Diffusion Models
  • 内容管理系统指南:企业内容运营的核心引擎
  • Kotlin Map映射转换
  • 论文阅读:WildGS-SLAM:Monocular Gaussian Splatting SLAM in Dynamic Environments
  • 院级医疗AI管理流程—基于数据共享、算法开发与工具链治理的系统化框架
  • ubuntu之坑(十八)——XML相关
  • CSS基础功能介绍和使用
  • Spring Boot项目结构解析:构建高效、清晰的代码框架
  • 关于僵尸进程
  • 进程、线程、协程
  • AI革命,分布式存储也在革命,全闪化拐点已至
  • MFC扩展库BCGControlBar Pro v36.2新版亮点:可视化设计器升级
  • 深入解析Paimon的RowKind数据变更机制 和 KeyValue存储
  • vue中使用西瓜播放器xgplayer (封装)+xgplayer-hls 播放.m3u8格式视频
  • 【王树森推荐系统】物品冷启05:流量调控
  • Java-72 深入浅出 RPC Dubbo 上手 生产者模块详解