Java—— IO流 第三期

发布于:2025-05-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

基本流与高级流

以下四种是IO流中最基本的流,称为基本流

字节流的FileInputStream和FileOutputStream

字符流的FileReader和FileWriter

高级流就是在基本流的基础上,额外又封装了一些功能,也是字节流或字符流的实现类

包括:缓冲流,转换流,序列化流与反序列化流,打印流,压缩流与解压缩流

缓冲流

缓冲流是属于字节流和字符流的高级流

分类

字节缓冲流

说明

底层自带了长度为8192的缓冲区提高性能

创建字节缓冲流对象
方法名称 说明
public BufferedInputStream (InputStream is)

把基本流包装成高级流,

参数是基本流

public BufferedOutputStream (OutputStream os)

把基本流包装成高级流,

参数是基本流

代码演示
import java.io.*;

public class Test5 {
    public static void main(String[] args) throws IOException {
        //将本模块下a.txt中的数据拷贝到本模块b.txt文件中

        /*//创建基本流
        FileInputStream fis = new FileInputStream("day04\\a.txt");
        FileOutputStream fos = new FileOutputStream("day04\\b.txt");
        //将基本流包装为缓冲流
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);*/

        //可以简化为
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day04\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day04\\b.txt"));

        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }

        //直接关闭缓冲流,基本流也会被关闭
        bos.close();
        bis.close();
    }
}

字符缓冲流

说明

字符流的基本流的底层也有缓冲区,所以字符缓冲流对性能的提升影响不大

主要是学习其特有的两个方法

创建字符缓冲流对象
方法名称 说明
public BufferedReader(Reader r) 把基本流变成高级流
public BufferedWriter(Writer r) 把基本流变成高级流
字符缓冲流特有方法
字符缓冲输入流特有方法 说明
public String readLine() 读取一行数据,如果没有数据可读了,会返回null

注意:readLine方法在读取的时候,一次读一整行,遇到回车换行结束,但是不会把回车换行读到内存当中 

字符缓冲输出流特有方法 说明
public void newLine() 跨平台的换行

注意:每个平台的换行符不同, 但使用newLine方法可以在任何平台进行换行

代码演示
import java.io.*;

public class Test6 {
    public static void main(String[] args) throws IOException {
        //将本模块下a.txt中的数据一行一行的拷贝到本模块b.txt文件中

        BufferedReader br = new BufferedReader(new FileReader("day04\\a.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("day04\\b.txt"));

        String line;
        while ((line = br.readLine()) != null) {
            //读一整行写一整行
            bw.write(line);
            //读不到换行符,要自己换行
            bw.newLine();
        }

        bw.close();
        br.close();
    }
}


转换流

转换流是属于字符流的高级流,是字符流和字节流之间的桥梁

分类

创建转换流对象

转换输入流
方法名称 说明
public InputStreamReader(InputStream in)

将字节流转换为字符流

public InputStreamReader(InputStream in, String charsetName)

将字节流转换为字符流,且指定字符集

转换输出流
方法名称 说明
public OutputStreamWriter(OutputStream out)

将字节流转换为字符流

public OutputStreamWriter(OutputStream out, String charsetName) 将字节流转换为字符流,且指定字符集

作用

让字节流使用字符流中的方法

代码演示

import java.io.*;

public class Test7 {
    public static void main(String[] args) throws IOException {
        //利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
        //只有字符缓冲流可以读一整行

        //创建字节流对象
        FileInputStream fis = new FileInputStream("day04\\a.txt");
        //将字节流转换为字符流
        InputStreamReader isr = new InputStreamReader(fis);
        //将字符流包装成字符缓冲流
        BufferedReader br = new BufferedReader(isr);

        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        //对酒当歌,人生几何!
        //譬如朝露,去日苦多。
        //慨当以慷,忧思难忘。
        //何以解忧?唯有杜康。

        br.close();
    }
}
指定字符集读写

代码演示

import java.io.*;

public class Test8 {
    public static void main(String[] args) throws IOException {
        //将本地GBK文件转成UTF-8文件
        
        //将GBK文件读到程序中
        //要指定使用GBK字符集进行读取,否则会出现乱码
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\a.txt"),"GBK");

        //再将读到程序中的数据以UTF-8写出
        //要指定使用UTF-8字符集进行写出,不指定字符集时idea默认使用UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day04\\a.txt"));

        int b;
        while((b = isr.read())!=-1){
            osw.write(b);
        }

        osw.close();
        isr.close();

    }
}

 -----> 

但是JDK11后出现了新的方式,以后主要使用新方式

代码演示

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;

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

        //将GBK文件读到程序中
        //使用Charset.forName指定GBK字符集进行读取,否则会出现乱码
        FileReader fr = new FileReader("D:\\a.txt", Charset.forName("GBK"));

        //再将读到程序中的数据以UTF-8写出
        //指定UTF-8字符集进行写出,不指定字符集时idea默认使用UTF-8
        //FileWriter fw = new FileWriter("day04\\z.txt",Charset.forName("UTF-8"));
        FileWriter fw = new FileWriter("day04\\a.txt");

        int b;
        while ((b = fr.read()) != -1) {
            fw.write(b);
        }

        fw.close();
        fr.close();

    }
}

