Java I/O框架

90 阅读7分钟

Java I/O框架

File 类

代表物理盘符中的一个文件或文件夹。

常用方法

⽅法名描述
createNewFile()创建⼀个新⽂件。
mkdir()创建⼀个新⽬录。
delete()删除⽂件或空⽬录。
exists()判断File对象所对象所代表的对象是否存在。
getAbsolutePath()获取⽂件的绝对路径。
getName()取得名字。
getParent()获取⽂件/⽬录所在的⽬录。
isDirectory()是否是⽬录。
isFile()是否是⽂件。
length()获得⽂件的⻓度。
listFiles()列出⽬录中的所有内容。
renameTo()修改⽂件名为。

FileFilter 接口

FileFilter 文件过滤接口

  • boolean accept(File pathname)
  • 当调用File类中的listFiles()方法时,支持传⼊FileFilter接⼝接⼝实现类,对获取⽂件进⾏过滤,只有 > 满⾜条件的⽂件的才可出现在listFiles()的返回值中。
public static void directoryOperator() throws Exception {
        File file = new File("D:\ccc\aaa");

        if (!file.exists()) {
            boolean isCreateFile = file.mkdirs();
            System.out.println(isCreateFile);
        } else {
            System.out.println(file.getName() + "已存在");
        }

        // 删除
//        System.out.println(file.delete());
//        file.deleteOnExit();

        // 获取
        System.out.println("获取路径" + file.getPath());
        System.out.println("获取绝对路径" + file.getAbsolutePath());
        System.out.println("获取文件名" + file.getName());
        System.out.println("=====================list()=====================");
        File file1 = new File("D:/");
        String[] stringList = file1.list();
        for (String string : stringList) {
            System.out.println(string);
        }
        System.out.println("=====================listFile()=====================");
        File[] files = file1.listFiles();
        for (File file2 : files) {
            System.out.println(file2.getName());
        }
        System.out.println("=====================FilenameFilter()=====================");
        
        // 使用了 FilenameFilter 接口
        File[] files1 = file1.listFiles((File dir, String name) -> name.equals("Game"));
        for (File file2 : files1) {
            System.out.println(file2);
        }

递归显示文件夹

整体思路:获取到需要遍历的文件夹后,将目录中所有内容存入到File数组中。遍历数组,如果被遍历的是文件夹则递归遍历,否则打印文件名。

public static void main(String[] args) {
    recursionErgodic(new File("src\cn\john"), 0);
}

// 递归遍历
public static void recursionErgodic(File dir, int level) {
    File[] files = dir.listFiles();
    System.out.println(printSpace(level) + dir.getName());
    level++;
    if (files != null) {
        // 遍历
        for (File file : files) {
            if (file.isDirectory()) {
                recursionErgodic(file, level);
            } else {
                System.out.println(printSpace(level) + file.getName());
            }
        }
    }
}

public static String printSpace(int level) {
    return ("\t" + "|\").repeat(Math.max(0, level));
}

递归删除文件夹

整体思路:获取到需要遍历的文件夹后,将目录中所有内容存入到File数组中。遍历数组,如果被遍历的是文件夹则递归遍历删除文件及文件夹,最终将最外层文件夹删除。

public static void recursionDelete(File dir) {
    File[] files = dir.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isDirectory()) {
                recursionDelete(file);
                System.out.println(file.getName() + "->" + file.delete());
            } else {
                System.out.println(file.getName() + "->" + file.delete());
            }
        }
    }
    System.out.println(dir.getName() + "->" + dir.delete());
}

内存与存储设备之间传输数据的通道 ⽔借助管道传输;数据借助流传输。

流的分类

按方向

  • 输入流:将存储设备中的内容读入到内存中。
  • 输出流:将内存中的内容写到存储设备中。

按单位

  • 字节流:以字节为单位,可以读写所有数据。
  • 字符流:以字符为单位,只能读写⽂本数据。

按功能

  • 节点流:具有实际传输数据的读写功能。
  • 过滤流:在节点流的基础上增强功能。

节点流和过滤流都是Java中的输入流和输出流。区别在于,节点流是直接连接到数据源或目标的流,例如FileInputStream和FileOutputStream,它们可以直接操作文件。而过滤流则是建立在节点流的基础上,可以对节点流进行包装并提供额外的功能。例如,DatalnputStream和DataOutputStream可以读写基本数据类型, BufferedReader和BufferedWriter可以提供缓冲功能和按行读写等。因此,过滤流通常是节点流的补充和增强。

字节流

