Java I/O 流

前言

1、文件对象

  • 文件 和 文件夹 都是用 File 代表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import java.io.File;

    public class TestFile {
    public static void main(String[] args) {

    // 绝对路径
    File f1 = new File("d:/LOLFolder");

    // 相对路径,相对于工作目录,如果在 eclipse 中,就是项目目录
    File f2 = new File("LOL.exe");

    // 把 f1 作为父目录创建文件对象
    File f3 = new File(f1, "LOL.exe");
    }
    }
  • 常用方法

关键字 介绍
exists 判断文件是否存在
isFile 判断是否是文件(非文件夹)
isDirectory 判断是否是文件夹
length 获取文件的长度
lastModified 获取文件最后修改时间
setLastModified 设置文件修改时间
renameTo 文件重命名,用于对物理文件名称进行修改,但是并不会修改 File 对象的 name 属性
list 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
listFiles 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
getParent 以字符串形式返回获取所在文件夹
getParentFile 以文件形式返回获取所在文件夹
mkdir 创建文件夹,如果父文件夹不存在,创建就无效
mkdirs 创建文件夹,如果父文件夹不存在,就会创建父文件夹
createNewFile 创建一个空文件,如果父文件夹不存在,就会抛出异常,所以创建一个空文件之前,通常都会创建父目录 .getParentFile().mkdirs()
listRoots 列出所有的盘符 c: d: e: 等等
delete 刪除文件
deleteOnExit JVM 结束的时候,刪除文件,常用于临时文件的删除

2、流

  • 流 (Stream) 就是一系列的数据。

  • 当不同的介质之间有数据交互的时候,JAVA 就使用流来实现。数据源可以是文件,还可以是数据库,网络甚至是其他的程序。

    • 输入流:InputStream
    • 输出流:OutputStream
  • 比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流。

    1
    2
    3
    4
    5
    6
    try {
    File f = new File("d:/lol.txt");
    FileInputStream fis = new FileInputStream(f); // 通过这个输入流,就可以把数据从硬盘,读取到 Java 的虚拟机中来,也就是读取到内存中
    } catch (IOException e) {
    e.printStackTrace();
    }
  • 流关系图

    1
    2
    3
    4
    5
    6
    7
    8
    流 --|--- 字节流 --|--- 数据流
    | |
    | |--- 对象流
    | |
    |
    |--- 字符流 --|--- 缓存流
    | |
    |

