0%

IO流-1

IO流

★★★第十八章 IO流

文件

  • 文件是保存数据的地方

  • 文件在程序中是以流的形式来操作的。

    流:数据在数据源(文件)和程序(内存)之间经历的路径

    • 输入流:数据从数据源(文件)到程序(内存)的路径。
    • 输出流:数据从程序(内存)到数据源(文件)的路径。
常用的文件操作
创建文件
  • 创建文件对象的相关构造器和方法:

    1. new File(String pathname):根据路径构建一个 File 对象。
    2. new File(File parent, String child):根据 “父目录文件+子路径” 构建。
    3. new File(String parent, String child):根据 “父目录+子路径” 构建。

    在构建 File 对象之后,还要通过 createNewFile 方法来创建该文件

    • 之所以还要使用 createNewFile 方法来创建 “ new 出来的文件对象” 所对应的文件,是因为 new 出来的文件对象是在 Java 程序中,即内存中,而要真正在磁盘上创建该文件,还要使用 createNewFile 方法,可以结合上面的图理解。
    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
    51
    52
    53
    54
    55
    56
    package com.f.chapter19.createfile;

    import java.io.File;
    import java.io.IOException;

    /**
    * @author fzy
    * @date 2023/7/7 14:45
    * 演示创建文件
    */
    public class CreateFile {
    public static void main(String[] args) {
    createFile01();
    createFile02();
    createFile03();
    }

    //1.`new File(String pathname)`:根据路径构建一个 `File` 对象。
    public static void createFile01() {
    String filePath = "C:\\Users\\1\\Desktop\\java\\file\\1.txt"; //路径
    //这里只是在Java程序中新建一个File对象,还没有在磁盘中实际创建对应的文件
    File file = new File(filePath);
    try {
    file.createNewFile(); //将文件在磁盘中实际创建起来
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    System.out.println("第一种方式创建文件成功...");
    }

    //2.`new File(File parent, String child)`:根据 “父目录文件+子路径” 构建。
    public static void createFile02() {
    File parentFile = new File("C:\\Users\\1\\Desktop\\java\\file\\"); //父目录文件
    String childPath = "2.txt"; //子路径
    File file = new File(parentFile, childPath);
    try {
    file.createNewFile();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    System.out.println("第二种方式创建文件成功...");
    }

    //3.`new File(String parent, String child)`:根据 “父目录+子路径” 构建。
    public static void createFile03() {
    String parentPath = "C:\\Users\\1\\Desktop\\java\\file\\"; //父目录
    String childPath = "3.txt"; //子路径
    File file = new File(parentPath, childPath);
    try {
    file.createNewFile();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    System.out.println("第三种方式创建文件成功...");
    }
    }
获取文件的相关信息
  1. getName():获取文件的名称。

  2. getAbsolutePath():获取文件的绝对路径。

  3. getParent():获取文件的父级目录。

  4. length():获取文件的大小(以字节为单位)。

  5. exists():判断文件是否存在。

  6. isFile():判断是否为文件。

  7. isDirectory():判断是否为文件夹目录。

    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
    package com.f.chapter19.fileinformation;

    import java.io.File;

    /**
    * @author fzy
    * @date 2023/7/8 12:36
    */
    public class FileInformation {
    public static void main(String[] args) {
    //先在Java程序中创建文件对象
    File file = new File("C:\\Users\\1\\Desktop\\java\\file\\1.txt");
    //1.getName():获取文件的名称
    System.out.println("文件名称 = " + file.getName());
    //2.getAbsolutePath():获取文件的绝对路径
    System.out.println("文件的绝对路径 = " + file.getAbsolutePath());
    //3.getParent():获取文件的父级目录
    System.out.println("文件的父级目录 = " + file.getParent());
    //4.length():获取文件的大小(以字节为单位)
    System.out.println("文件的字节长度为 = " + file.length());
    //5.exists():判断文件是否存在
    System.out.println("文件是否存在? -> " + file.exists());
    //6.isFile():判断是否为文件
    System.out.println("是否是文件? -> " + file.isFile());
    //7.isDirectory():判断是否为文件夹目录
    System.out.println("是否是文件夹目录? -> " + file.isDirectory());
    }
    }
目录操作
  • 首先要知道,Java 编程中,目录也被视为是文件
  1. mkdir():创建一级目录。

  2. mkdirs():创建多级目录。

  3. 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
    28
    29
    package com.f.chapter19.file;

    import java.io.File;

    /**
    * @author fzy
    * @date 2023/7/8 12:55
    */
    public class Directoty_ {
    public static void main(String[] args) {
    String dirPath = "C:\\Users\\1\\Desktop\\java\\file2";
    File dir = new File(dirPath);
    if (dir.exists()) {
    System.out.println(dirPath + " 已存在,准备删除");
    //删除该目录
    if (dir.delete()) {
    System.out.println(dirPath + " 删除成功");
    } else {
    System.out.println(dirPath + " 删除失败");
    }
    } else {
    if (dir.mkdirs()) { //创建多级目录用mkdirs()
    System.out.println(dir + " 创建成功");
    } else {
    System.out.println(dir + " 创建失败");
    }
    }
    }
    }

★★★IO流原理及流的分类

  • Java IO流原理:

    1. I/OInput/Output 的缩写,I/O 技术是非常实用的技术,用于处理数据传输,如读/写文件,网络通讯等。
    2. Java 程序中,对于数据的输入/输出操作以 “流(stream)” 的方式进行
    3. Java.io 包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
    4. 输入 input:读取外部数据(磁盘、光盘等存储设备)的数据到程序(内存)中。
    5. 输出 output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
  • 流的分类:

    • 按操作数据单位不同分为:字节流(8bit)、字符流(按字符)
    • 按数据流的流向不同分为:输入流、输出流
    • 按流的角色的不同分为:节点流、处理流/包装流
    (抽象基类)字节流字符流
    输入流InputStreamReader
    输出流OutputStreamWriter
    • JavaIO 流共涉及 40 多个类,但实际上非常规则,都是从如上 4 个抽象基类的基础上派生出来的

      由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-3.jpg)

★★★节点流和处理流

  • 节点流:可以从一个特定的数据源读写数据。如 **FileReaderFileWriter**。
  • 处理流(也叫包装流):“连接” 在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活。如 **BufferedReaderBufferedWriter**。

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-7.jpg)

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-8.jpg)

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

    1. 节点流是底层流/低级流,直接跟数据源相接
    2. 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
    3. 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
    • 处理流的功能主要体现在以下两个方面:
      1. 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
      2. 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
    • 注意:在关闭处理流时,只需要关闭外层流即可,因为底层会去自动关闭节点流。

