源码我已放入gitee仓库:https://gitee.com/xu-nuojie/java-io/tree/master/src/main/java/com/xnj/fileIODemo
File类对应 ——》filedemo包
字节输入流/字节输出流/文件复制 ——》file_byte_stream_demo包
字符输入流/字符输出流 ——》file_char_stream_demo包
资源释放 ——》trycachdemo包

File与IO流

  1. File是java.io.包下的类,File类的对象,用于代表当前操作系统的文件(文件或文件夹)。

    • 常见的方法:获取文件信息(大小,文件名,修改时间),判断文件类型,创建/删除文件/文件夹…
    • 注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据。
  2. IO流是java.io.包下的类,用于读写数据,分为输入流和输出流。

    • 用于读写数据的(可以读写文件,或网络中的数据…)
    • 输入流:从数据源读取数据到程序中,如:从键盘读取数据,从文件读取数据,从网络读取数据…
    • 输出流:从程序中写出数据到数据目的地,如:向屏幕输出数据,向文件输出数据,向网络输出数据…
  3. 对比

    • File:代表文本
    • IO流:读写数据

File类

文件类型,文件信息

File提供了的 判断文件的类型、获取文件信息功能

方法名称 说明
public boolean exists() 判断当前文件对象,对应的文件路径是否存在,存在返回true
public boolean isFile() 判断当前File对象是否代表一个文件,是文件返回true
public boolean isDirectory() 判断当前File对象是否代表一个文件夹,是文件夹返回true
public String getName() 获取文件的名称(包含后缀)
public long length() 获取文件的大小,返回字节个数
public long lastModified() 获取文件的最后修改时间
public String getPath() 获取创建文件对象时,使用的路径
public String getAbsolutePath() 获取绝对路径
public String getParent() 获取当前File对象代表的文件的父目录的路径

示例代码:

  • 文件对象、是否存在、字节大小,绝对路径/相对路径
    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
    public class FileTest {
    public static void main(String[] args) {
    // File可以代表一个文件,
    /* 文件路径符的表达方式有三种
    File file1 = new File("D:\\测试文件1.txt"); 使用双反斜杠,斜杠是转义字符,需要使用双反斜杠
    File file1 = new File("D:/测试文件1.txt"); 使用反斜杠,反斜杠是普通字符,不需要使用双反斜杠
    File file1 = new File("D:"+File.separator+"测试文件1.txt"); 使用File.separator,根据系统自动选择
    */
    File file1 = new File("D:\\测试文件1.txt");


    // 判断文件是否存在
    System.out.println(file1.exists());// true

    // 文件的字节大小
    System.out.println(file1.length()); // 14

    // 即使文件不存在,也可以创建文件对象
    File file2 = new File("D:\\测试文件2.txt");
    System.out.println(file2.exists()); // false

    // File也可以代表一个文件夹
    File dir = new File("D:/测试文件夹");
    // 输出的是文件夹自身的大小,并不加上文件夹内文件的大小
    System.out.println(dir.length()); // 0

    /* 绝对路径和相对路径
    绝对路径:从盘符开始,一直到文件或者文件夹,使用反斜杠或者双反斜杠
    相对路径:从当前目录开始,一直到文件或者文件夹,使用反斜杠或者双反斜杠
    下面的例子为java项目src目录下创建了一个test.txt
    */
    File file3 = new File("src/test.txt"); // 相对路径
    File file4 = new File("D:\\code\\JavaIODemo\\src\\test.txt"); // 绝对路径
    System.out.println(file3.length() == file4.length()); // true
    }
    }
  • 文件是否存在,是否是文件/文件夹、文件名、大小、修改时间、获取路径
    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
    public class FileTest2 {
    public static void main(String[] args) {
    File file1 = new File("D:\\测试文件夹\\aa.txt");

    // 1. 判断文件是否存在
    System.out.println(file1.exists()); // true

    // 2. 判断是否是文件
    System.out.println(file1.isFile()); // true

    // 3. 判断是否是目录
    System.out.println(file1.isDirectory()); // false

    // 4. 获取文件的名字,包括后缀名
    System.out.println(file1.getName()); // aa.txt

    // 5. 获取文件大小
    System.out.println(file1.length()); // 6

    // 6. 获取文件的上次修改时间
    long time = file1.lastModified(); // 返回1970年1月1日0时0分0秒到当前时间的毫秒数
    System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)); // 2025-03-21 15:55:23

    // 7. 获取文件的路径,获取创建文件对象时使用的路径
    File file2 = new File("D:\\code\\JavaIODemo\\src\\test.txt");
    File file3 = new File("src/test.txt");
    System.out.println(file2.getPath()); // D:\code\JavaIODemo\src\test.txt
    System.out.println(file3.getPath()); // src/test.txt

    // 8.获取文件的绝对路径
    System.out.println(file3.getAbsolutePath()); // D:\code\JavaIODemo\src\test.txt

    }
    }

