IO流

文件

Snipaste_2022-05-08_22-35-19.png

Snipaste_2022-05-08_22-38-38.png

三种常用创建文件的代码:

// method1 new File(String pathname)
    public static void create1() {
        String filePath = "E:\\test.txt";
        File file = new File(filePath); //这里的file在java程序中,只是一个对象
        try {
            file.createNewFile(); //这里才创建文件到磁盘
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // method2 new File(File parent, String child) 根据父目录文件 + 子路径创建
    public static void create2() {
        File parentFile = new File("E:\\");
        String fileName = "test2.txt";
        File file = new File(parentFile, fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // method3 new File(String parentName, String childName)
    public static void create3() {
        String parentName = "E:\\";
        String childName = "test3.txt";
        File file = new File(parentName, childName);
        try {
            System.out.println("文件创建成功");
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

常用操作:

getNamegetAbsolutePathgetParentlength(字节大小)、existsisFileisDirectorydelete等。

IO流

流的分类:

  • 按操作数据单位不同分为:字节流(8 bit)二进制文件字符流(按字符) 文本文件

  • 按数据流的流向不同分为:输入流,输出流

  • 按流的角色的不同分为:节点流(FileReader、FileWriter等),处理流/包装流(BufferedReader、BufferedWriter等)

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputSream Writer

InputStream

InputStream常用的子类:

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

FileInputStream

public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        byte[] buf = new byte[8];
        int readLen = 0;
        try {
            fileInputStream = new FileInputStream("E:\\test.txt");
            // 可以read读字节,也可以read(byte[] byte)读一个字符数组
            while ((readLen = fileInputStream.read(buf)) != -1) {   
                System.out.print(new String(buf, 0, readLen));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

OutputStream

FileOutputStream

public static void main(String[] args) {
        /**
         * 注意:new FileOutputStream(filePath)            当写入内容时会覆盖原来的内容
         *      new FileOutputStream(filePath, true)      当写入内容时会追加到末尾
         * */
        String pathName = "E:\\test2.txt";
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(pathName, true);
            // 写入一个字节
            fileOutputStream.write('a');
            // 写入字符串
            String str = "hello, world!";
            fileOutputStream.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

FileReader

操作和FileInputStream类似。

FileWriter

操作和FileOutputStream类似。

new FilwWriter(File/String, true)追加到文件末尾

注意FileWriter使用后,必须要关闭(close)或刷新(flush)!

BufferedInputStream

FileInputStream操作类似。

BufferedOutputStream

FileOutputStream操作类似。

BufferedReader

Reader的一个子类,是对节点流的包装。

例如:new BufferedReader(new FileReader(PathName))

bufferedReader.readLine() 按行读取文件,当返回null时,表示文件读取完毕。

BufferedWriter

BufferedReader,是Writer的一个子类,是对节点流的包装。

例如:new BufferedWriter(new FileWriter(PathName, true))

bufferWriter.write()等。

对象处理流

Snipaste_2022-05-14_07-53-00.png

序列化:保存数据的数据类型(必须实现Serializable接口(常用)或Externalizable接口)

反序列化:恢复数据的数据类型

ObjectOutputStream提供序列化功能

ObjectInputStream提供反序列化功能

和BufferedReader和BufferedWriter类似,是包装类。

public class ObjectOutputStream_ {
    public static void main(String[] args) throws Exception {
        String pathName = "E:\\test.txt";

        // 序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(pathName));
        objectOutputStream.writeInt(100);
        objectOutputStream.writeBoolean(true);
        objectOutputStream.writeObject(new Dog("小黄", 1));
        objectOutputStream.writeObject(new Dog("小黑", 2));
        objectOutputStream.close();
        System.out.println("ok");

        // 反序列化,注意读取顺序
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(pathName));
        System.out.println(objectInputStream.readInt());
        System.out.println(objectInputStream.readBoolean());
        Object obj = objectInputStream.readObject();
        // 向下转型  注意:这里需要将Dog类的定义拷贝到可以引用的位置
        Dog dog = (Dog)obj;
        System.out.println("姓名: " + dog.getName() + "    年龄: " + dog.getAge());
        objectInputStream.close();
        System.out.println("ok");
    }
}

// 要被序列化的类一定要实现Serializable接口
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;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

对象处理流的使用细节

  • 读写顺序要一致
  • 要求实现序列化或反序列化对象,需要实现Serializable
  • 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
    • 例如:private static final long SerialVersionUID = 1L
  • 序列化对象时,默认将里面所有属性都进行序列化,但除了statictransient修饰的成员
  • 序列化对象时,要求里面属性的类型也需要实现序列化接口
  • 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

面试题:close()和flush()的区别

  • close() 关闭流对象,但要先刷新一次缓冲区。关闭后流对象不可以继续被使用了。

  • flush() 仅仅刷新缓冲区,刷新之后流对象依然可以继续使用。

字节流和字符流使用情况:(重要)

字符流和字节流的使用范围:

  • 字节流一般用来处理图像,视频,以及PPT,Word类型的文件。

  • 字符流一般用于处理纯文本类型的文件,如TXT文件等。

  • 字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件。

结论:只要处理纯文本类型的数据,优先考虑字符流,其余文件一律用字节流。

字节流和字符流的区别(重点)

字节流和字符流的区别:(详细可以参见这篇文章)

字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。(close()方法在关闭字符流之前会强制刷新一遍缓冲区)

  • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

  • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。