InputStream

  • InputStream 抽象类是所有字节输入流的父类:public abstract class InputStream implements Closeable

  • InputStream 常用的子类:

    • FileInputStream:文件输入流。
    • BufferedInputStream:缓冲字节输入流。
    • ObjectInputStream:对象字节输入流。

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-4.jpg)

FileInputStream
  • public class FileInputStream extends InputStream
  1. public int read():从该输入流读取一个字节的数据,如果达到文件的末尾,则返回 -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
    29
    30
    31
    32
    33
    34
    35
    36
    package com.f.chapter19.inputstream_;

    import java.io.FileInputStream;
    import java.io.IOException;

    /**
    * @author fzy
    * @date 2023/7/8 13:47
    * 演示FileInputStream的使用(字节输入流:文件 --> 程序)
    */
    public class FileInputStream01 {
    public static void main(String[] args) {
    String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
    int readContent = 0;
    FileInputStream fileInputStream = null;
    try {
    //创建 FileInputStream 对象,用于读取文件内容
    fileInputStream = new FileInputStream(filePath);
    //从 fileInputStream 读取一个字节的数据,如果未达到文件的末尾,则不停地读取
    //如果 read 方法返回 -1,则表示读取完毕
    //单个字节的读取,效率较低
    while ((readContent = fileInputStream.read()) != -1) {
    System.out.print((char) readContent);
    }
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    //在 finally 里关闭流,避免资源的浪费
    try {
    fileInputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
    }
  2. public int read(byte[] b):从该输入流读取最多 b.length 字节的数据到字节数组。如果读取正常,返回的是实际读取的字节数;如果达到文件的末尾,则返回 -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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.f.chapter19.inputstream_;

    import java.io.FileInputStream;
    import java.io.IOException;

    /**
    * @author fzy
    * @date 2023/7/8 14:23
    */

    public class FileInputStream02 {
    public static void main(String[] args) {
    String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
    byte[] buf = new byte[8]; //一次读取8个字节
    FileInputStream fileInputStream = null;
    int readLen = 0; //读取的字节数
    int cnt = 0; //读取的次数
    try {
    fileInputStream = new FileInputStream(filePath);
    //如果读取正常,read(byte[] b) 返回的是实际读取的字节数
    //如果达到文件的末尾,则返回 -1
    while ((readLen = fileInputStream.read(buf)) != -1) {
    cnt++;
    System.out.println("这次读取的字节数 = " + readLen);
    System.out.println(new String(buf, 0, readLen));
    }
    System.out.println("共读取了 " + cnt + " 次");
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    fileInputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
    }

    输出为:

    1
    2
    3
    4
    5
    这次读取的字节数 = 8
    hello,wo
    这次读取的字节数 = 4
    rld!
    共读取了 2 次
BufferedInputStream
  • public class BufferedInputStream extends FilterInputStream
ObjectInputStream
  • public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-9.jpg)

  • 使用 ObjectInputStream 读取 data.dat 并反序列化恢复数据:

    注意:读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致

    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
    package com.f.chapter19.inputstream_;

    //为了让反序列化后的Dog类的对象能调用Dog类的方法,所以需要可以引用该Dog类
    import com.f.chapter19.outputstream_.Dog;

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;

    /**
    * @author fzy
    * @date 2023/7/11 20:51
    */
    public class ObjectInputStream_ {
    public static void main(String[] args) {
    //指定反序列化的文件
    String filePath = "C:\\Users\\1\\Code Project\\Java project\\file\\data.dat";
    ObjectInputStream objectInputStream = null;
    try {
    objectInputStream = new ObjectInputStream(new FileInputStream(filePath));

    //注意:读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
    System.out.println(objectInputStream.readInt());
    System.out.println(objectInputStream.readBoolean());
    System.out.println(objectInputStream.readChar());
    System.out.println(objectInputStream.readDouble());
    System.out.println(objectInputStream.readUTF());
    Object obj = null;
    try {
    obj = objectInputStream.readObject();
    } catch (ClassNotFoundException e) {
    throw new RuntimeException(e);
    }
    System.out.println(obj + " 的运行类型为 " + obj.getClass());
    Dog dog = (Dog) obj;
    System.out.println(dog.getName()); //调用Dog类的getName方法
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    objectInputStream.close();
    System.out.println("读取成功!");
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
    }

OutputStream

  • OutputStream 抽象类是所有字节输出流的父类:public abstract class OutputStream implements Closeable, Flushable
  • OutputStream 常用的子类:
    • FileOutputStream:文件输出流。
    • BufferedOutputStream:缓冲字节输出流。
    • ObjectOutputStream:对象字节输出流。
FileOutputStream
  • public class FileOutputStream extends OutputStream
  1. public void write(int b):将指定的字节写入此文件输出流,如果文件不存在,则创建该文件,下面的方法同理。

  2. public void write(byte b[]):将 b.length 个字节从指定的字节数组写入此文件输出流。

  3. public void write(byte b[], int off, int len):将 len 个字节从位于偏移量 off 的指定字节数组写入此文件输出流。

    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
    package com.f.chapter19.outputstream_;

    import java.io.FileOutputStream;
    import java.io.IOException;

    /**
    * @author fzy
    * @date 2023/7/8 14:41
    * 演示FileOutputStream的使用(字节输出流:程序 --> 文件)
    */
    @SuppressWarnings({"all"})
    public class FileOutputStream01 {
    public static void main(String[] args) {
    String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
    FileOutputStream fileOutputStream = null;
    try {
    //true表示在写入时是添加在文件末尾,默认为false
    fileOutputStream = new FileOutputStream(filePath);
    //fileOutputStream = new FileOutputStream(filePath, true);
    //1.`public void write(int b)`:将指定的字节写入此文件输出流。
    fileOutputStream.write('a');
    //2.`public void write(byte b[])`:将 `b.length` 个字节从指定的字节数组写入此文件输出流。
    String str = "hello!";
    fileOutputStream.write(str.getBytes()); //getBytes():将字符串转换为字符数组
    //3.`public void write(byte b[], int off, int len)`:将 `len` 个字节从位于偏移量 `off` 的指定字节数组写入此文件输出流。
    fileOutputStream.write(str.getBytes(), 0, str.length());
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    fileOutputStream.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
    }
    • 注意:在对磁盘中的文件创建 FileOutputStream 对象时,如果 FileOutputStream 构造器的 append 参数接收到的是 true,表示在写入时是添加在文件末尾,反之则是重写原来的文件。

      但是需要注意,在创建好了 FileOutputStream 对象后,每次调用该对象的 write 方法时,都是添加在已经写好的文件内容的后面,不是调用 write 方法一次,就重写文件一次。除非又对该文件创建了 FileOutputStream 对象。

BufferedOutputStream
  • public class BufferedOutputStream extends FilterOutputStream
ObjectOutputStream
  • public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C18-10.jpg)

  • 使用 ObjectOutputStream 序列化基本数据类型和一个 Dog 对象(name, age),并
    保存到 data.dat 文件中:

    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
    package com.f.chapter19.outputstream_;

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;

    /**
    * @author fzy
    * @date 2023/7/11 20:57
    */
    public class ObjectOutputStream_ {
    public static void main(String[] args) {
    //序列化后,保存的文件格式不是纯文本的,而是按照他的格式来保存的
    String filePath = "C:\\Users\\1\\Code Project\\Java project\\file\\data.dat";
    ObjectOutputStream objectOutputStream = null;
    try {
    //ObjectOutputStream是处理流
    objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));

    //序列化数据到文件中
    objectOutputStream.writeInt(100); //int --自动装箱--> Integer,Integer实现了 Serializable 接口
    objectOutputStream.writeBoolean(true); //原理同上 boolean -> Boolean
    objectOutputStream.writeChar('a'); //原理同上 char -> Character
    objectOutputStream.writeDouble(1.1); //原理同上 double -> Double
    objectOutputStream.writeUTF("Java"); //写入字符串是用 writeUTF
    objectOutputStream.writeObject(new Dog("小黄", 3)); //写入Dog对象
    } catch (IOException e) {
    throw new RuntimeException(e);
    } finally {
    try {
    objectOutputStream.close();
    System.out.println("数据保存完成(序列化形式)");
    } 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
    package com.f.chapter19.outputstream_;

    import java.io.Serializable;

    /**
    * @author fzy
    * @date 2023/7/11 21:41
    */
    //为了让Dog类对象能序列化地保存,让其实现Serializable接口
    public class Dog implements Serializable {
    private String name;
    private int age;

    public Dog(String name, int age) {
    this.name = name;
    this.age = age;
    }

    public String getName() {
    return name;
    }

    @Override
    public String toString() {
    return "Dog{" +
    "name='" + name + '\'' +
    ", age=" + age +
    '}';
    }
    }
PrintStream
  • public class PrintStream extends FilterOutputStream implements Appendable, Closeable

  • 字节打印流:

    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
    package com.f.chapter19.print_;

    import java.io.IOException;
    import java.io.PrintStream;

    /**
    * @author fzy
    * @date 2023/7/12 16:37
    */
    public class PrintStream_ {
    public static void main(String[] args) throws IOException {
    PrintStream out = System.out;
    //在默认情况下,PrintStream 输出数据的位置是标准输出,即显示器
    /*public void print(String s) {
    if (s == null) {
    s = "null";
    }
    write(s);
    }*/
    out.print("hello,world!\n");
    // 因为print最后调用的是write方法,所以也可以直接调用write方法进行输出
    out.write("hi!".getBytes());

    //可以修改打印流输出的位置, 修改为到 hi.txt 文件中
    /*public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out); //native方法,修改了out
    }*/
    System.setOut(new PrintStream("C:\\Users\\1\\Code Project\\Java project\\file\\hi.txt"));
    System.out.println("hi,txt!");

    out.close(); //关闭流
    }
    }
---------------The End---------------