序列化流与反序列化流

序列化流与反序列化流是属于字节流的高级流,用来操作Java中的对象

分类

序列化流

序列化流也称为对象操作输出流,可以把Java中的对象写到本地文件中

构造方法 说明
public ObjectOutputStream(OutputStream out) 把基本流包装成高级流
成员方法 说明
public final void writeObject(Object obj) 把对象序列化(写出)到文件中去

反序列化流

反序列化流也称为对象操作输入流,可以把序列化到本地文件中的对象,读取到程序中来

构造方法 说明
public ObjectInputStream(InputStream out) 把基本流包装成高级流
成员方法 说明
public object readObject() 把序列化到本地文件中的对象,读取到程序中来

序列化流/反序列化流细节

使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。否则,会出现NotSerializableException异常。

Serializable接口里面是没有抽象方法,是标记型接口
一旦实现了这个接口,那么就表示当前的类可以被序列化

如果一个对象中的某个成员变量的值不想被序列化,可以给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

序列化流写到文件中的数据是不能修改的,一旦修改破坏数据后就无法再次读回来了

序列化对象后,修改了Javabean类,再次反序列化,会出现InvalidClassException异常

是因为Javabean类底层会根据成员变量和方法计算出一个版本号,修改后版本号会改变

两次版本号不统一就会出现异常

可以给Javabean类添加serialVersionUID(序列号、版本号),使版本号不会被改变

这样序列化对象后,修改Javabean类,也不会出现异常

代码演示

自定义Student对象的javabeen类

import java.io.Serializable;

//实现接口Serializable,表示该Student类可以被序列化
public class Student implements Serializable {

    //设定版本号serialVersionUID
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    //不想序列化address,使用关键字transient
    private transient String address;

    public Student() {
    }

    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

进行序列化, 将Student对象写到本地文件中

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ObjectOutput{

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

        Student stu = new Student("张三",23,"北京");

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day04\\a.txt"));

        oos.writeObject(stu);

        oos.close();
    }
}

 进行反序列化,将序列化到本地文件中的对象,读取到程序中来

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ObjectInput {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day04\\a.txt"));

        Student o = (Student) ois.readObject();
        System.out.println(o);
        //Student{name='张三', age=23, address='null'}
        //因为address不想序列化,使用了关键字transient,所以反序列化得到的是null

        ois.close();
    }
}

序列化多个自定义对象的方法

先将多个自定义对象添加到一个集合中,再将集合序列化(集合也实现了Serializable接口)

这样不管有多少个自定义对象,在反序列化时只需使用一次readObject方法,得到装有对象的集合遍历就行。

代码演示

进行序列化, 将装有多个Student对象的集合写到本地文件中

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;

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

        ArrayList<Student> list = new ArrayList<>();

        Student s1 = new Student("张三", 23, "北京");
        Student s2 = new Student("李四", 24, "天津");
        Student s3 = new Student("王五", 25, "上海");
        Collections.addAll(list, s1, s2, s3);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day04\\a.txt"));
        oos.writeObject(list);
        oos.close();

    }
}

进行反序列化,将序列化到本地文件中的集合,读取到程序中来

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class ObjectInput {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day04\\a.txt"));
        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();

        for (Student s : list) {
            System.out.println(s);
        }
        //Student{name='张三', age=23, address='北京'}
        //Student{name='李四', age=24, address='天津'}
        //Student{name='王五', age=25, address='上海'}

        ois.close();
    }
}

打印流

打印流是属于输出流的高级流

分类 

特点

打印流只操作文件目的地,不操作数据源
特有的写出方法可以实现数据原样写出(写97就会写出97,不会转变为a)
特有的写出方法,可以实现自动刷新,自动换行,打印一次数据 = 写出 + 换行 + 刷新 

字节打印流

构造方法 说明
public PrintStream(OutputStream/File/String) 关联字节输出流/文件/文件路径
public PrintStream(OutputStream out,boolean autoFlush) 自动刷新
public PrintStream(OutputStream out,boolean autoFlush, String encoding) 指定字符编码且自动刷新
public PrintStream(File/String,Charset charset) 指定字符编码
public PrintStream(File/String,String csn)

字节流底层没有缓冲区,开不开自动刷新都一样

idea默认字符编码为UTF-8

成员方法 说明
public void write(int b) 常规方法:规则跟字节输出流基本流一样,将指定的字节写出
public void println(Xxx xx) 特有方法:打印任意数据,自动刷新,自动换行
public void print(Xxx xx) 特有方法:打印任意数据,不换行
public void printf(String format, 0bject... args) 特有方法:带有占位符的打印语句,不换行 

 特有方法可以实现数据原样写出

