IO框架

一、流的概念

内存与存储设备之间传输数据的通道叫做流。

注意:流创建后,还需要进行数据的传递,传递结束应该关闭通道(流)。关闭的代码应该写在finally中。

二、流的分类

2.1 按方向

参照物是内存。

输入流:从存储设备将数据输入到内存。读,Input

输出流:从内存中将数据输出到存储设备。写,Output

IO流是指输入输出流的简称。

2.2 按单位

字节流:能够操作所有的文件类型,以字节为单位传输。

字符流:只能操作文本,而且受到字符集限制(如果字符集不对会出现乱码),以字符为单位。

2.3 按功能

节点流:一般的传输数据的读写功能。

过滤流:在普通的节点流上做的增强功能。

三、字节流

3.1 抽象父类

InputStream:主要作用是读取(read)。

OutputStream:主要作用是写入(write)。

3.2 字节节点流

3.2.1 FileInputStream

FileInputStream:操作文件读取

  1. public class TestMain {
  2. public static void main(String[] args) {
  3. FileInputStream fis = null;
  4. try {
  5. fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
  6. int n;
  7. while((n = fis.read()) != -1) {
  8. System.out.print((char)n);
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }finally {
  13. if(fis != null) {
  14. try {
  15. fis.close();
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }
  21. }
  22. }
  1. try (
  2. // 定义变量,此处定义的变量下面的区域均有访问作用域
  3. // 在此处定义的变量必须具备有关闭的方法,会自动关闭
  4. FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
  5. // int n = 5; // 此代码不能在此处定义,因为没有实现自动关闭的接口
  6. ){
  7. int n;
  8. while((n = fis.read()) != -1) {
  9. System.out.print((char)n);
  10. }
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }

3.2.2 FileOutputStream

FileOutputStream:以字节的方式写入数据。

public class TestMain1 {
    public static void main(String[] args) {
        try (
                FileOutputStream fos = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
                ){
            String str = "Hello, world\n"; // 如果需要写入的时候换行,可以使用\n
            byte [] bytes = str.getBytes(); // 将字符串转成byte数组
            fos.write(bytes);
            fos.write(bytes);
            fos.flush(); // 刷新,清空系统缓冲区,目的将内存中的数据直接写到硬盘
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

注意:

1、FileOutputStream可以处理中文

2、FileOutputStream写入文件时,并非追加内容,而是覆盖。

3.2.3 复制文件

复制文件,指的是将一个文件先读取,然后写入到另一个文件中。

public class TestMain2 {
    // 复制内容
    public static void main(String[] args) {
        try (
                // 需要读取的文件
                FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\14.png");
                // 需要写入的文件,如果文件不存在会自动创建文件,但是不会创建中途的文件夹
                FileOutputStream fos = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\2.txt");
                ){
            byte [] buffer = new byte[1024]; // 创建一个1kb大小的空间,每次读写都操作1kb
            int len; // 每次读取的真正长度
            // 每次读取1024个字节,len记录每次能够读取到的字节数
            while((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len); // 将读取的内容从0开始写,写入真正的内容长度
            }
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3 字节过滤流

3.3.1 缓冲流

减少磁盘IO操作,提高性能。

缓冲区中的内容写入文件:

  • flush方法
  • 关闭流
  • 缓冲区满时会自动写入
public class TestMain3 {
    public static void main(String[] args) {
        try (
                FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
                BufferedInputStream bis = new BufferedInputStream(fis);
                ){
            int n;
            bis.skip(3); // 跳过部分,进行读取
            while((n = bis.read()) != -1) {
                System.out.print((char)n);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class TestMain4 {
    public static void main(String[] args) {
        try (
                FileOutputStream fis = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
                BufferedOutputStream bis = new BufferedOutputStream(fis);
                ){
            String str = "hello, world\n";
            bis.write(str.getBytes());
            bis.write(str.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.2 对象流

主要作用是将对象写入文件,以及将文件中保存的对象读取到内存。

注意:要将对象写入文件或者从文件中读取,需要对象所对应的类实现序列化接口。

将对象的属性应该如何解析告知操作的流,叫做对象序列化。

在Java中对象序列化比较简单,只需要实现序列化接口即可。无需实现任何方法。

public class Student implements java.io.Serializable{
    private int id;
    private String name;
    private String sex;
    private int age;

    // 省略getter和setter
}

对象流写入文件:

public class TestMain5 {
    public static void main(String[] args) {
        try (
                ObjectOutputStream fis = new ObjectOutputStream(new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt"));
                ){
            Student stu = new Student(1, "张三", "男", 20);
            fis.writeObject(stu);
            fis.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

对象流读取文件:

public class TestMain6 {
    public static void main(String[] args) {
        try (
                ObjectInputStream fis = new ObjectInputStream(new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt"));
                ){
            Student stu = (Student)fis.readObject();
            System.out.println(stu);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.3 序列化UID

标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。

序列化即告诉对象流如何写入一个对象。

反序列化即告诉对象流如何读取一个对象。

public class Student implements java.io.Serializable{
    private static final long serialVersionUID = -840791652073023431L;
    private int id;
    private String name;
    private String sex;
    private int age;

    // 省略getter和setter
}

如果写入的时候版本号为:-840791652073023430L, 但是读取时版本号已经改为-840791652073023431L,那么读取时会出现异常:java.io.InvalidClassException: com.qf.day21_IO.Student; local class incompatible: stream classdesc serialVersionUID = -840791652073023430, local class serialVersionUID = -840791652073023431

3.3.4 transient关键字

用来修饰属性。表示在序列化和反序列化时忽略该属性。

public class Student implements java.io.Serializable{

    private static final long serialVersionUID = -840791652073023431L;

    private int id;
    private String name;
    private transient String sex; // 忽略属性,不进行序列化和反序列化
    private int age;

    // 省略getter和setter
}

四、字符流

4.1 抽象父类

Reader:可以操作char型数组

Writer:可以操作char型数组,可以直接操作字符串。

4.2 字符节点流

FileReader:文本文件读取

public class TestMain7 {
    public static void main(String[] args) {
        try (
                FileReader reader = new FileReader("C:\\Users\\wangliang\\Desktop\\1.txt");
                ){
            char [] buffer = new char[1024];
            int len;
            while((len = reader.read(buffer))!=-1) {
                String str = new String(buffer, 0, len);
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

FileWriter:文本文件写入

public class TestMain8 {
    public static void main(String[] args) {
        try (
                FileWriter writer = new FileWriter("C:\\Users\\wangliang\\Desktop\\1.txt");
                ){
            String str = "标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。\n序列化即告诉对象流如何写入一个对象。";
            writer.write(str);
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.3 字符过滤流

4.3.1 缓冲流

字符缓冲流是文本文档操作使用最多的流。能够直接操作一行,读取一行,写入一行,支持换行符等。

BufferedReader:读取文本文件,支持按行读取

public class TestMain9 {
    public static void main(String[] args) {
        try (
                BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\wangliang\\Desktop\\1.txt"));
                ){
            String str;
            // 读取一行字符串
            while((str = reader.readLine()) != null) {
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

BufferedWriter:写入文本文件,支持按行写入,支持换行。

public class TestMain10 {
    public static void main(String[] args) {
        try (
                BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\wangliang\\Desktop\\1.txt"));
                ){
            String str = "标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。";
            writer.write(str);
            writer.newLine(); // 写入换行
            writer.write("序列化即告诉对象流如何写入一个对象。");
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.3.2 PrintWriter

封装了print()和println()这些打印的方法,支持原样打印。

五、转换流

作用是将字节流转换字符流。

public class TestMain11 {
    public static void main(String[] args) {
        try (
                // 将字节流转换成字符流
                FileInputStream fis = new FileInputStream(""); // 创建一个字节流
                InputStreamReader isr = new InputStreamReader(fis, "utf-8"); // 创建一个转换流
                BufferedReader reader = new BufferedReader(isr); // 创建一字符流
                ){
            String str;
            while((str = reader.readLine()) != null) {
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class TestMain12 {
    public static void main(String[] args) {
        try (
                // 将字节流转换成字符流
                FileOutputStream fos = new FileOutputStream(""); // 创建一个字节流
                OutputStreamWriter osr = new OutputStreamWriter(fos, "utf-8"); // 创建一个转换流
                BufferedWriter writer = new BufferedWriter(osr); // 创建一字符流
                ){
            writer.write("床前明月光");
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:在项目中,可以需要定义一些通用方法,此时,为了通用,会将所有文件操作定义成字节流,但是如果操作的文件在明知是文本文件的情况下,需要将得到字节流转换成字符流,此时就需要使用转换流。

六、File

6.1 File类

File类代表磁盘上的一个文件或文件夹。

常见方法:

public class TestMain13 {
    // 文件操作
    public static void main(String[] args) {
        try {
            File file = new File("C:\\Users\\wangliang\\Desktop\\1.txt");
            file.createNewFile(); // 创建一个文件
            File file1 = new File("C:\\Users\\wangliang\\Desktop\\1\\2\\3");
            file1.mkdir(); // 创建一个文件夹,只能在上层文件夹存在情况下创建,否则无法创建
            file1.mkdirs();// 创建一个文件夹,但是如果路径中有上层文件夹不存在,会连上层一起创建,可以创建多层文件夹
            file1.delete(); // 删除文件或空文件夹
            System.out.println(file.exists()); // 判断一个文件或文件夹是否存在
            System.out.println(file1.getAbsolutePath()); // 得到文件或文件夹的绝对路径
            System.out.println(file1.getName()); // 得到当前文件或文件夹的名称
            System.out.println(file.getParent()); // 得到上层文件夹的路径名称
            file.getParentFile(); // 得到上层文件夹File对象
            System.out.println(file.isDirectory()); // 判断file是否文件夹
            System.out.println(file.isFile()); // 判断file是否文件
            System.out.println(file.length()); // 得到文件的大小
            file.list(); // 得到文件夹中的所有内容的路径
            file.listFiles();// 得到文件夹中所有的File对象
            file.renameTo(new File("C:\\Users\\wangliang\\Desktop\\2.txt")); // 给file改名
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6.2 案例:复制文件夹中的所有内容
public class TestMain14 {
    // 文件夹复制
    public static void main(String[] args) {
        String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
        String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
        copy(srcPath, destPath);
    }
    /**
     * 复制文件夹
     * @param srcPath 源路径
     * @param destPath 目标路径
     */
    public static void copy(String srcPath, String destPath) {
        try {
            File srcFile = new File(srcPath); // 源路径文件
            // 如果源路径存在
            if(srcFile.exists()) {
                File destFile = new File(destPath); // 目标路径文件
                if(!destFile.exists()) {
                    destFile.mkdirs();
                }
                copyFile(srcFile, destFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void copyFile(File src, File dest) {
        if(src.isDirectory()) {
            dest.mkdir(); // 创建该文件夹
            // 循环遍历
            File[] files = src.listFiles();
            for (File file : files) {
                // 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
                // Java中提供路径常量File.separator
                copyFile(file, new File(dest + File.separator + file.getName())); 
            }
        }else {
            try (
                    FileInputStream fis = new FileInputStream(src);
                    FileOutputStream fos = new FileOutputStream(dest);
                    ){
                byte [] buffer = new byte[1024];
                int len;
                while((len = fis.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                fos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }    
    }
}

七、FileFilter接口

过滤文件,例如,在复制文件夹时,部分文件不复制。

public class TestMain15 {
    // 文件夹复制
    public static void main(String[] args) {
        String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
        String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
        copy(srcPath, destPath);
    }
    /**
     * 复制文件夹
     * @param srcPath 源路径
     * @param destPath 目标路径
     */
    public static void copy(String srcPath, String destPath) {
        try {
            File srcFile = new File(srcPath); // 源路径文件
            // 如果源路径存在
            if(srcFile.exists()) {
                File destFile = new File(destPath); // 目标路径文件
                if(!destFile.exists()) {
                    destFile.mkdirs();
                }
                copyFile(srcFile, destFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void copyFile(File src, File dest) {
        if(src.isDirectory()) {
            dest.mkdir(); // 创建该文件夹
            // 循环遍历
            // 文件名称过滤器
            File[] files = src.listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    // 得到文件
                    String path = dir.getAbsolutePath() + File.separator + name;
                    // 判断文件是否路径
                    if(new File(path).isDirectory()) {
                        return true;
                    }
                    if(name.endsWith(".jpg") || name.endsWith(".png")) {
                        return true;
                    }
                    return false;
                }
            });
            for (File file : files) {
                // 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
                // Java中提供路径常量File.separator
                copyFile(file, new File(dest + File.separator + file.getName())); 
            }
        }else {
            try (
                    FileInputStream fis = new FileInputStream(src);
                    FileOutputStream fos = new FileOutputStream(dest);
                    ){
                byte [] buffer = new byte[1024];
                int len;
                while((len = fis.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                fos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }    
    }
}
public class TestMain16 {
    // 文件夹复制
    public static void main(String[] args) {
        String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
        String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
        copy(srcPath, destPath);
    }
    /**
     * 复制文件夹
     * @param srcPath 源路径
     * @param destPath 目标路径
     */
    public static void copy(String srcPath, String destPath) {
        try {
            File srcFile = new File(srcPath); // 源路径文件
            // 如果源路径存在
            if(srcFile.exists()) {
                File destFile = new File(destPath); // 目标路径文件
                if(!destFile.exists()) {
                    destFile.mkdirs();
                }
                copyFile(srcFile, destFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void copyFile(File src, File dest) {
        if(src.isDirectory()) {
            dest.mkdir(); // 创建该文件夹
            // 循环遍历
            // 文件名称过滤器
            File[] files = src.listFiles(new FileFilter() {
                @Override
                public boolean accept(File file) {
                    // 判断文件是否文件夹
                    if(file.isDirectory()) {
                        return true;
                    }
                    if(file.getName().endsWith(".jpg") || file.getName().endsWith(".png")) {
                        return true;
                    }
                    return false;
                }
            });
            for (File file : files) {
                // 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
                // Java中提供路径常量File.separator
                copyFile(file, new File(dest + File.separator + file.getName())); 
            }
        }else {
            try (
                    FileInputStream fis = new FileInputStream(src);
                    FileOutputStream fos = new FileOutputStream(dest);
                    ){
                byte [] buffer = new byte[1024];
                int len;
                while((len = fis.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                fos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }    
    }
}