2.1 关闭流的方式

  • 所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。如果不关闭,会产生对资源占用的浪费。当量比较大的时候,会影响到业务的正常开展。

    1
    fis.close();
  • 关闭方式

    • try 中关闭:如果文件不存在,或者读取的时候出现问题而抛出异常,不推荐使用。
    • finally 中关闭:首先把流的引用声明在 try 的外面,在 finally 关闭之前,要先判断该引用是否为空,关闭的时候,需要再一次进行 try catch 处理,看上去很繁琐。
    • 使用 try() 的方式:把流定义在 try() 里,trycatch 或者 finally 结束的时候,会自动关闭。这种编写代码的方式叫做 try-with-resources。
  • 所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在 try() 中进行实例化。 并且在 trycatchfinally 结束的时候自动关闭,回收相关资源。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    File f = new File("d:/lol.txt");

    // 把流定义在 try() 里,try、catch 或者 finally 结束的时候,会自动关闭
    try (FileInputStream fis = new FileInputStream(f)) {

    byte[] all = new byte[(int) f.length()];
    fis.read(all);
    for (byte b : all) {
    System.out.println(b);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }

2.2 中文问题

  • Java 采用的是 Unicode(统一码,万国码)编码方式,每个数字都是很长的(4 个字节),不仅要表示字母,还要表示汉字等。
  • UTF-8,UTF-16 和 UTF-32 是针对不同类型的数据有不同的减肥效果的子编码,一般说来 UTF-8 是比较常用的方式。
  • UTF-8 对数字和字母就使用一个字节,而对汉字就使用 3 个字节。

3、字节流

  • 字节流:以字节的形式读取和写入数据。

    • 字节输入流:InputStream 是抽象类,只提供方法声明,不提供方法的具体实现。
    • 字节输出流:OutputStream 是抽象类,只提供方法声明,不提供方法的具体实现。
  • 读取文件内容,FileInputStreamInputStream 子类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol.txt"); // 准备文件 lol.txt 其中的内容是 AB,对应的 ASCII 分别是 65 66

    try (FileInputStream fis = new FileInputStream(f)) { // 创建基于文件的输入流
    byte[] all = new byte[(int) f.length()]; // 创建字节数组,其长度就是文件的长度
    fis.read(all); // 以字节流的形式读取文件所有内容
    for (byte b : all) {
    System.out.println(b); // 打印出来是 65 66
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 向文件写入数据,FileOutputStreamOutputStream 子类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol2.txt"); // 准备文件 lol2.txt 其中的内容是空的

    try (FileOutputStream fos = new FileOutputStream(f)) { // 创建基于文件的输出流
    byte data[] = { 88, 89 }; // 准备长度是 2 的字节数组,用 88, 89 初始化,其对应的字符分别是 X, Y
    fos.write(data); // 把数据写入到输出流
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 如果文件 d:/lol2.txt 不存在,写出操作会自动创建该文件。但是如果是文件 d:/xyz/lol2.txt,而目录 xyz 又不存在,会抛出异常.

4、字符流

  • 字符流:以字符的形式读取和写入数据。

    • 字符输入流:Reader
    • 字符输出流:Writer
  • 读取文件内容,FileReaderReader 子类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol.txt"); // 准备文件 lol.txt 其中的内容是 AB

    try (FileReader fr = new FileReader(f)) { // 创建基于文件的 Reader
    char[] all = new char[(int) f.length()]; // 创建字符数组,其长度就是文件的长度
    fr.read(all); // 以字符流的形式读取文件所有内容
    for (char b : all) {
    System.out.println(b); // 打印出来是 A B
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 向文件写入数据,FileWriterWriter 子类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol2.txt"); // 准备文件 lol2.txt

    try (FileWriter fr = new FileWriter(f)) { // 创建基于文件的 Writer
    String data ="abcdefg1234567890";
    char[] cs = data.toCharArray();
    fr.write(cs); // 以字符流的形式把数据写入到文件中
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

5、缓存流

  • 缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

  • 在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了 IO 操作。

    • 缓存字符输入流:BufferedReader 可以一次读取一行数据。
    • 缓存字符输出流:PrintWriter 可以一次写出一行数据。
  • 缓存流必须建立在一个存在的流的基础上。

  • 读取数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol.txt"); // 准备文件 lol.txt 其中的内容是

    try (FileReader fr = new FileReader(f);
    BufferedReader br = new BufferedReader(fr);) { // 创建文件字符流,缓存流必须建立在一个存在的流的基础上
    while (true) {
    String line = br.readLine(); // 一次读一行
    if (null == line) {
    break;
    }
    System.out.println(line);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 写出数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;

    public class TestStream {
    public static void main(String[] args) {

    File f = new File("d:/lol2.txt"); // 向文件 lol2.txt 中写入三行语句

    try (FileWriter fw = new FileWriter(f);
    PrintWriter pw = new PrintWriter(fw);) { // 创建文件字符流,缓存流必须建立在一个存在的流的基础上
    pw.println("garen kill teemo");
    pw.println("teemo revive after 1 minutes");
    pw.println("teemo try to garen, but killed again");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 立即把数据写入到硬盘

    1
    2
    3
    pw.println("garen kill teemo");

    pw.flush(); // 强制把缓存中的数据写入硬盘,无论缓存是否已满

6、数据流

  • 数据流

    • 数据输入流:DataInputStream
    • 数据输出流:DataOutputStream
  • 数据流必须建立在一个存在的流的基础上。

  • 字符串的读写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    FileInputStream fis  = new FileInputStream(f);
    FileOutputStream fos = new FileOutputStream(f);

    DataInputStream dis = new DataInputStream(fis); // 创建 数据输入流
    DataOutputStream dos = new DataOutputStream(fos); // 创建 数据输出流

    boolean b= dis.readBoolean();
    int i = dis.readInt();
    String str = dis.readUTF();

    dos.writeBoolean(true);
    dos.writeInt(300);
    dos.writeUTF("123 this is gareen");

7、对象流

  • 对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘。

    • 对象输入流:ObjectInputStream
    • 对象输出流:ObjectOutputStream
  • 一个对象以流的形式进行传输,叫做序列化。该对象所对应的类,必须是实现 Serializable 接口。

  • 数据流必须建立在一个存在的流的基础上。

  • 序列化一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    FileInputStream fis = new FileInputStream(f);
    FileOutputStream fos = new FileOutputStream(f);

    ObjectInputStream ois = new ObjectInputStream(fis); // 创建 对象输入流
    ObjectOutputStream oos = new ObjectOutputStream(fos); // 创建 对象输出流

    Hero h2 = (Hero) ois.readObject();
    oos.writeObject(h);

8、控制台输入/输出流

  • 控制台输入/输出流

    • 控制台输入流:System.in
    • 控制台输出流:System.out
  • 输入/输出数据

    1
    2
    3
    4
    5
    6
    InputStream is = System.in;
    int i = is.read(); // 控制台输入

    int i = System.in.read(); // 控制台输入

    System.out.println(i); // 控制台输出

8.1 Scanner

  • 使用 Scanner 可以逐行读取数据。

  • 从控制台读取数据

    1
    2
    3
    4
    5
    Scanner s = new Scanner(System.in);

    String line = s.nextLine(); // 读取字符串
    int a = s.nextInt(); // 读取整数
    float a = s.nextFloat(); // 读取浮点数
文章目录
  1. 1. 前言
  2. 2. 1、文件对象
  3. 3. 2、流
    1. 3.1. 2.1 关闭流的方式
    2. 3.2. 2.2 中文问题
  4. 4. 3、字节流
  5. 5. 4、字符流
  6. 6. 5、缓存流
  7. 7. 6、数据流
  8. 8. 7、对象流
  9. 9. 8、控制台输入/输出流
    1. 9.1. 8.1 Scanner
隐藏目录