IO流

85 阅读11分钟

File

File对象

微信截图_20240729214610.png

File表示什么?

File对象表示路径,可以是文件、也可以是文件夹。

绝对路径和相对路径是什么意思?
  • 绝对路径带盘符
  • 相对路径是不带盘符的,默认到当前项目下去找。
File三种构造方法的作用?
方法名作用
public File(String pathname)把字符串表示的路径变成File对象
public File(String parent,String child)把父路径和子路径进行拼接
public File(File parent, String child)把父路径和子路径进行拼接

代码示例

public class TestFile {
    public static void main(String[] args) {
        //绝对路径
        File file1 = new File("C:\Users\ZG\Desktop\file.txt");
        File file2 = new File("C:\Users\ZG\Desktop", "file.txt");
        File file3 = new File("C:\Users\ZG\Desktop");
        File file4 = new File(file3, "file.txt");
        //相对路径
        File file5 = new File("io\test01.txt");
        System.out.println(file1);
        System.out.println(file2);
        System.out.println(file4);
        System.out.println(file5);
    }
}

File常见成员方法

微信截图_20240729221646.png

微信截图_20240729221927.png

代码示例

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

        //windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
        //mkdir只能创建单级文件夹,不能创建多级文件夹
        File file1 = new File("io\doc");
        boolean mkdir = file1.mkdir();
        System.out.println(mkdir);
        //mkdirs创建多级文件夹
        File file2 = new File("io\mkdirs\doc");
        boolean mkdirs = file2.mkdirs();
        System.out.println(mkdirs);
        //如果当前路径表示的文件是不存在的,则创建成功,方法返回true
        //如果当前路径表示的文件是存在的,则创建失败,方法返回false
        //如果父级路径不存在,那么方法会抛出IOException
        //createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
        File file3 = new File("io\doc\test01.txt");
        boolean newFile = file3.createNewFile();
        System.out.println(newFile);
        //如果删除的是文件,则直接删除,不走回收站
        //如果删除的是文件夹,则直接删除,不走回收站
        //如果删除的是有内容的文件夹,则删除失败
        File file4 = new File("io\doc\test01.txt");
        boolean delete = file4.delete();
        System.out.println(delete);
    }
}

微信截图_20240729221151.png

  • 当调用者File表示的路径不存在时,返回null
  • 当调用者File表示的路径是文件时,返回null
  • 当调用者File表示的路径是空文件夹时,返回一个长度为0的数组
  • 当调用者File表示的路径是一个有内容的文件夹时,将里面所有的文件夹和文件的路径放在File数组中返回
  • 当调用者File表示的路径是一个有隐藏文件的文件夹时,将里面所有的文件夹和文件的路径放在File数组中放回,包含隐藏文件
  • 当调用者File表示的路径是需要权限才能访问的文件夹时,返回null

代码示例

public class TestFile {
    public static void main(String[] args) {
        File file = new File("F:\新建文件夹");
        File[] files = file.listFiles();
        for (File file1 : files) {
            System.out.println(file1);
        }
    }
}

File练习

创建一个文件
public class TestFile {
    public static void main(String[] args) throws IOException {
        //创建文件
        File file1 = new File("io\doc");
        file1.mkdirs();
        File file2 = new File(file1, "test.txt");
        boolean newFile = file2.createNewFile();
        if (newFile) {
            System.out.println("创建成功");
        } else {
            System.out.println("创建失败");
        }
    }
}
查询某一个文件夹中是否有txt文件
public static boolean haveTxt(File file) {
    if (!file.exists()) {
        return false;
    }

    if (file.isFile() && file.getName().endsWith(".txt")) {
        return true;
    }

    File[] files = file.listFiles(pathname -> pathname.isFile() && pathname.getName().endsWith(".txt"));
    return files != null && (files.length > 0);
}
查询电脑上所有的xlsx文件
public class TestFile {
    public static void main(String[] args) throws IOException {
        File[] files = File.listRoots();
        for (File file : files) {
            findXlsx(file);
        }
    }

