IO流


一、什么是IO流

  IO流就是以流的方式进行输入输出

  就是存储和读取数据的解决方案

二、IO流体系

字节流:

  InputStream

  OutputStream

字符流

  Reader

  Writer

这四个类都是抽象类,我们需要学习他们的子类

字节流的子类:

  文件操作:

    FileOutputStream的示例:

1.创建对象 2.写出数据 3.释放资源
        FileOutputStream fos = new FileOutputStream("a.txt");
        try {
            fos.write("123123".getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

在构造函数中也可以指定续写为开启

FileOutputStream fos = new FileOutputStream("a.txt", true);

细节:

  1.参数是字符表示的路径或者File对象

  2.文件如果不存在会创建一个新的文件,但要保证父级路径存在

  3.如果文件已经存在,则会覆盖文件,而不是追加

  4.输出的内容是字节,ASCII码,

  5.如果不释放,则在程序运行时会一直占用资源。

  6.在java中\n或者\r都可以表示换行,但是Windows系统原生换行符为\r\n,Linux为\n,macOS为\r

    FileInputStream的示例:

1.创建对象 2.读取数据 3.释放资源
		FileInputStream fis = new FileInputStream("a.txt");  
		int rd = fis.read();  
		System.out.println(rd);

细节:

  1.如果文件不存在,就直接报错。

  2.read方法一次读取一个字节,ASCII码,如果文件结束,返回-1。

  3.一般在拷贝大文件时,我们会创建一个5-10MB的数组(1024 * 1024 * 5)

文件拷贝示例:

    public static void main(String[] args) throws IOException {  
        FileInputStream fis = new FileInputStream("a.txt");  
        FileOutputStream fos = new FileOutputStream("b.txt");  
  
        byte[] array = new byte[1024 * 1024 * 5];  
  
        int b = 0;  
  
        while ((b = fis.read(array)) != -1) {  
            fos.write(array,0,b);  
            System.out.println(Arrays.toString(array));  
        }  
  
        fis.close();  
        fos.close();  
    }  
}

JDK7方案

public static void main(String[] args) {  
    try(FileInputStream fis = new FileInputStream("a.txt");  
        FileOutputStream fos = new FileOutputStream("b.txt")){  
        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();  
    }  
}

JDK9方案

public static void main(String[] args) throws FileNotFoundException {  
    public static void main(String[] args) {  
        FileInputStream fis = new FileInputStream("a.txt");  
        FileOutputStream fos = new FileOutputStream("b.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();  
        }  
    }  
}

字符集&编码规则


乱码:看到的数据与原始数据不一致,一般是编码格式不同导致的

Unicode



字符流的子类


为了解决不同编码格式的问题,所以有了字符流的接口,其下有抽象类:Reader和Writer

一、FileReader

可以从纯文本文件中读取数据

细节:

  一、在读取之后,方法的底层还会进行解码并转换为10进制。
  二、这个10进制会作为返回值,同时表示在字符集上的数字。
  三、所以如果强转成char类型,则不能正常显示中文!

public static void main(String[] args) throws IOException {  
    FileReader fr = new FileReader("a.txt");  
    char[] rd = new char[10];  
    int len = fr.read(rd);  
    System.out.println(new String(rd,0,len));  
}

底层原理:

  一、在创建对象的同时,在底层会创建一个8192字节的字节数组,作为缓冲区。
  二、在第一次调用read的无参方法时,会先从文件读取到缓冲区,之后如果数据在缓冲区中,则从缓冲区读取,否则重新填充缓冲区,如果文件已经读取完毕,则返回-1。在字节流中没有缓冲区
  三、缓冲区只会更新前部分,后面的数据不会被覆盖。
  四、如果在缓冲区读取完成之前文件被清空,还能继续读取缓冲区内的数据,但是在缓冲区读取完之后,会返回-1。


二、FileWriter

重点:在创建FileWriter的对象,如果不指定append=true,那么文件会被直接清空

底层原理:

  一、在创建完对象之后,就会在底层创建一个长度为8192的字节数组
  二、在执行write方法时,数据会先存储到缓冲区,有三种情况下数据会被写入磁盘
    ①缓冲区满。
    ②手动调用flush方法,刷新缓冲区。
    ③调用close方法,释放资源。



我们已经学完了IO流的四种基本流,接下来开始学习高级流

(将基本流封装,添加新功能)


缓冲流



一、字节缓冲流(Buffered-)

  原理:在底层自带了长度为8192的缓冲区来提高性能

1.构造方法

利用字节缓冲流来拷贝文件

public static void main(String[] args) throws IOException {  
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));  
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));  
    //循环读取,并写入  
    int b;  
    while ((b = bis.read()) != -1) {  
        bos.write(b);  
    }  
    bis.close();  
    bos.close();  
  
    }

二、字符缓冲流

  相比起字符流,两者都有缓冲区,但是字符缓冲流的方法更加好用。

1.构造方法

2.特有的方法

利用字符缓冲流来拷贝文件

public static void main(String[] args) throws IOException {  
    BufferedReader br = new BufferedReader(new FileReader("a.txt"));  
    BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));  
  
    String line;  
    while ((line = br.readLine()) != null) {  
        bw.write(line);  
        bw.newLine();  
    }  
    br.close();  
    bw.close();  
  
}


转换流


  使用转换流就可以在字节流的基础上使用字符流中的方法