创建/删除文件,文件夹,

File类还提供了创建文件,文件夹的方法,还有删除文件的方法。

方法 描述
public boolean createNewFile() 创建一个新的空文件
public boolean mkdir() 只能创建一级文件夹,如果父文件夹不存在,则创建失败
public boolean mkdirs() 可以创建多级文件夹,如果父文件夹不存在,则创建父文件夹
public boolean delete() 删除一个文件或空文件夹

示例代码

  • 创建文件/文件夹,删除文件/文件夹
    注意:delete方法只能删除文件和空文件夹,删除后的文件不会进入回收站。
    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
    /**
    * 创建文件和删除
    */
    public class FileTest3 {
    public static void main(String[] args) throws IOException {
    File file1 = new File("D:/测试文件夹/bb.txt");
    File file2 = new File("D:/测试文件夹/aa");
    File file3 = new File("D:/测试文件夹/bb/cc/dd");

    // 1. 创建一个文件,如果文件存在,则不创建,如果文件不存在,则创建
    System.out.println(file1.createNewFile()); // true

    // 2. 创建一个文件夹,如果文件夹存在,则不创建,如果文件夹不存在,则创建
    // 只会创建一级文件夹,如果父文件夹不存在,则创建失败
    System.out.println(file2.mkdir()); // true

    // 3. 创建多级文件夹,如果父文件夹不存在,则创建父文件夹
    System.out.println(file3.mkdirs()); // true

    // 4. 删除文件或文件夹
    // 如果文件夹中有文件或文件夹,删除失败
    System.out.println(file1.delete()); // true
    System.out.println(file2.delete()); // true
    File file4 = new File("D:/测试文件夹/bb");
    System.out.println(file4.delete()); // false
    }
    }

遍历文件夹

File类还提供了遍历文件夹的功能

方法 描述
public String[] listFiles() 获取当前文件夹下的所有的一级文件名称到一个字符串数组中返回
public File[] listFiles() 获取当前文件夹下的所有一级文件对象到一个文件对象数组中返回

示例代码:

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
/**
* 遍历文件夹下的两种方法
*/
public class FileTest4 {
public static void main(String[] args) {
File file = new File("D:/code");

/**
* 方式一:list
* 返回文件和文件夹的字符串数组,String 类型
* 当前文件下的一级文件和文件夹名称
*/
String[] fileList1 = file.list();
for(String f : fileList1){
System.out.println(f); // 打印出来的是文件名,不包含路径
}

/**
* 方式二:listFiles
* 返回文件和文件夹的文件对象数组,File 类型
* 当前文件下的一级文件和文件夹对象
* 注意:如果file是文件,则返回null
* 如果file是空文件夹,则返回空数组
*/
File[] files = file.listFiles();
for(File f : files){
System.out.println(f); // 打印出来的是文件名,包含路径
}

}
}

文件搜索功能