    public static void findXlsx(File file) {
        File[] files = file.listFiles();
        if (files != null) {
            for (File file1 : files) {
                if (file1.isFile() && file1.getName().endsWith(".xlsx")) {
                    System.out.println(file1);
                } else {
                    findXlsx(file1);
                }
            }
        }
    }
}
删除一个多级文件夹
public class TestFile {
    public static void main(String[] args) throws IOException {
        File file = new File("F:\新建文件夹 (2)");
        delete(file);
    }

    public static void delete(File file) {
        File[] files = file.listFiles();
        if (files != null) {
            for (File file1 : files) {
                if (file1.isFile()) {
                    file1.delete();
                } else {
                    delete(file1);
                }
            }
        }
        file.delete();
    }
}
统计文件夹大小
public class TestFile {
    public static void main(String[] args) throws IOException {
        File file = new File("F:\新建文件夹\掘金素材");
        System.out.println(count(file));
    }

    public static long count(File file) {
        long len = 0;
        File[] files = file.listFiles();
        if (files != null) {
            for (File file1 : files) {
                if (file1.isFile()) {
                    len += file1.length();
                } else {
                    len += count(file1);
                }
            }
        }
        return len;
    }
}

IO流

IO流分类

微信截图_20240730091625.png

什么是IO流?
  • 存储和读取数据的解决方案
  • I:input O:output
  • 流:像水流一样传输数据
IO流的作用?

用于读写数据(本地文件、网络)

IO流按照流向可以分为哪两种流/
  • 输出流:程序->文件
  • 输入流:文件->程序
IO流按照操作文件的类型可以分为哪两种流?
  • 字节流:可以操作所有类型的文件
  • 字符流:只能操作纯文本数据
什么是纯文本文件?

用windows系统自带的记事本打开并且能读懂的文件。txt文件、md文件、xml文件、lrc文件等

字节流

微信截图_20240730092827.png

字节输出流 FileOutputStream

书写步骤:

  1. 创建字节输出流对象
  2. 写数据
  3. 释放资源

代码示例

public class TestIO {
    public static void main(String[] args) throws IOException {
        //创建字节输出流对象
        //参数是字符串表示的路径或者是File对象都是可以的
        //如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
        //如果已经存在,则会清空文件
        FileOutputStream fos = new FileOutputStream("io\doc\test01.txt");
        //write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符,57在ASCII上对应就是9
        fos.write(57);
        fos.write(57);
        //每次使用完流之后都要释放资源
        fos.close();
    }
}

微信截图_20240730094225.png

换行:

public class TestIO {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("io\doc\test01.txt");
        String str1 = "hello world";
        fos.write(str1.getBytes());
        String str2 = "\r\n";
        fos.write(str2.getBytes());
        String str3 = "china";
        fos.write(str3.getBytes());
        fos.close();
    }
}

续写:

public class TestIO {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("io\doc\test01.txt", true);
        String str1 = "hello world\r\n";
        fos.write(str1.getBytes());
        fos.close();
    }
}

字节输入流 FileIntputStream

书写步骤:

  1. 创建字节输入流对象
  2. 读取数据
  3. 释放资源

代码示例

public class TestIO {
    public static void main(String[] args) throws IOException {
        //文件不存在直接报错
        FileInputStream fis = new FileInputStream("io\doc\test01.txt");
        //一次读一个字节,读取出来的数据是在ASCII上对应的数字
        //读到文件末尾了,read方法返回-1
        int read;
        while ((read = fis.read()) != -1) {
            System.out.print((char) read);
        }
        //每次使用完流之后都要释放资源
        fis.close();
    }
}

文件复制:

public class TestIO {
    public static void main(String[] args) throws IOException {
        //文件复制,一次读一个字节写一个字节,效率低
        FileInputStream fis = new FileInputStream("io\doc\test01.txt");
        FileOutputStream fos = new FileOutputStream("io\doc\test02.txt");
        int read;
        while ((read = fis.read()) != -1) {
            fos.write(read);
        }
        fos.close();
        fis.close();
    }
}

IO流中不同JDK版本捕获异常的方式

基本用法:

public class TestIO {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("io\doc\test01.txt");
            fos = new FileOutputStream("io\doc\test02.txt");
            byte[] bytes = new byte[1024 * 1024 * 5];
            int len;
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDK7(自动释放资源):必须实现AutoCloseable接口

public class TestIO {
    public static void main(String[] args) {
        //自动释放资源
        try (FileInputStream fis = new FileInputStream("io\doc\test01.txt");
             FileOutputStream fos = new FileOutputStream("io\doc\test02.txt")) {

            byte[] bytes = new byte[1024 * 1024 * 5];
            int len;
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

JDK9(自动释放资源):必须实现AutoCloseable接口

public class TestIO {
    public static void main(String[] args) throws FileNotFoundException {
        //自动释放资源
        FileInputStream fis = new FileInputStream("io\doc\test01.txt");
        FileOutputStream fos = new FileOutputStream("io\doc\test02.txt");
        try (fis; fos) {
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符集

ASCII字符集、GBK字符集、Unicode字符集(万国码)

  • 在计算机中,任意的数据都是以二进制的形式进行存储的
  • 计算机中最小的存储单元是一个字节
  • ASCII字符集中,一个英文占一个字节
  • 简体中文版windows,默认使用GBK字符集
  • GBK字符集完全兼容ASCII字符集
    • 一个英文占一个字节,二进制的第一位是0
    • 一个中文占两个字节,二进制高位字节的第一位是1

Unicode字符集:

  • UTF-16编码规则:2-4个字节保存
  • UTF-32编码规则:固定使用4个字节保存
  • UTF-8编码规则:1-4个字节保存

UTF-8编码规则(二进制):

  • ASCII(英文):1个字节(0xxxxxxx)二进制第一位是0,转成十进制是正数
  • 简体中文:3个字节(1110xxxx 10xxxxxx 10xxxxxx)二进制第一位是1,转成十进制是负数

乱码:

  • 读取数据的时候未读完整个汉字
  • 编码和解码时的方式不统一
  • 解决方式:不要使用字节流读取文本文件,编码和解码时使用同一个码表,同一个编码方式

字符流

微信截图_20240730113301.png

微信截图_20240730112947.png

字符输入流 FileReader

read()无参读取:

public class TestIO {
    public static void main(String[] args) throws IOException {
        //创建对象并关联本地文件
        FileReader fr = new FileReader("io\doc\test01.txt");
        int ch;
        //使用无参构造读取文件
        //字符流的底层是字节流,默认也是一个一个字节读取的,
        //如果遇到中文就会一次读取多个,GBK一次读取2个字节UTF-8一次读取3个字节
        //在读取之后,方法的底层还会进行解码并转成10进制,并最终把10进制作为返回值,这个10进制也表示在字符集上的数字
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        //关闭流
        fr.close();
    }
}

read(chars)有参读取:

public class TestIO {
    public static void main(String[] args) throws IOException {
        //创建对象并关联本地文件
        FileReader fr = new FileReader("io\doc\test01.txt");
        char[] chars = new char[2];
        int len;
        //读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
        while ((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars,0,len));
        }
        //关闭流
        fr.close();
    }
}

字符输出流 FileWriter

微信截图_20240731165112.png

微信截图_20240731165220.png

代码示例

public class TestIO {
    public static void main(String[] args) throws IOException {
        //开启续写
        FileWriter fileWriter = new FileWriter("io\doc\test01.txt", true);
        //写出一个字符,写出的是字符集上面对应的字符
        fileWriter.write(97);
        //写出一个字符串
        fileWriter.write("我爱你中国!");
        //写出一个字符串的一部分
        fileWriter.write("我爱你中国!", 0, 3);
        //写出一个字符数组
        char[] chars = {'我', '爱', '你', '西', '安'};
        fileWriter.write(chars);
        //写出一个字符数组的一部分
        fileWriter.write(chars, 0, 3);
        fileWriter.close();
    }
}

字符流底层原理

字符输入流底层原理

微信截图_20240731171403.png

字符输出流底层原理

微信截图_20240731171828.png

文件夹复制

public class TestIO {
    public static void main(String[] args) throws IOException {
        copyFile(new File("F:\新建文件夹"),new File("F:\001"));
    }


    public static void copyFile(File src, File dest) throws IOException {
        dest.mkdirs();
        File[] files = src.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (file.isFile()) {
                //复制文件
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
                int len;
                byte[] bytes = new byte[1024];
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.close();
                fis.close();
            } else {
                copyFile(file, new File(dest, file.getName()));
            }
        }
    }
}

缓冲流

字节缓冲流

微信截图_20240731181427.png

private static int DEFAULT_BUFFER_SIZE = 8192;

public BufferedInputStream(InputStream in, int size) {
    super(in);
    if (size <= 0) {
        throw new IllegalArgumentException("Buffer size <= 0");
    }
    buf = new byte[size];
}
public class BufferTest01 {
    public static void main(String[] args) throws IOException {

        //利用字节缓冲流复制文件
        //字节缓冲输入流,缓冲区默认为8192,可以根据有参构造继进行设置
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("io\test01.txt"));
        //字节缓冲输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("io\test02.txt"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bufferedInputStream.read(bytes)) != -1) {
            bufferedOutputStream.write(bytes, 0, len);
        }
        bufferedOutputStream.close();
        bufferedInputStream.close();
    }
}

字符缓冲流

字符缓冲输入流(特有方法readLine,一次读取一行)

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new FileReader("io\test01.txt"));
        //字符缓冲输入流特有方法,readLine一次读取一整行
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
        bufferedReader.close();
    }
}