序列化流


  一、作用:

  序列化流可以把Java中的对象写到本地文件中
  反序列化流可以将文件中的数据重新读取为对象

  二、方法

注意:在使用自定义对象时需要实现Serializable接口

这个接口没有抽象方法,只是作为标记型接口,一旦实现这个接口,就表示可以被序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {  
    Student stu = new Student("张三",18);  
    Student stu1 = new Student("李四",19);  
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));  
    oos.writeObject(stu);  
    oos.writeObject(stu1);  
    oos.close();  
  
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));  
    Student stu2 = (Student)ois.readObject();  
    System.out.println(stu2);  
    Student stu3 = (Student)ois.readObject();  
    System.out.println(stu3);  
}

细节:

  一、如果JavaBean类被修改,那么先前存储的数据就不匹配了。会无法读入对象而报错,此时可以通过手动指定序列号解决
    private static final long serialVersionUID = xxxxxL;
  二、如果想要一个属性不被序列化到文件中,可以使用transient(瞬态关键字)修饰变量。

import java.io.Serial;  
import java.io.Serializable;  
  
public class Student implements Serializable {  
    @Serial  
    private static final long serialVersionUID = -770572251124423830L;  
    private String name;  
    private int age;  
    private transient String address;  
  
    public Student() {  
    }  
    public Student(String name, int age, String address) {  
        this.name = name;  
        this.age = age;  
        this.address = address;  
    }  
    /**  
     * 获取     * @return name  
     */    public String getName() {  
        return name;  
    }  
    /**  
     * 设置     * @param name  
     */  
    public void setName(String name) {  
        this.name = name;  
    }  
    /**  
     * 获取     * @return age  
     */    public int getAge() {  
        return age;  
    }  
    /**  
     * 设置     * @param age  
     */  
    public void setAge(int age) {  
        this.age = age;  
    }  
    /**  
     * 获取     * @return address  
     */    public String getAddress() {  
        return address;  
    }  
    /**  
     * 设置     * @param address  
     */  
    public void setAddress(String address) {  
        this.address = address;  
    }  
    public String toString() {  
        return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";  
    }  
}



打印流


打印流不能读只能写

一、方法

  字节打印流


  字符打印流


总结:



压缩流






通过例子了解ZipEntry:

如下:

new ZipEntry(name):`这个`name`是什么,解压后的文件结构就是什么
我有一个文件是:`‪D:\CaptureTest\filepath\1.jpg`,
而`name = "Image\01.jpg"`
生成压缩文件后,再解压的文件结构就是`"Image\01.jpg"
/**
 * 压缩指定路径的文件
 */
public static void fileToZip(String srcFile, String zipFile) throws IOException {
        File file = new File(srcFile);
        //取出文件名
        String name = file.getName();
        //读取文件
        FileInputStream inputStream = new FileInputStream(file);
        //输出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
        //ZipEnter:表示压缩文件的条目(文件目录)
        zipOutputStream.putNextEntry(new ZipEntry("Image\\01.jpg"));

        int temp = 0;
        while ((temp = inputStream.read()) != -1) {
            zipOutputStream.write(temp);
        }
        zipOutputStream.close();
        inputStream.close();
    }

    public static void main(String[] args) {
        try {
            fileToZip("‪D:\CaptureTest\filepath\1.jpg", "D:\\CaptureTest\\123.zip");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
1234567891011121314151617181920212223242526272829

用解压软件打开123.zip:路径就是:\Image\01.jpg

将文件夹添加到压缩文件案例

public class ZIPOutputStreamDemo {  
    public static void main(String[] args) throws IOException {  
        //1.创建File对象表示要压缩的文件夹  
        File src = new File("D:\\test");  
        //2.创建File对象表示压缩包的存放目录、  
        File destParent = src.getParentFile();  
        //3.创建File对象表示压缩包的路径  
        File dest = new File(destParent,src.getName() + ".zip");  
        //4.创建压缩流关联压缩包  
        ZipOutputStream zos = new ZipOutputStream((new FileOutputStream(dest)));  
        //5.获取src里的每一个文件,编程ZipEntry对象,放入压缩包中  
        toZip(src, zos);  
        //6.释放资源  
        zos.close();  
    }  
    /*  
    * 将一个目录添加到压缩包    * 参数一、数据源    * 参数二、压缩流    * 参数三、内部路径    * */    public static void toZip(File src,ZipOutputStream zos) throws IOException {  
        String name = src.getName();  
        File[] files = src.listFiles();  
        for (File file : files) {  
            if (file.isDirectory()){  
                toZip(file,zos);  
            }else{  
                //判断文件,转为ZipEntry对象  
                ZipEntry fileEntry = new ZipEntry(name + "\\" + file.getName());  
                zos.putNextEntry(fileEntry);  
                //读取文件中的数据,写入到压缩包  
                FileInputStream fis = new FileInputStream(file);  
                int b;  
                while((b = fis.read()) != -1){  
                    zos.write(b);  
                }  
                fis.close();  
                zos.closeEntry();  
            }  
        }  
    }  
}


Commons-io


Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包

作用:提高IO流的开发效率。

使用步骤:

  一、在项目中新建一个文件夹:lib

  二、将jar包复制粘贴到lib文件夹

  三、右键点击jar包,选择Add as Library

  四、在类中导包使用

32-commons-io整理的文档