通过递归,获取文件夹下一级文件对象,判断是否是文件,判断文件名是否是搜索目标,这样就能达到搜问目标文件的功能

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
/**
* 文件搜索案例
*/
public class FileSearchDemo {
public static void main(String[] args) {
String filename = "test.txt";
File dir = new File("D:/");

fileSearch(dir, filename);


}

public static void fileSearch(File dir,String filename){
// 1. 拦截非法情况
if(dir == null || !dir.exists() || dir.isFile()){
return ;
}

// 2. 是文件夹,获取其子一级目录的内容
File[] files = dir.listFiles();
if(files != null ){
// 遍历该目录下所有一级文件对象
for(File f : files){// 是文件,判断
if(f.isFile()){
if (f.getName().contains(filename)) {
System.out.println("找到相关文件:"+f.getAbsolutePath());
/**
* 如果文件为.exe文件,我们想启动该文件,
* 那么可以调用Runtime.getRuntime().exec(文件绝对路径);
*/
// try {
// Runtime runtime = Runtime.getRuntime();
// runtime.exec(f.getAbsolutePath());
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
else {// 是文件夹,递归
fileSearch(f, filename);
}
}
}

}
}

IO流

  1. IO流概述

    • I指Input,称为输入流:负责把数据读到内存中去
      • 磁盘/网络/… => 程序/内存
    • O指Output,称为输出流:负责把数据写出去
      • 程序/内存 => 磁盘/网络/…
  2. IO流分类

    • 按流的方向分为:输入流/输出流
    • 按流的数据最小单位分为:字节流/字符流
      • 字节流:适合操作所有类型的文件,比如图片、视频、音频、文本文件的复制、转移等等
      • 字符流:只适合操作纯文本文件,比如:读写txt、java文件等
  3. 总结IO流分类

    • 字节输入流:以内存为基准,来自磁盘文件/网络中的数据已字节的形式读入内存中去的流
    • 字节输出流:以内存为基准,将内存中的数据以字节写出到磁盘文件/网络中去的流
    • 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入内存中去的流
    • 字符输出流:以内存为基准,将内存中的数据以字符写出到磁盘文件/网络介质中去的流
  4. IO流的体系
    JavaIO流的体系

字节输入流(FileInputStream)

作用:以内存为基准,把磁盘文件中的数据以字节的形式读入内存中去

构造器 说明
public FileInputStream(File file) 根据File对象创建字节输入流对象
public FileInputStream(String name) 根据文件路径创建字节输入流对象
方法 说明
public int read() 每次读取一个字节数据,如果已经到达文件末尾,则返回-1
public int read(byte[] buffer) 每次用一个字节数组读取数据,返回字节数组读取了多少字节,如果已经达末尾,则返回-1
public byte[] readAllBytes() 一次性读取整个文件内容,返回字节数组

示例代码:

  • 每次读取一个字节数据
    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
    // 使用FileInputStream从内存中读取文件,每次读取一个字节
    public class FileInputStreamTest1 {
    public static void main(String[] args) throws IOException {
    // 1. 两种输入流构造方法
    // 方式一:
    // File file = new File("src/aa.txt"); // 该文件内容为: ab
    // FileInputStream is = new FileInputStream(file);

    // 方式二:推荐
    FileInputStream is = new FileInputStream("src/aa.txt");

    // 2. 读取文件内容
    // public int read() 每次读取一个字节,文件指针后移一位,没有数据返回-1
    // 2.1 不推荐写法,用于了解
    // int read1 = is.read();
    // System.out.println((char)read1); // a
    //
    // int read2 = is.read(); // b
    // System.out.println((char)read2);
    //
    // int read3 = is.read(); // -1
    // System.out.println(read3);

    // 2.2 使用循环改改良2.1
    int b; // 用于记录当前读取的字节
    while((b = is.read())!= -1){
    System.out.print((char) b);
    }

    /*
    在上面2.2的读取性能是很差的,
    且读取汉字还会乱码,因为每次只读取一个字节,而汉字是两个字节(gbk)或三个字节(utf-8)
    流使用完后必须关闭,释放系统资源
    */

    is.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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 使用FileInputStream每次读取多个字节
    public class FileInputStreamTest2 {
    public static void main(String[] args) throws IOException {
    // 创建一个字节输入流管道和源文件接通 文件内容为:abc66
    FileInputStream is = new FileInputStream("src/bb.txt");

    // 创建一个缓存数组
    byte [] buffer = new byte[3];

    // public int read(byte[] b) 读取多个字节,将字节数组作为参数传入,返回读取的字节数
    // 方式1:
    int len = is.read(buffer);
    System.out.println("第一次读了:"+ len + "个字节"); // 3
    System.out.println(new String(buffer)); // abc

    // int len2 = is.read(buffer);
    // System.out.println("第二次读了:"+ len2 + "个字节"); // 2
    // System.out.println(new String(buffer)); // 66c


    /*
    问题:
    因为每次读取到的字节放入缓存字节数组中覆盖上次的内容,
    如果最后一处读取的长度小于缓存数组的长度,那么内容无法完全覆盖上次的内容
    解决办法:
    读多少取多少
    new String(byte[] b, int off, int len)
    */


    // 优化如下
    // 方式二:都多少,取多少
    int len2 = is.read(buffer);
    System.out.println("第二次读了:"+ len2 + "个字节"); // 2
    System.out.println(new String(buffer, 0, len2)); // 66


    // 测试:如果文件已经读取完毕,再次读取,返回-1
    int len3 = is.read(buffer);
    System.out.println(len3);

    is.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
    32
    33
    34
    35
    36
    // 使用FileInputStream每次读取多个字节
    public class FileInputStreamTest3 {

    public static void main(String[] args) throws IOException {

    // 创建一个字节输入流管道和源文件接通 文件内容为:abc66
    FileInputStream is = new FileInputStream("src/bb.txt");


    // 使用循环读取数据到字节数组中
    byte [] buffer = new byte[3];
    int len;
    while ((len = is.read(buffer))!= -1){
    // 每一次读多少取多少
    System.out.print(new String(buffer, 0, len));
    }

    /*
    但是该方式仍然无法解决读取汉字乱码问题,无法避免汉字编码被截断的问题
    但是适用于文件拷贝操作
    */

    /*
    解决汉字乱码的关键就是不截断汉字的编码,那就一次性读取整个文件
    字节输入流FileInputStream提供了读取全部字节的方法,如下
    public byte[] readAllBytes()
    但是如果文件太大,创建的字节数组太大,会报内存溢出异常
    读写文本更适合使用字符流
    */
    // byte[] bytes = is.readAllBytes();
    // System.out.println(new String(bytes));


    is.close();
    }
    }

注意:直接把文件数据全部读取到一个字节数组可以避免乱码,但如果文件过大,创建的字节数组也会过大,可能引起内存溢出。读写文本更适合用字符流,字节流适合做数据的转移,如:文件复制等。

字节输出流(FileOutputStream)

作用:以内存为基准,把内存中的数据已字节的形式写出到文件中去

构造器 说明
FileOutputStream(File file) 创建一个字节输出流,以写入模式打开指定文件
FileOutputStream(String filepath) 创建一个字节输出流,以写入模式打开指定文件
FileOutputStream(File file, boolean append) 创建一个字节输出流,以写入模式打开指定文件,如果第二个参数为true,则追加到文件末尾
FileOutputStream(String filepath, boolean append) 创建一个字节输出流,以写入模式打开指定文件,如果第二个参数为true,则追加到文件末尾
方法 描述
public void write(int b) 写一个字节出去
public void write(byte[] b) 写一个字节数组出去
public void write(byte[] b, int off, int len) 写一个字节数组的一部分出去,从off开始写,长度为len
public void 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 文件字节输出流,写文件到硬盘
public class FileOutPutStreamTest5 {
public static void main(String[] args) throws IOException {
/*
创建文件字节输出流对象,构造函数中的文件可不存在,会自动创建
覆盖式写入:每次写入,会覆盖源文件的原本内容
*/
// FileOutputStream os = new FileOutputStream("src/out.txt");

/*
追加式写入:每次写入,不会覆盖源文件的原本内容,而是追加到文件末尾
*/
FileOutputStream os = new FileOutputStream("src/out.txt", true);


/*
写入方式一:
写入单个字节,write(int b)
*/
os.write('a');
os.write(98);
//因为汉字不止一个字节,所以会被截断,导致文件中乱码
//os.write('徐');

/*
写入方式二:
一次写入多个字节,write(byte[] b)
*/
byte[] bytes = "aa我爱你中国".getBytes();
os.write(bytes);

/*
写入换行符: \r\n
*/
os.write("\r\n".getBytes());

/*
写入方式三:
指定写入的字节个数 write(byte[] b, int off, int len)
*/
os.write("aa我爱你中国".getBytes(), 2, 9);



os.close();
}
}

复制文件

https://gitee.com/xu-nuojie/java-io/blob/master/src/main/java/com/xnj/file_buffer_stream_demo/CopyTimeTest.java

字节流非常适合做一切文件的复制操作。
任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题

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 class FileCopyDemo {

public static void main(String[] args) throws IOException {
// 1. 创建文件字节输入流与源文件连通:读取源文件
FileInputStream is = new FileInputStream("src/疯狂星期四.png");
// 2. 创建文件字节输出流与目标文件连通:写入目标文件
FileOutputStream os = new FileOutputStream("src/疯狂星期四copy.png");
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB=>1024字节
// 4. 从字节输入流中读取字节数据,写出到字节输出流中
int len; // len表示每次读取的字节数
while ((len = is.read(buffer)) != -1){
// 写入目标文件
// 输入流读多少输出流写多少
os.write(buffer, 0, len);
}


// 5.关闭流,从里往外关,先创建的后关
os.close();
is.close();

}
}

资源的释放

  1. 创建流对象时,使用try-with-resource语句,自动释放资源。
    jdk7开始提供的,更推荐使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try(定义资源1;定义资源2;...
    // 注意:这里只能放置资源对象。(流对象)
    // 如何判断?资源都会实现AutoCloseable接口,并实现close方法
    // 用完之后会自动调用close方法完成资源释放操作
    ){
    // 可能出现异常的代码逻辑

    } catch (异常类名 变量名) {
    // 处理异常的代码逻辑
    }
  2. 使用finally语句,手动释放资源。
    finally代码区的特点:无论try语句是否出现异常,finally代码区都会执行,除非JVM退出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    FileInputStream is = null;
    FileOutputStream os = null;
    try {
    // 可能出现异常的代码逻辑
    is = new FileInputStream("");
    os = new FileOutputStream("");
    }catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    // 5.关闭流,从里往外关,先创建的后关
    try {
    if(os!=null) os.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    try {
    if(is != null) is.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }

字符输入流(FileReader)

作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去

构造器 说明
FileReader(File file) 创建一个字符输入流,以读模式打开指定文件
FileReader(String filepath) 创建一个字符输入流,以读模式打开指定文件
方法 说明
read() 每次读取一个字符,如果已经到达文件末尾,则返回 -1
read(char[] buffer) 每次用一个字符数组去读取数据,返回读取到的字符数,没有则返回 -1

示例代码:

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
// 字符输入流一次读取一个或多个字符
public class FileReaderTest1 {
public static void main(String[] args) {

try (
// 1. 创建一个字符输入流对象
FileReader reader = new FileReader("src/出师表.txt");
){

// 2. 一次读取一个字符,性能较差
// int c; // 读取一个字符返回其编码
// while ((c = reader.read()) != -1){
// System.out.print((char)c);
// }

//3. 一次读取多个字符,性能较好
char [] buffer = new char[3]; // 每次读取3个字符
int len;
while ((len = reader.read(buffer)) != -1){
System.out.print(new String(buffer, 0, len));
}

} catch (Exception e) {
e.printStackTrace();
}

}
}

字符输出流(FileWriter)

作用:以内存为基准,把内存中的数据以字符的形式写出到文件中

构造器 功能
FileWriter(String filepath) 创建字符输出流,用于将数据写入文件
FileWriter(File file) 创建字符输出流,用于将数据写入文件
FileWriter(File file,boolean append) 创建字符输出流,用于将数据写入文件,可追加
FileWriter(String filepath,boolean append) 创建字符输出流,用于将数据写入文件,可追加
方法 功能
write(int c) 写入一个字符
write(String str) 写入一个字符串
write(String str,int off,int len) 写入一个字符串的指定部分
write(char[] cbuf) 写入一个字符数组
write(char[] cbuf,int off,int len) 写入一个字符数组的指定部分
flush() 刷新缓冲区

注意:字符输出流写出数据后,必须刷新缓冲区,或者关闭流,写出的数据才会被写入到文件。

示例代码:

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
// 文件字符输出流
public class FileWriteTest2 {
public static void main(String[] args) {
try (
// 0. 创建字符输出流对象
FileWriter writer = new FileWriter("src/字符输出流test.txt");
// 追加模式
// FileWriter writer = new FileWriter("src/字符输出流test.txt",true);
){
// 1. void write(int c) 写入一个字符
writer.write('A');
writer.write(97);
writer.write('徐');

// 2. void write(String str) 写入一个字符串
writer.write("天天向上");

// 3. void write(String str, int off, int len) 写入一个字符串的一部分
writer.write("天天向上", 0, 2);

// 4. void write(char[] cbuf) 写入一个字符数组
char [] chars = {'a', 'b', 'c','你','好'};
writer.write(chars);

// 5. void write(char[] cbuf, int off, int len) 写入一个字符数组的一部分
writer.write(chars, 3, 2);

// 6. 写入换行符 \r\n
writer.write("\r\n");
writer.write("哈哈");

} catch (IOException e) {
e.printStackTrace();
}
}
}

缓冲流(BufferedStream)

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

  • 字节缓冲流
    • 字节缓存输入流:BufferedInputStream 对应 FileInputStream
    • 字节缓存输出流:BufferedOutputStream 对应 FileOutputStream
  • 字符缓冲流
    • 字符缓存输入流:BufferedReader 对应 FileReader
    • 字符缓存输出流:BufferedWriter 对应 FileWriter

字节缓冲输入输出流

原理:字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池

构造器 功能
BufferedInputStream(InputStream is) 构造方法,参数是字节输入流
BufferedOutputStream(OutputStream os) 构造方法,参数是字节输出流

示例:

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
// 字节输入输出缓冲流
public class FileByteBufferTest {
public static void main(String[] args) {
try (
FileInputStream is = new FileInputStream("src/疯狂星期四.png");
// 1.1 创建字节输入缓存流
BufferedInputStream bis = new BufferedInputStream(is);

FileOutputStream os = new FileOutputStream("src/疯狂星期四copy.png");
//2.2 创建字节输出缓存流
BufferedOutputStream bos = new BufferedOutputStream(os);
){

byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1){

bos.write(buffer, 0, len);
}

} catch (Exception e) {
e.printStackTrace();
}

}
}

字符缓冲输入流(BufferedReader)

作用:自带8K(8192)的字符缓冲流,可以提高字符输入流读取字符数据的性能

构造器 说明
BufferedReader(Reader r) 创建一个使用默认大小输入缓冲区的缓冲字符输入流

字符缓冲输入流新增了按行读取字符的方法

方法 说明
String readLine() 从该流中读取一行数据返回,如果已经到达流的末尾,则返回 null

示例代码:

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
// 字符输入输出缓冲流
public class BufferReaderTest {
public static void main(String[] args) {
try (
FileReader reader = new FileReader("src/out.txt");
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(reader);
){

// char[] buffer = new char[1024];
// int len;
// while ((len = br.read(buffer)) != -1){
// System.out.print(new String(buffer, 0, len));
// }

// 字符缓存输入流新方法 readLine()
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}


} catch (Exception e) {
e.printStackTrace();
}

}
}

字符缓冲输出流(BufferedWriter)

作用:自带8K(8192)的字符缓冲流,可以提高字符输出流输出字符数据的性能

构造方法 功能
BufferedWriter(Writer out) 创建一个使用默认大小8K的缓冲输出流

字符输出缓冲流新功能:换行

方法 功能
public void newLine() 换行,默认为系统换行符

示例代码:

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
// 字符缓冲输出流,写出数据
public class BufferWriterTest {
public static void main(String[] args) {
try(
FileWriter fw = new FileWriter("src/bufferwriter.txt",true);
// 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(fw);
){

bw.write('a');
bw.write("你好啊");

// 新增的方法:写入换行
bw.newLine();

char [] chr = {'a', 'b', 'c'};
bw.write(chr,2,1);

bw.newLine();

String str = "good time";
bw.write(str,5,4);


}catch (Exception e){
e.printStackTrace();
}
}
}

字符转换流

如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码!
如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件时就会出现乱码!

字符输入转换流(InputStreamReader)

  • 解决不同编码时,字符流读取文本内容乱码的问题。
  • 先获取文件的原始字节流,再将其按真实的字符编码转换成字符输入流,这样字符输入流中的字符就不乱码了。
构造器 说明
InputStreamReader(InputStream is) 把原始的字节输入流,按照代码默认编码转换成字符输入流
InputStreamReader(InputStream is, String charsetName) 创建一个使用指定字符集的 InputStreamReader

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 字符输入转换流
public class InputStreamReaderTest {
public static void main(String[] args) {
try (
// 1. 得到文件的原始字节输入流
FileInputStream frd = new FileInputStream("src/转换流gbk.txt");
// 2. 把原始字节输入流按指定字符集进行转换成字符输入流
InputStreamReader in = new InputStreamReader(frd, "GBK");
// 3. 把字符输入流包装成字符缓冲输入流
BufferedReader br = new BufferedReader(in);
){

String line ;
while ((line = br.readLine())!=null){
System.out.println(line);
}

} catch (Exception e) {
e.printStackTrace();
}
}

}

字符输出流转换流(OutputStreamWriter)

  • 作用:可以控制写出去的字符使用什么字符编码
  • 获取字节输出流,再按照指定字符编码,将其转换成字符输出流
构造器 说明
OutputStreamWriter(OutputStream os) 把原始的字节输出流,按照代码默认编码转换成字符输出流
OutputStreamWriter(OutputStream os, String charsetName) 创建一个使用指定字符集的 OutputStreamWriter

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 字符输出转换流
public class OutputStreamWriterTest {
public static void main(String[] args) {
try(
// 1. 创建一个字节输出流对象
FileOutputStream os = new FileOutputStream("src/transformout.txt");
// 2. 创建一个字符输出转换流对象, 指定字符集GBK
OutputStreamWriter osw = new OutputStreamWriter(os,"GBK");
// 3. 使用字符输出缓冲流对象,写入数据
BufferedWriter bw = new BufferedWriter(osw);
){
bw.write("abc你好hh");
}catch (Exception e){
e.printStackTrace();
}
}
}

打印流(PrintStream/PrintWriter)

作用:打印流可以实现更方便,更高效的打印数据出去,能实现打印啥出去就是啥出去。

PrintStream提供的打印数据的方案

构造器 说明
PrintStream(OutputStream/File/String) 打印流直接通向字节输出流/文件/文件路径
PrintStream(String fileName, Charset charset) 可以指定写出去的字符编码
PrintStream(OutputStream out, boolean autoFlush) 可以设置自动刷新
PrintStream(OutputStream out, boolean autoFlush, String encoding) 可以设置自动刷新和字符编码
方法 说明
println(XXX xx) 打印任意类型的数据出去
public void write(int/byte[]/byte[]一部分) 可以支持写字节数据出去

PrintWriter提供的打印数据的方案

构造器 说明
PrintWriter(OutputStream/Writer/File/String) 打印流直接通向字节输出流/文件/文件路径
PrintWriter(String filename, Charset charset) 指定写出去的字符编码
PrintWriter(OutputStream out/Writer, boolean autoFlush) 可以设置自动刷新
PrintWriter(OutputStream out, boolean autoFlush, String encoding) 可以设置自动刷新和字符编码
方法 说明
public void println(XXX xx) 打印任意类型的数据出去
public void print(int/String/char[]/…) 打印字符

演示代码:https://gitee.com/xu-nuojie/java-io/tree/master/src/main/java/com/xnj/print_stream_writer_demo

区别

  • 打印数据的功能上是一模一样的:都是使用方柏霓,性能高效(核心优势)
  • PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
  • PrintWriter继承自字符输出流Writer,因此支持写字符数据的方法。

应用:输出语句的重定向。

可以把输出语句的打印位置改到某个文件中去。

1
2
PrintStream ps = new PrintStream("文件地址");
System.setOut(ps);

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 打印流,重定向println
public class Test2 {
public static void main(String[] args) {

// 打印到控制台
System.out.println("helloWorld");
try (
PrintStream ps = new PrintStream("src/systemout.txt");
){
System.setOut(ps);
// 打印到文件
System.out.println("helloWorld");

} catch (Exception e) {
e.printStackTrace();
}
}
}

数据流

数据输出流(DataOutputStream)

  • 允许把数据和其类型一并写出去
构造器 说明
DataOutputStream(OutputStream out) 创建新数据输出流包装基础的字节输出流

| 方法 | 说明 |
| writeByte(int v) | 将指定的 byte 值写入此数据输出流 |
| writeInt(int v) | 将指定的 int 值写入此数据输出流 |
| writeUTF(String v) | 将字符串数据以UTF-8编码成字节写入此数据输出流 |
| writeBoolean(boolean v) | 将指定的 boolean 值写入此数据输出流 |
| write(int/byte[]/byte[]一部分) | 支持写字节数据出去 |

数据输入流(DataInputStream)

  • 用于读取数据输出流写出去的数据
构造器 描述
DataInputStream(InputStream in) 创建一个数据输入流,以便从指定的输入流读取字节
方法 描述
readBoolean() 从输入流读取一个字节,将其转换为一个 boolean 值并返回
readByte() 从输入流读取一个字节,将其转换为一个 byte 值并返回
readChar() 从输入流读取两个字节,将其转换为一个 char 值并返回
readDouble() 从输入流读取 eight bytes,将其转换为一个 double 值并返回
readUTF() 从输入流读取一个 UTF-8 编码的字符串并返回
readInt()/read(byte[] b) 支持读字节数据进来

序列化流

  • 对象序列化:把Java对象写入到对象文件中去
    • 注意:对象必须实现Serializable接口
    • 如果某个属性不想被序列化,那么在属性前加上transient关键字
  • 对象反序列化:把文件里的Java对象读出来

对象字节输出流(ObjectOutputStream)

构造器 说明
ObjectOutputStream(OutputStream out) 创建一个对象字节输出流,向指定的输出流中写入字节
方法 说明
void writeObject(Object obj) 将指定的对象写入字节流

如果要一次序列化多个对象:可以用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可

  • ArrayList集合已经实现了序列化接口。

对象字节输入流(ObjectInputStream)

构造器 说明
ObjectInputStream(InputStream in) 创建一个对象字节输入流,从指定的输入流中读取字节
方法 说明
Object readObject() 把存储在文件中的Java对象读出来

IO框架

Commons-io:可以去了解下,它是apache下的一个开源项目,提供了一些很实用的工具类,

FileUtile类提供的部分方法

  • copyFile(String srcFile, String destFile) 复制文件
  • copyDirectory(String srcDir, String destDir) 复制文件夹
  • deleteDirectory(File directory) 删除文件夹
  • readFileToString(File file, String encoding) 读出文件所有数据以字符串的形式返回
  • writeStringToFile(File file, String data, String encoding, boolean append) 将字符串写入文件

IOUtils类提供的部分方法

  • copy(InputStream input, OutputStream output) 复制文件
  • copy(Reader input, Writer output) 复制文件
  • write(String data, OutputStream output, String encoding) 将字符串写入文件

Java自己也提供了一些便捷
Files类就提供了一些便捷的方法。