Java基础之IO流(缓冲流、转换流、序列化流)

1. 缓冲流

IO流中有一些更强大的流,比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等.这些功能更为强大的流,都是在基本的流对象基础上创建出来的,相当于是对基本流对象的一种增强

1.1 概述

缓冲流,也叫高效流,是对基本的输入输出流的增强,所以也是4个流,按照数据类型分为:

  • 字节缓冲流:BufferedInputStream,BufferedOutputStream
  • 字符缓冲流:BufferedReader,BufferedWriter

缓冲流的基本原理是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写效率

1.2 字节缓冲流

构造方法

  • public BufferedInputStream(InputStream in):创建一个新的缓冲输入流
  • public BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流
public class Test1 {
    public static void main(String[] args) throws IOException {
       // 创建字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));
        // 创建字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
    }
}

1.3 字符缓冲流

构造方法

  • public BufferedReader(Reader in):创建一个新的缓冲输入流
  • public BufferedWriter(Writer out):创建一个新的缓冲输出流
public class Test1 {
    public static void main(String[] args) throws IOException {
       // 创建字符缓冲输入流
        BufferedReader bis = new BufferedReader(new FileReader("b.txt"));
        // 创建字符缓冲输出流
        BufferedWriter bos = new BufferedWriter(new FileWriter("b.txt"));
    }
}

特有方法

字符缓冲流的基本方法与普通字符流调用方法一致,它的特有方法有:

  • BufferedReader:public String readLine():读一行文字
  • BufferedWriter:public void newLine():写一行分隔符,由系统属性定义符号
public class Test1 {
    public static void main(String[] args) throws IOException {
       // 创建字符缓冲输入流
        BufferedReader bis = new BufferedReader(new FileReader("b.txt"));
        // 定义字符串,保存读取的一行文字
        String str = null;
        // 循环读取,读取到最后返回null
        while (((str = bis.readLine()) != null)) {
            System.out.println(str);
        }
        // 释放资源
        bis.close();
    }
}
public class Test1 {
    public static void main(String[] args) throws IOException {
        // 创建字符缓冲输出流
        BufferedWriter bos = new BufferedWriter(new FileWriter("b.txt"));
        // 写出数据
        bos.write("bsj");
        // 写出换行
        bos.newLine();
        // 释放资源
        bos.close();
    }
}

2. 转换流

2.1 字符编码和字符集

字符编码

计算机中存储的信息都是使用二进制数表示的,而我们在屏幕上看到的数字,英文,标点符号,汉字等字符是二进制转换之后的结果.按照某种规则,将字符存储到计算机中,称为编码,反之,将存储在计算机中的二进制数按照某种规则解析显示出来的,称为解码.

  • 字符编码Character Encoding:就是一套自然语言的字符与二进制数之间的对应规则
  • 编码表:生活中文字和计算机二进制的对应规则

字符集

  • 字符集 Charset:也叫编码表.是一个系统支持的所有字符的集合,包括各国家文字,标点符号,图形符号,数字等

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码.常见的字符集有ASCII字符集,GBK字符集,Unicode字符集等

可见,当指定了编码,它所对应的字符集自然就指定了,所以编码菜式我们最终要关心的

2.2编码引出的问题

在IDEA中,使用FileReader读取项目的文本文件,由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题.但是,当读取Windows系统中创建的文本文件时,由于Windows系统默认的时GBK编码,就会出现乱码

public class Test1 {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("a.txt");
        int read;
        while (((read = fileReader.read()) != -1)) {
            System.out.println((char) read);
        }
        fileReader.close();
    }
}

2.3 InputStream类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁.它读取字节,并使用指定的字符集将期解码为字符.它的字符集可以由名称指定,也可以接受平台的默认字符集

构造方法

  • InputStreamReader(InputStream in):创建一个使用默认字符集的字节流
  • InputStreamReader(InputStream in,String charsetName):创建一个指定字符集的字符流
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

指定编码读取

public class Test1 {
    public static void main(String[] args) throws IOException {
        // 定义文件路径,文件为GBK编码
        String fileName = "a.txt";
        // 创建流对象,默认UTF-8编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName));
        // 创建流对象.指定GBK编码
        InputStreamReader isr2 = new InputStreamReader(new FileInputStream(fileName, "GBK"));
        // 定义变量,保存字符
        int read;
        // 使用默认编码流读取,乱码
        while (((read = isr.read()) != -1)) {
            System.out.println((char) read);
        }
        isr.close();
        // 使用指定编码字符流读取,正常解析
        while (((read = isr2.read()) != -1)) {
            System.out.println((char) read);
        }
        isr2.close();
    }
}

2.4 OutputStreamWriter类