代码演示

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Test1 {
    public static void main(String[] args) throws FileNotFoundException {

        PrintStream ps = new PrintStream(new FileOutputStream("day04\\a.txt"),true);

        ps.println(97);
        ps.print(true);
        ps.printf("你是%s","张三");

        ps.close();
    }
}

 

字符打印流

构造方法 说明
public PrintWriter(OutputStream/Write/File/String) 关联字节输出流/字符输出流/文件/文件路径
public PrintWriter(OutputStream/Write,boolean autoFlush) 自动刷新
public PrintWriter(OutputStream out,boolean autoFlush,Charset charset) 指定字符编码且自动刷新
public PrintWriter(File/String,Charset charset) 指定字符编码
public PrintWriter(File/String,String csn)

字符流底层有缓冲区,想要自动刷新需要开启

idea默认字符编码为UTF-8

成员方法 说明
public void write(...) 常规方法:规则跟之前一样,写出字节或者字符串
public void println(Xxx xx) 特有方法:打印任意类型的数据并且换行
public void print(Xxx xx) 特有方法:打印任意类型的数据,不换行
public void printf(String format, Object... args) 特有方法:带有占位符的打印语句 

特有方法可以实现数据原样写出

代码演示

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

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

        PrintWriter pw = new PrintWriter(new FileWriter("day04\\a.txt"), true);

        pw.println(12345);
        pw.print(false);
        pw.printf("1+1=%d",2);

        pw.close();

    }
}

 

打印流的应用场景

打印输出语句底层计算打印流

public class Test3 {
    public static void main(String[] args) {

        //获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台
        //这是特殊的打印流,也叫系统中的标准输出流,不能关闭,在系统中是唯一的。
        PrintStream ps = System.out;

        //调用打印流中的方法println
        //写出数据,自动换行,自动刷新
        ps.println("123");
        //123

        //关闭后,下面的输出语句不会输出数据
        ps.close();
        System.out.println("456");
    }
}

 

压缩流与解压缩流

压缩流与解压缩流是属于字节流的高级流

分类

压缩流

public class Test4 {
    public static void main(String[] args) throws IOException {
        //把D:\\aaa文件夹压缩成一个压缩包

        //1.创建File对象表示要压缩的文件夹
        File src = new File("D:\\aaa");
        //2.创建File对象表示压缩包放在哪里(压缩包的父级路径)
        File destParent = src.getParentFile();//D:\\
        //3.创建File对象表示压缩包的路径
        File dest = new File(destParent, src.getName() + ".zip");
        //4.创建压缩流关联压缩包
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
        //5.获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
        toZip(src, zos, src.getName());//aaa
        //6.释放资源
        zos.close();
    }

    /*
     *   作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
     *   参数一:数据源
     *   参数二:压缩流
     *   参数三:压缩包内部的路径
     * */
    public static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
        //1.进入src文件夹
        File[] files = src.listFiles();
        //2.遍历数组
        for (File file : files) {
            if (file.isFile()) {
                //3.判断-文件,变成ZipEntry对象,放入到压缩包当中
                ZipEntry entry = new ZipEntry(name + "\\" + file.getName());//aaa\\no1\\a.txt
                zos.putNextEntry(entry);
                //读取文件中的数据,写到压缩包中的该ZipEntry对象中
                FileInputStream fis = new FileInputStream(file);
                int b;
                while ((b = fis.read()) != -1) {
                    zos.write(b);
                }
                fis.close();
                //关闭该ZipEntry对象
                zos.closeEntry();
            } else {
                //4.判断-文件夹,递归
                toZip(file, zos, name + "\\" + file.getName());
            }
        }
    }
}

解压缩流

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

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

        //1.创建一个File表示要解压的压缩包
        File src = new File("D:\\aaa.zip");
        //2.创建一个File表示解压的目的地
        File dest = new File("D:\\");

        //调用方法
        unzip(src, dest);

    }

    //定义一个方法用来解压
    public static void unzip(File src, File dest) throws IOException {
        //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
        //创建一个解压缩流用来读取压缩包中的数据
        ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
        //要先获取到压缩包里面的每一个Zipentry对象
        //Zipentry对象表示当前在压缩包中获取到的文件或者文件夹
        ZipEntry entry;
        while ((entry = zip.getNextEntry()) != null) {
            //System.out.println(entry);
            if (entry.isDirectory()) {
                //文件夹:需要在目的地dest处创建一个同样的文件夹
                File file = new File(dest, entry.toString());
                file.mkdirs();
            } else {
                //文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
                FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString()));
                int b;
                while ((b = zip.read()) != -1) {
                    //写到目的地
                    fos.write(b);
                }
                fos.close();
                //表示在压缩包中的一个文件处理完毕了,将其关闭
                zip.closeEntry();
            }
        }

        zip.close();

    }
}


网站公告

今日签到

点亮在社区的每一天
去签到