抽象类

  • InputStream:字节输入流
    • public int read(){}
    • public int read(byte[] b){}
    • public int read(byte[] b, int off, int len){}
  • OutputStream:字节输出流
    • public int write(){}
    • public int write(byte[] b){}
    • public int write(byte[] b, int off, int len){}

字节节点流

  • FileInputStream:文件字节输入流
    • public int read(byte[] b)
      • 一次写多个字节,将b数组中所有字节,写入输入流。
  • FileOutputStream:文件字节输出流
    • public int write(byte[] b)
      • 从流中读取多个字节,将读到内容存⼊b数组,返回实际读到的字节数。
      • 如果达到文件的尾部,则返回**-1**。
// 字节输入流
public static void main(String[] args) throws Exception {
    InputStream fis = new FileInputStream(new File("bcd.txt"));
    // 读取单个字节
    /*int data = fis.read();
    System.out.println(data);*/
    // 单字节循环读
    int len;
    while ((len = fis.read()) != -1) {
        System.out.print((char) len + " ");
    }
    // 读取多字节
    /*byte[] buffer = new byte[10];
    while ((len = fis.read(buffer)) != -1) {
        String s = new String(buffer, 0, len);
        System.out.println(s);
    }*/
    fis.close();
}
// 字节输出流
public static void main(String[] args) throws Exception {
        OutputStream fos = new FileOutputStream(new File("bcd.txt"), true);

//        fos.write(97); // a

        String s = "测\r\n";
        for (int i = 0; i < 3; i++) {
            fos.write(s.getBytes());
            System.out.println(Arrays.toString(s.getBytes()));
        }

        byte[] te = {-26, -75, -117};
        fos.write(te);


        fos.close();
        System.out.println("写入完成");
    }

字节缓冲流

  • BufferedOutputStream:字节输出缓冲流
  • BufferedInputStream:字节输入缓冲流
    • 提⾼IO效率,减少访问磁盘的次数。
    • 数据存储在缓冲区中,flush是将缓冲区的内容写⼊⽂件中,也可以直接close。
// 字节输入缓冲流
public static void main(String[] args) throws Exception{
    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("bcd.txt"));

    int data;
    while ((data = bufferedInputStream.read()) != -1) {
        System.out.println(data);
    }

    bufferedInputStream.close();
}
// 字节输出缓冲流
public static void main(String[] args) throws Exception{
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bcd.txt"));

    String s = "疑是地上霜";
    bos.write(s.getBytes());

    bos.close();
}

对象流

  • ObjectInutStream/ObjectOutputStream
    • 增强了缓冲区功能
    • 增强了读写8种基本数据类型和字符串功能
    • 增强了读写对象的功能
      • readObject():从流中读取一个对象
      • writeObject(Object obj):向流中写入一个对象
序列化

使用流传输对象的过程称为序列化和反序列化

  1. 序列化:把对象写入到硬盘或网络中的过程。
  2. 反序列化:把硬盘或网络中的二进制文件读取到内存形成对象的过程。
  3. 对象序列化的细节
    • 序列化类及其对象属性必须实现Serializable接口。
    • transient修饰为临时属性,不参与序列化。
    • 读取到文件尾部的标志:java.io.EOFException。
    • 使用serialVersionUID属性保证序列化的类和反序列化的类是同一个类。
public class TestStudent {
    public static void main(String[] args) {
        try {
            serializableResult();
//            serializableView();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 序列化
    public static void serializableResult() throws Exception {
        Student xm = new Student(3212, "john", 22);
        Student xh = new Student(3214, "jack", 22);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.bin"));

        // 1. 2.
//        oos.writeObject(xm);
//        oos.writeObject(xh);

        ArrayList<Student> students = new ArrayList<>();
        students.add(xm);
        students.add(xh);

        oos.writeObject(students);

        oos.close();
    }

    // 反序列化
    public static void serializableView() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.bin"));

        // 1.
//        Object xm = ois.readObject();
//        Object xh = ois.readObject();
//
//        System.out.println(xm);
//        System.out.println(xh);

        // 2.
//        try {
//            while (true) {
//                Object student = ois.readObject();
//                System.out.println(student);
//            }
//        } catch (EOFException e) { // 通过捕获EOFException(读到文件尾的标志) -> 在不知道别人序列化存入几个对象的情况
//            System.out.println("读取完毕");
//        }

        // 3.
        ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
        System.out.println(students.toString());

        ois.close();
    }
}

字符编码

编码说明
ISO-8859-1收录除ASCII外,还包括⻄欧、希腊语、泰语、阿拉伯语、希伯来语对应的⽂字符号。
UTF-8针对Unicode的可变⻓度字符编码。
GB2312简体中⽂。
GBK简体中⽂、扩充。
BIG5台湾,繁体中⽂。

字符流

抽象类

