IO

本文最后更新于 2025年9月9日 下午

    QA*韩顺平B站IO流课程**

目录

文件基础知识


  • 输入还是输出是针对内存而言

常用文件操作

  • tips:一个汉字是3个字节

  • 文件的删除

  • 目录也是一种文件

流的分类


FileInputStream


  • 用于读取文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    @Test  
    public void readFile01() {
    String filePath = "e:\\io\\hello.txt";
    int readData = 0;
    java.io.FileInputStream fileInputStream = null;

    try {
    // 创建fileinputstream对象用于读取文件
    fileInputStream = new java.io.FileInputStream(filePath);
    while ((readData = fileInputStream.read()) != -1) {
    System.out.print((char) readData); // 转成char显示
    }
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    // 关闭文件流,释放资源
    fileInputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }

    /**
    * 读取文件中汉字,用字节数组的形式提高读取效率
    */
    @Test
    public void readFile02() {
    String filePath = "e:\\io\\hello.txt";
    byte[] buf = new byte[8]; // 一次读8个字节
    Integer readLen = 0;
    java.io.FileInputStream fileInputStream = null;
    try {
    // 创建fileinputstream对象用于读取文件
    fileInputStream = new FileInputStream(filePath);
    while ((readLen = fileInputStream.read(buf)) != -1) {
    System.out.print(new String(buf, 0, readLen)); // 转成char显示
    }
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    // 关闭文件流,释放资源
    fileInputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
  • 讲解

    FileInputStream.read(byte[] b) 方法详解

  1. 功能

    • 尝试从文件中读取最多 b.length 个字节的数据,并将其存储到字节数组 b 中。
    • 返回实际读取的字节数(整数),如果已到达文件末尾,则返回 -1
  2. 执行流程

    • 读取数据:从文件当前位置开始,尝试读取 b.length 个字节到数组 b 中。
    • 返回值
      • 正整数:表示成功读取的字节数(可能小于 b.length,例如文件剩余数据不足时)。
      • -1:表示已到达文件末尾(EOF,End of File)。
    • 文件指针移动:每次读取后,文件内部的指针会向后移动实际读取的字节数。

FileOutputStream


  • 用于往文件中写入内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     @Test  
    public void writeFile01() {
    //创建对象
    FileOutputStream fileOutputStream = null;
    String filePath = "e:\\io\\a.txt";
    try {
    fileOutputStream = new FileOutputStream(filePath);
    // 写入字节数据 注意,char会自动转成int类型数据
    // fileOutputStream.write('B'); // 如果没有文件会自动创建
    // 写入字符串 string.getBytes()会自动把字符串转换成字节数组
    // fileOutputStream.write("hello,world".getBytes());
    fileOutputStream.write("hello,world".getBytes(),3,5); //从第三个开始写入5个字节

    // 1. new FileOutputStream(filePath); 当写入内容会覆盖原来的内容
    // 2. new FileOutputStream(filePath,true); 当写入内容会追加内容
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    fileOutputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }

文件的拷贝


  • 源代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 创建文件输入流,将文件读取到程序  
    // 创建文件输出流,将读取到的文件数据,写入到指定的文件中
    String filePath = "C:\\Users\\user\\Pictures\\100NZ502\\DSC_0002.JPG"; // 源文件路径
    String targetPath = "e:\\io\\copy.jpg"; // 注意目标指向的一定是一个文件而不是目录
    FileInputStream fileInputStream = null;
    FileOutputStream fileOutputStream = null;

    try {
    fileInputStream = new FileInputStream(filePath);
    fileOutputStream = new FileOutputStream(targetPath);
    // 定义字节数组提高读取效率
    byte[] buf = new byte[1024];
    int readLen = 0;
    // 一边读取一边写入
    while ((readLen= fileInputStream.read(buf))!=-1){
    fileOutputStream.write(buf,0,readLen); // 一定要用这个方法,否则会出现读取的字节不够
    }
    System.out.println("拷贝完成");
    } catch (IOException e) {
    throw new RuntimeException(e);
    }finally {
    try {
    if(fileInputStream!=null){
    fileInputStream.close();
    }
    if(fileOutputStream!=null){
    fileOutputStream.close();
    }
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }

FileReader FileWriter


  • 案例
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // 单个字符读取
      public static void main(String[] args) {
      String filePath = "e:\\io\\story.txt";
      FileReader fileReader = null;
      int data = 0;
      try {
      fileReader = new FileReader(filePath);
      // 循环读取并输出 单个字符输出
      while ((data = fileReader.read())!=-1){
      System.out.print((char)data);
      }
      } catch (IOException e) {
      throw new RuntimeException(e);
      } finally {
      if(fileReader!=null){
      try {
      fileReader.close();
      } catch (IOException e) {
      throw new RuntimeException(e);
      }
      }
      }
      }
      改进
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 使用字符数组批量读取
      public static void main(String[] args) {
      String filePath = "e:\\io\\story.txt";
      FileReader fileReader = null;
      int readLength = 0;
      char[] buf = new char[1024]; // 数组长度自拟
      try {
      fileReader = new FileReader(filePath);
      // 循环读取并输出 批量读取 read(buf)返回的是实际读取到的字符数
      // 如果返回 -1,说明文件读取结束
      while ((readLength = fileReader.read(buf))!=-1){
      System.out.print(new String(buf, 0, readLength));
      }
      } catch (IOException e) {
      throw new RuntimeException(e);
      } finally {
      if(fileReader!=null){
      try {
      fileReader.close();
      } catch (IOException e) {
      throw new RuntimeException(e);
      }
      }
      }
      }

节点流和处理流


节点流


处理流


BufferedReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String filePath = "e:\\io\\story.txt";  
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//读取
String line;
// 按行读取
/*
注意这里的readline是按行读取
*/while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}

/* 关闭流
注意: 这里只需要关闭 BufferedReader,因为底层的会自动去关闭节点流
*/bufferedReader.close();

BufferedWriter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 String filePath = "e:\\io\\a.txt";  
// BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));// 追加写入
bufferedWriter.write("输入第一句话");
// 插入一个和系统相关的换行
bufferedWriter.newLine();
bufferedWriter.write("输入第二句话");
bufferedWriter.newLine();
bufferedWriter.write("输入第三句话");
bufferedWriter.newLine();
// 关闭流
// 注意:关闭外层流即可,底层会自动关闭节点流
bufferedWriter.close();
}

拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
String sourcePath = "e:\\io\\a.txt";  
String destPath = "e:\\io\\a_copy.txt";
BufferedReader br = null;
BufferedWriter bw = null;
String line ;
try {
br =new BufferedReader(new FileReader(sourcePath));
bw = new BufferedWriter(new FileWriter(destPath));
while ((line=br.readLine())!=null){
// readline 是读取一行写入一行,但是没有换行,所以要手动换行
bw.write(line);
bw.newLine();
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
try {
//关闭外层流
if(br!=null)
br.close();
if(bw!=null)
bw.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 注意:
* BufferedReader或者BufferedWriter是按照字符操作,
* 不要去操作二进制文件,可能会造成文件损坏
*/
  • 实际工作的是节点流处理流只是包装增强功能,关闭流的时候只用关闭bufferedReader,然后他会自动关闭底层的字节流(当前的BufferedReader是包装流)

  • 二进制文件有:图片 声音 视频

二进制拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*  拷贝图片 String sourcePicturePath = "C://Users//Yething//Pictures//Screenshots//cyxxtf.jpg";   
String destPicturePath = "E://io//bufferedCopyPicture.jpg";*/
//拷贝视频
String sourcePicturePath = "C://Users//Yething//Videos//5月19日//[DLPanda.com][30就拍鸟的ALLEN LAN]7513506201276976419.mp4";
String destPicturePath = "E://io//bufferedCopyVideo.mp4";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
// 因为FileInputStream是InputStream的子类
bis = new BufferedInputStream(new FileInputStream(sourcePicturePath));
bos = new BufferedOutputStream(new FileOutputStream(destPicturePath));

// 循环读取文件,并写入到目标位置
byte[] buf = new byte[1024]; //提升读取效率
int readLength = 0;
while ((readLength = bis.read(buf)) != -1) {
bos.write(buf, 0, readLength);
}
System.out.println("拷贝完成");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭流,关闭外层流,因为会自动关闭底层的字节流

try {
if(bis!=null){
bis.close();
}
if(bos!=null){
bos.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

节点流和处理流的区别和联系


  • 用修饰器模式,利用对象动态绑定机制,绑定到对应的实现子类

对象流


  • 保存数据的时候把数据类型也保存下来
    • 以前是:保存值
    • 现在是:保存值和数据类型,比如100是Integer类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ObjectInputStream_ {  
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 指定反序列化的文件
String filePath = "e:\\io\\ioObject_Output_Stream.bat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

// 注意:反序列化的顺序要和当初序列化的顺序相同
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object dog = ois.readObject();
System.out.println("运行类型 = "+dog.getClass());
System.out.println("dog信息 = "+dog);

//如果需要调用Dog的方法,需要向下转型
Dog dog1 = (Dog) dog;
System.out.println("dog1信息:"+dog1.getName());
// 关闭流,关闭外层流
ois.close();
}
}

class Dog implements Serializable {
private String name;
private int age;
// serialVersionUID 序列化的版本号,可以提高兼容性,加了下面这个之后不会认为是一个全新的类,会认为是Dog的一个升级版
private static final long serialVersionUID = 1L;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
  • 第六个点

标准输入输出流


  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class InputAndOutput {  
    public static void main(String[] args) {
    // System类的 public final static InputStream in = null;
    // System.in 编译类型 InputStream
    // System.in 运行类型 BufferedInputStream
    // 表示标准输入 键盘
    System.out.println(System.in.getClass());

    // System类的 public final static PrintStream out = null;
    // System.out 编译类型 PrintStream
    // System.out 运行类型 PrintStream
    // 表示标准输出 显示器
    System.out.println(System.out.getClass());
    }
    }

转换流


  • 把字节流转换成字符流
  • 学IO流最困难的是要知道在什么时候用什么流
  • ANSI是国标码,其他国家也有自己的国标码,是根据当前安装的系统决定是哪个国家的国标码
  • 出现乱码的原因是没有指定读取文件的编码方式
  • 转换流可以把字节流转换成字符流

正式学习

InputStreamReader

  • 传入字节流,然后变成字符流,完成字节流到字符流的转换
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static void main(String[] args) throws IOException {  
    String filePath = "e:\\io\\hello.txt";
    // 把FileInputStream 转成 InputStreamReader // 指定编码GBK
    InputStreamReader isr = new InputStreamReader(Files.newInputStream(Paths.get(filePath)), "GBK");
    // 把InputStreamReader 转成 BufferedReader /* 我自己的理解:
    把字节流转换成字符流,这里用到了转换流 然后把字符流用包装器来接收之后再读取,但是弹幕说buffer只是让效率变得更高,不用也行
    */ BufferedReader br = new BufferedReader(isr);
    //现在再读取
    String s = br.readLine();
    System.out.println("打印出来的内容: "+s);
    // 关闭外层流
    br.close();
    }

OutputStreamWriter

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class OutputStreamWriter_ {  
    public static void main(String[] args) throws IOException {
    String filePath = "e:\\io\\out.txt";
    String charset = "utf8";
    OutputStreamWriter osw = new OutputStreamWriter(Files.newOutputStream(Paths.get(filePath)),charset);
    osw.write("hi,你好编程");
    osw.close();
    System.out.println("按照"+charset+"格式保存文件完成");
    //这里没有用buffer也可以,buffer只是增加效率
    }
    }

PrintWriter

  • 打印流只有输出流

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    public class PrintWriter_ {  
    public static void main(String[] args) throws IOException {
    // PrintWriter printWriter = new PrintWriter(System.out); // 指向显示器
    PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\io\\printwriter.txt")); // 指向显示器
    printWriter.print("hi,Java你好");
    printWriter.close();
    }
    }

Properties


  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Properties_01 {  
    public static void main(String[] args) throws IOException {
    // 传统的读取配置文件的方式

    // 读取文件并获取相关数据
    BufferedReader br = new BufferedReader(new FileReader("D:\\WorkSpace\\IO\\newfile\\src\\mysql.properties"));
    String line;
    while((line= br.readLine())!=null){
    String[] split = line.split("=");
    System.out.println(split[0]+"="+split[1]);
    }
    br.close();
    System.out.println("读取完毕!");
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Properties_02 {  
public static void main(String[] args) throws IOException {
// 使用Properties类读取文件
Properties properties = new Properties();
// 加载指定配置文件
properties.load(new FileReader("D:\\WorkSpace\\IO\\newfile\\src\\mysql.properties"));
// 把 K V显示控制台
properties.list(System.out); // 指定显示到显示器
// 根据 key 获取对应的值
String user= properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("用户名:" + user);
System.out.println("密码:" + pwd);


}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Properties_03 {  
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
// 创建
//1.如果该文件没有key,就是创建
//2.如果该文件有key,就是修改
/*
Properties 父类是Hashtable, 底层就是Hashtable 方法
*/
// 使用Properties来创建配置类
properties.setProperty("user", "汤姆");
properties.setProperty("pwd", "121133");
properties.setProperty("ip", "127.0.0.1");

// 将KV 存储在文件中即可 字节流才是unicode码 字符流不是
properties.store(Files.newOutputStream(Paths.get("D:\\WorkSpace\\IO\\newfile\\src\\mysql2.properties")), "注释");
System.out.println("保存配置文件成功!");
}
}

IO
http://yething.github.io/posts/2248260274.html
作者
Odyssey
发布于
2025年7月13日
许可协议