转换流java.io.OutputStreamWriter,是Writer的子类,是从字符流到字节流的桥梁.使用指定的字符集将字符编码为字节.它的字符集可以由名称指定,也可以接受平台的默认字符集

构造方法

  • OutputStreamWriter(OutputStream in):创建一个使用默认字符集的字符流
  • OutputStreamWriter(OutputStream in,String charsetName)创建一个指定字符集的字符流
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
### 指定编码写出
public class Test1 {
    public static void main(String[] args) throws IOException {
        // 定义文件路径,
        String fileName = "a.txt";
        // 创建流对象,默认UTF-8编码
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileName));
        // 写出数据
        osw.write("你好");// 保存为6个字节
        // 创建流对象.指定GBK编码
        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(fileName),"GBK"));
        // 写出数据
        osw.write("你好");// 保存为4个字节
        osw.close();
        osw2.close();
    }
}

3. 序列化

3.1 概述

Java提供了一种对象序列化的机制.用一个字节序列可以表示一个对象,该字节序列 包含该对象的数据,对象的类型和对象中存储的属性等信息,字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息

扫描二维码关注公众号,回复: 10971045 查看本文章

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化,对象的数据,对象的类型和对象中存储的数据信息,都可以在内存中创建对象

3.2 ObjectOutputStream类

java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储

构造方法

  • public ObjectOutputStream(OutputStream out):创建一个指定OutputStream的ObjectOutputStream
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

序列化操作

一个对象要想序列化,必须满足两个条件

  • 该类必须实现java.io.Sereiazable接口,Serializable是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的,如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰
public class Employee implements Serializable {
    public String name;
    public String address;
    public transient int age;// transient瞬态修饰成员,不会被序列化
    public void addredssCheck(){
        System.out.println("Address check:"+name+"--"+address);
    }
}

写出对象方法

  • public final void writerObject(Object obj):将指定的对象写出
public class Test1 {
    public static void main(String[] args) throws IOException {
        Employee e = new Employee();
        e.name="张三";
        e.address="北京";
        e.age=20;
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.txt"));
        // 写出对象
        out.writeObject(e);
        // 释放资源
        out.close();
        System.out.println("Serialized data is saved");// 姓名,地址被序列化,年龄没有被序列化
    }
}

3.3 ObjectInputStream类

ObjectInputStream反序列化类,将之前使用ObjectOutputStream序列化的原始数据恢复为对象

构造方法

  • public ObjectInputStream(InputStream in):创建一个指定InputStreamObjectInputStream

反序列化操作1

入股能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

  • public final Object readObject():读取一个对象
public class Test1 {
    public static void main(String[] args) throws IOException {
        Employee e = null;
        // 创建反序列化流
        FileInputStream fileIn = new FileInputStream("a.txt");
        ObjectInputStream in = new ObjectInputStream(fileIn);
        // 读取一个对象
        try {
            e = (Employee) in.readObject();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        in.close();
        System.out.println("Name:"+e.name); // 张三
        System.out.println("Address:"+e.address); // 北京
        System.out.println("age"+e.age);//0
    }
}

对于JVM可以反序列化对象,它必须是能够找到class文件的类.如果找不到该类的class文件,则抛出一个ClassNotFoundException异常

返回序列化操作2

另外,当JVM反序列化对象时.能找到class文件.但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常.发生这个异常的原因如下:

  • 该类的序列版本号从刘忠范读取的类描述的版本号不匹配
  • 该类包含未知数据类型
  • 该类没有可访问的无参数构造方法

Serializable接口给需要列化的类,序列版本号
serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配

public class Employee implements Serializable {
    // 加入序列版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public String address;
    // 添加新的属性,重新编译,可以反序列化,该属性赋为默认值
    public int eid;
    public transient int age;// transient瞬态修饰成员,不会被序列化
    public void addredssCheck(){
        System.out.println("Address check:"+name+"--"+address);
    }
}

4. 打印流

4.1 概述

平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自java.io.PrintStream类.该类能够方便打印各种数据类型的值,是一种便捷的输出方式

4.2 PrintStream类

构造方法

  • public PrintStream(String fileName):使用指定的文件名创建一个新的打印流
PrintStream ps= new PrintStream("ps.txt");

改变打印流向

System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控制台上.不过,既然是流对象,我们可以改变它的打印流向

public class Test1 {
    public static void main(String[] args) throws IOException {
        // 调用系统的打印流,控制台直接输出97
        System.out.println(97);
        // 创建打印流,指定文件名称
        PrintStream ps = new PrintStream("ps.txt");
        // 设置系统的打印流向,输出到ps.txt
        System.setOut(ps);
        //调用系统的打印流,ps.txt输出97
    }
}
发布了24 篇原创文章 · 获赞 27 · 访问量 2042

猜你喜欢

转载自blog.csdn.net/weixin_45860268/article/details/104414507