zoukankan      html  css  js  c++  java
  • Java文件I/O简单介绍

    一、File类

    java.io.File类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

    1.1 构造方法

    public File(String pathname); // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
    public File(String parent, String child); // 从父路径名字符串和子路径名字符串创建新的 File实例。
    public File(File parent, String child); // 从父抽象路径名和子路径名字符串创建新的 File实例。
    

    1.2 常用方法

    /// API
    // 获取功能
    public String getAbsolutePath(); // 返回此File的绝对路径名字符串。
    public String getPath(); // 将此File转换为路径名字符串。
    public String getName(); // 返回由此File表示的文件或目录的名称。
    public long length(); // 返回由此File表示的文件的长度。若File对象表示目录,则返回值未指定。
    // 判断功能
    public boolean exists(); // 此File表示的文件或目录是否实际存在。
    public boolean isDirectory(); // 此File表示的是否为目录。
    public boolean isFile(); // 此File表示的是否为文件。
    // 创建删除功能
    public boolean createNewFile();  // 当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    public boolean delete(); // 删除由此File表示的文件或目录。
    public boolean mkdir(); // 创建由此File表示的目录。
    public boolean mkdirs(); // 创建由此File表示的目录,包括任何必需但不存在的父目录
    // 目录的遍历
    public String[] list(); // 返回一个String数组,表示该File目录中的所有子文件或目录。
    public File[] listFiles(); // 返回一个File数组,表示该File目录中的所有的子文件或目录。
    

    1.3 例子

    • 递归打印多级目录
    public static void printDir(File dir) {
        // 获取子文件和目录
        File[] files = dir.listFiles();
        // 循环打印
        for (File file : files) {
            // 判断
            if (file.isFile()) {
                // 是文件,输出文件绝对路径
                // if (file.getName().endsWith(".java")) // 筛选后缀是 .java 的文件
                System.out.println("文件名:" + file.getAbsolutePath());
            } else {
                // 是目录,输出目录绝对路径
                System.out.println("目录:" + file.getAbsolutePath());
                // 继续遍历,调用printDir,形成递归
                printDir(file);
            }
        }
    }
    
    • 使用文件过滤器java.io.FileFilter接口来筛选文件:
    public static void printDir2(File dir) {
        // Lambda表达式优化
        // File[] files = dir.listFiles(f -> f.getName().endsWith(".java") || f.isDirectory());
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".java") || pathname.isDirectory();
            }
        });
        // 循环打印
        for (File file : files) {
            // 判断
            if (file.isFile()) {
                System.out.println("文件名:" + file.getAbsolutePath());
            } else {
                printDir2(file);
            }
        }
    }
    

    java 7 引入了一些新的文件处理类用来代替 File 类的文件 I/O 操作方式:Java NIO系列教程

    import java.nio.file.DirectoryStream;
    import java.nio.file.FileSystem;
    import java.nio.file.FileSystems;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.attribute.FileAttribute;
    import java.nio.file.attribute.PosixFilePermission;
    import java.nio.file.attribute.PosixFilePermissions;
    

    二、基础I/O:字节流、字符流

    I/O 顶级父类们
    输入流 输出流
    字节流 InputStream OutputStream
    字符流 Reader Writer

    2.1 字节流

    一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节。所以字节流可以传输任意文件数据。

    2.1.1 字节输出流 OutputStream

    java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

    public void close();
    public void flush();
    public void write(byte[] b);
    public void write(byte[] b, int off, int len);
    public abstract void write(int b);
    

    2.1.2 FileOutputStream类

    java.io.FileOutputStream类是文件输出流,用于将数据写出到文件。

    public FileOutputStream(File file);
    public FileOutputStream(String name);
    
    1. 写出字节:write 方法;
    2. 每次程序运行,创建输出流对象,都会清空目标文件中的数据。构造方法还可以传入一个boolean append参数,表示追加写。
    3. 写出换行:fos.write(" ".getBytes());

    2.1.3 字节输入流 InputStream

    java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

    public void close();
    public abstract int read();
    public int read(byte[] b);
    

    2.1.4 FileInputStream类

    java.io.FileInputStream类是文件输入流,从文件中读取字节。

    FileInputStream(File file);
    FileInputStream(String name);
    
    1. 读取字节:read 方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回 -1;
    2. 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回 -1。

    2.2 字符流

    一些字符如中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

    2.2.1 字符输出流 Writer

    java.io.Writer抽象类是表示用于写出字符流的所有类的超类。

    void write(int c);
    void write(char[] cbuf);
    abstract void write(char[] cbuf, int off, int len);
    void write(String str);
    void write(String str, int off, int len);
    void flush();
    void close();
    

    2.2.2 FileWriter类

    java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

    FileWriter(File file);
    FileWriter(String fileName);
    
    1. 写出字符:write 方法;
    2. flush :刷新缓冲区;
    3. close :先刷新缓冲区,然后通知系统释放资源。

    字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流

    2.2.3 字符输入流 Reader

    java.io.Reader抽象类是表示用于读取字符流的所有类的超类。

    public void close();
    public int read();
    public int read(char[] cbuf);
    

    2.2.4 FileReader类

    java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

    FileReader(File file);  
    FileReader(String fileName);
    
    1. 读取字符: read() 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1;
    2. 使用字符数组读取: read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,
      读取到末尾时,返回 -1。

    当创建一个文件输入流对象时,若没有该文件,会抛出FileNotFoundException;创建文件输出流对象时,若没有该文件,会自动创建。

    try-with-resource

    三、缓冲流、转换流、序列化流、打印流

    3.1 缓冲流

    缓冲流,也叫高效流,是对4个基本的FileXxx流的增强,所以也是4个流,按照数据类型分类。

    • 字节缓冲流:BufferedInputStreamBufferedOutputStream
    • 字符缓冲流:BufferedReaderBufferedWriter

    缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

    3.1.1 字节缓冲流

    构造方法:

    public BufferedInputStream(InputStream in); // 创建一个新的缓冲输入流。
    public BufferedOutputStream(OutputStream out); // 创建一个新的缓冲输出流。
    

    字节缓冲流读写方法与基本的流是一致的。

    3.1.2 字符缓冲流

    构造方法

    public BufferedReader(Reader in); // 创建一个新的缓冲输入流。
    public BufferedWriter(Writer out); // 创建一个新的缓冲输出流。
    

    字符缓冲流的基本方法与普通字符流调用方式一致。特有方法:

    /// BufferedReader:
    public String readLine(); // 读一行文字
    /// BufferedWriter:
    public void newLine(); // 写一行行分隔符,由系统属性定义符号
    

    3.2 转换流

    3.2.1 InputStreamReader类

    转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

    InputStreamReader(InputStream in); // 创建一个使用默认字符集的字符流。
    InputStreamReader(InputStream in, String charsetName); // 创建一个指定字符集的字符流
    

    3.2.1 OutputStreamWriter类

    转换流java.io.OutputStreamWriter,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

    OutputStreamWriter(OutputStream in); // 创建一个使用默认字符集的字符流。
    OutputStreamWriter(OutputStream in, String charsetName); // 创建一个指定字符集的字符流。
    

    3.3 序列化

    Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

    反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据对象的类型对象中存储的属性信息,都可以用来在内存中创建对象。

    3.3.1 ObjectOutputStream类

    java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
    构造方法:

    public ObjectOutputStream(OutputStream out); // 创建一个指定OutputStream的ObjectOutputStream
    
    /// 示例:
    FileOutputStream fileOut = new FileOutputStream("employee.txt");
    ObjectOutputStream out = new ObjectOutputStream(fileOut);
    
    1. 一个对象要想序列化,必须满足两个条件
    • 该类必须实现java.io.Serializable接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
    • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰
    1. 写出对象方法
    public final void writeObject (Object obj); // 将指定的对象写出
    

    3.3.2 ObjectInputStream类

    java.io.ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

    构造方法:

    public ObjectInputStream(InputStream in); // 创建一个指定InputStream的ObjectInputStream。
    

    如果能找到一个对象的class文件,我们可以进行反序列化操作,调用 ObjectInputStream 读取对象的方法:

    public final Object readObject (); // 读取一个对象
    

    对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个ClassNotFoundException异常。

    另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。 发生这个异常的原因如下:

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
    • 该类包含未知数据类型
    • 该类没有可访问的无参数构造方法

    Serializable接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配。示例:

    public class Employee implements java.io.Serializable {
        // 加入序列版本号
        private static final long serialVersionUID = 1L;
        public String name;
        public String address;
        // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值
        public int eid;
        
        public void addressCheck() {
            System.out.println("Address check : " + name + " ‐‐ " + address);
        }
    }
    

    3.4 打印流

    平时我们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

    3.4.1 PrintStream类

    构造方法:

    public PrintStream(String fileName);
    public PrintStream(File file);
    public PrintStream(OutputStream out);
    
    • System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。
      我们可以改变它的流向:
    public class PrintDemo {
        public static void main(String[] args) throws IOException {
            // 调用系统的打印流,控制台直接输出97
            System.out.println(97);
            // 创建打印流,指定文件的名称
            PrintStream ps = new PrintStream("ps.txt");
            // 设置系统的打印流流向,输出到ps.txt
            System.setOut(ps);
            // 调用系统的打印流, ps.txt中输出97
            System.out.println(97);
        }
    }
    

    另,三篇讲得较全面的博客:

    博客园:这些年一直记不住的 Java I/O

    简书:看完这个,Java IO从此不在难

    CSDN:Java8 I/O源码-整体结构

  • 相关阅读:
    win7-64位,vs32位,odbc 连接oracle问题总结
    vscode 格式化代码
    vue 自动切换导航图
    Unexpected console statement
    css flex 布局之---骰子
    vue百度地图在IE11下空白
    css使用font-face
    centos7计划任务
    MySQL(Mariadb)主从同步基础
    Ubuntu(16.04) 常见问题
  • 原文地址:https://www.cnblogs.com/caophoenix/p/12619784.html
Copyright © 2011-2022 走看看