字符缓冲输出流(特有方法newLine,跨平台换行)

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("io\test02.txt"));
        //字符缓冲输出流特有方法,newLine换行
        String content1 = "电视里看到教授副教授打开房间";
        String content2 = "丢失了多久";
        bufferedWriter.write(content1);
        bufferedWriter.newLine();
        bufferedWriter.write(content2);
        bufferedWriter.close();
    }
}

转换流

字节流转换为字符流

利用转换流按照指定编码读取

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        //字节流转换为字符流
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("io\gbk.txt"), Charset.forName("GBK"));
        int read;
        while ((read = inputStreamReader.read()) != -1) {
            System.out.println((char) read);
        }
        inputStreamReader.close();
    }
}
public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        //JDK11之后才出现
        FileReader fileReader = new FileReader("io\gbk.txt", Charset.forName("GBK"));
        int read;
        while ((read = fileReader.read()) != -1) {
            System.out.println((char) read);
        }
        fileReader.close();
    }
}

利用转换流按照指定的编码写入

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        //传统模式字节输出流包装成字符输出流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("io\gbk.txt",true), Charset.forName("GBK"));
        outputStreamWriter.write("我爱你!");
        outputStreamWriter.close();

        //JDK11之后
        FileWriter fileWriter = new FileWriter("io\gbk.txt", Charset.forName("GBK"),true);
        fileWriter.write("我爱你中国!");
        fileWriter.close();
    }
}

序列化流

序列化流

可以把java中的对象写到本地文件中,前提是javabean必须实现Serializable接口

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("io\student.txt"));
        Student student = new Student();
        student.setAge(20);
        student.setName("lisi");
        objectOutput.writeObject(student);
        objectOutput.close();
    }
}

反序列化流

可以把序列化到本地文件中的对象,读取到程序中来。

public class BufferTest01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("io\student.txt"));
        Object o = objectInputStream.readObject();
        System.out.println(o);
    }
}

序列化与反序列化的细节

微信截图_20240731220928.png

打印流

微信截图_20240731221337.png

字节打印流

微信截图_20240731221454.png

微信截图_20240731222027.png

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        PrintStream printStream = new PrintStream(new FileOutputStream("io\gbk.txt"), true, StandardCharsets.UTF_8);
        printStream.println(456);
        printStream.close();
    }
}

字符打印流

微信截图_20240731222441.png

微信截图_20240731222535.png

public class BufferTest01 {
    public static void main(String[] args) throws IOException {
        PrintWriter printWriter = new PrintWriter(new FileWriter("io\gbk.txt"), true);
        //打印一个字节,97对应的是a
        printWriter.write(97);
        //打印一个字符串
        printWriter.write("99");
        printWriter.close();
    }
}

总结

  • 有字节打印流和字符打印流两种
  • 打印流不操作数据源,只能操作目的地
  • 字节打印流,默认自动刷新,特有println自动换行
  • 字符打印流,自动刷新需要开启,特有println自动换行