  • Reader:字符输入流
    • public int read(){}
    • public int read(char\[] c){}
    • public int read(char\[] b, int off, int len){}
  • Writer:字符输出流
    • public void write(int n){}
    • public void write(String str){}
    • public void write(char\[] c){}

字符节点流

  • FileWriter:
    • public void write(String str)
    • ⼀次写多个字符,将b数组中所有字符,写⼊输出流。
  • FileReader:
    • public int read(char[] c)
    • 从流中读取多个字符,将读到内容存⼊c数组,返回实际读到的字符数;如果达到⽂件的尾部,则返回-1。
public static void main(String[] args) throws Exception {
    Reader fr = new FileReader(new File("fw.txt"));

    // 1. 
    char[] buf = new char[1024];
    int len;
    while ((len = fr.read(buf)) != -1) {
        String s = new String(buf, 0, len);
        System.out.println(s);
    }

    // 2. 
    int data;
    while ((data = fr.read()) != -1) { // 25925 20065 30340 27185 33457
        System.out.print((char) data); // 230 149 133
    }
}
public static void main(String[] args) throws Exception {
    FileWriter fw = new FileWriter(new File("fw.txt"));

    // 1. 
    String s = "故乡的樱花";
    fw.write(s);

    // 2.
    char[] c = {'故', '乡', '的', '樱', '花'};
    fw.write(c);
    fw.close();
}

字符缓冲流

BufferedWriter/BufferedReader

  • 支持输入换行符
  • 可一次写一行、读一行
方法说明
readLine()读一行
newLine()换行
public static void main(String[] args) throws Exception {
    BufferedReader br = new BufferedReader(new FileReader("fw.txt"));

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

    br.close();
}
public static void main(String[] args) throws Exception {
    BufferedWriter bw = new BufferedWriter(new FileWriter("fw.txt", false));

    String data = "中国";
    for (int i = 0; i < 5; i++) {
        bw.write(data);
        bw.newLine();
    }

    bw.close();
}

打印流

PrintWriter

  • 封装了print() / println()⽅法,⽀持写⼊后换⾏。
  • ⽀持数据原样打印。
// PrintStream和PrintWriter的使用
public static void main(String[] args) throws Exception {
    PrintStream ps = new PrintStream("fw.txt");
    PrintWriter pw = new PrintWriter("fw.txt");

    ps.write(97); // 打印a
    ps.println(97); // 打印97

    pw.write(97);
    pw.println(97);

    ps.flush();

    PrintStream ps2 = System.out;
    ps2.println(97);
    ps2.write(97);

    ps2.flush();
}
// 设置out默认输出不为控制台 -> bcd.txt
// System.out 返回 PrintStream,但不能直接将打印流赋值给System.out,需要用setOut方法
public static void main(String[] args) throws Exception {
    System.setOut(new PrintStream("bcd.txt"));
    System.out.println("97");
    System.out.println(97);
}
// System.in返回InputStream
public static void main(String[] args) throws Exception {
    InputStream is = System.in;
    BufferedReader br = new BufferedReader(new InputStreamReader(is));

    System.out.println("请输入姓名");
    String name = br.readLine();
    System.out.println("请输入年龄");
    String SAge = br.readLine();
    int age = Integer.parseInt(SAge);
    System.out.println("姓名:" + name + ",年龄:" + (age + 10));
}

转换流

InputStreamReader/OuputStreamWriter

  • 可将字节流转换为字符流
  • 可设置字符的编码方法
public static void main(String[] args) throws Exception {
    FileInputStream fis = new FileInputStream("fw.txt");
    BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8));

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

    br.close();
}
public static void main(String[] args) throws Exception {
    FileOutputStream fos = new FileOutputStream("fw.txt");
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8));

    String data = "故宫";
    for (int i = 0; i < 3; i++) {
        bw.write(data);
        bw.newLine();
    }

    bw.close();
}

Properties 集合

Properties:属性集合

特点:

  • 存储属性名和属性值
  • 属性名和属性值都是字符串类型
  • 没有泛型
  • 和流有关
public static void main(String[] args) throws Exception {
    Properties properties = new Properties();

    properties.setProperty("name", "john");
    properties.setProperty("age", "18");
    properties.setProperty("gender", "boy");

    properties.list(System.out);
    properties.store(new FileOutputStream("bcd.txt"), "john个人信息");

    Properties properties1 = new Properties();
    properties1.load(new FileInputStream("bcd.txt"));
    properties1.list(System.out);
}