IO流---字节流

我们使用字节流虽然可以操作所有类型的文件, 但也会遇到一些问题:

  • 把文本文件中的中文内容读取到内存(Java程序)时, 可能会出现乱码
  • 将中文写入到文本文件中, 可能会出现乱码

那为什么会导致这种现象发生呢?

  • 其实是编码和解码的编码表不一致导致的

编码表

基础知识:

  • 计算机中储存的信息都是用二进制数据表示的; 我们在屏幕上看到的英文 汉字等字符是二进制数转换之后的结果
  • 按照编码表规则, 将字符存储到计算机中, 称为编码
  • 按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码
  • 编码和解码使用的码表必须一致,否则会导致乱码
    • 简单理解:
      • 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。称为编码
      • 读取的时候,先把二进制解析出来,再转成97,通过97查找码表中对应的字符是a。称为解码

编码表的种类:

ASCII码表

  • ASCII(American Standard Code for Information Interchange, 美国信息交换标准码表): 包括了数字字符, 英文大小写字符和一些常见的标点符号字符

GBK码表

  • window系统默认的码表; 兼容ASCII码表, 也包含了21003个汉字, 并支持繁体汉字以及部分日韩文字
  • GBK是中国的码表, 一个中文以两个字节的形式存储; 但不包含世界上所有国家的文字

Unicode码表

  • 由国际组织ISO制定, 是统一的万国码表, 计算机科学领域里的一项业界标准, 容纳世界上大多数国家的所有常见文字和符号
  • 但是因为表示的字符太多, 所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7, UTF-7.5, UTF-8, UTF-16, 以及UTF-32的编码方式再存储到计算机, 其中最为常见的就是UTF-8
  • Unicode是万国码表, 以UTF-8编码后一个中文以三个字节的形式存储

在这里插入图片描述


字符流读取中文的过程

字符流 = 字节流 + 编码表

  • 不管是在哪张码表中,中文的第一个字节一定是负数
    在这里插入图片描述
    上图采用的码表为Unicode码表, 采用的编码方式为UTF-8

在这里插入图片描述

使用字符输入流一次读一个字符
使用字符输入流一次读一个字符数组

public class ReaderDemo1 {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("C:\\Users\\11946\\Desktop\\old.txt");
             FileWriter fw = new FileWriter("day11\\new.txt")) {
            // 一次读写一个字符
            /*int ch = -1;
            while ((ch = fr.read()) != -1) {
                fw.write(ch);
                fw.flush();
            }*/

            // 一次读写一个字符数组
            char[] chs = new char[1024];
            int len = -1;
            while ((len = fr.read(chs)) != -1) {
                fw.write(chs, 0, len);
                fw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符缓冲流特有功能

BufferedWriter:

  • void newLine​():写一个行分隔符(换行),会根据操作系统的不同,写入不同的行分隔符

BufferedReader:

  • public String readLine​() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null

转换流

转换流就是来进行字节流和字符流之间转换的

  • InputStreamReader 是从字节流到字符流的桥梁
  • OutputStreamWriter 是从字符流到字节流的桥梁
public class ConversionDemo1 {
    public static void main(String[] args) throws IOException {
        // method1();
        // InputStreamReader : 从字节流到字符流的桥梁
        // public InputStreamReader(InputStream in, String charsetName) : 创建指定编码的 InputStream
        InputStreamReader isr = new InputStreamReader(new FileInputStream("day11\\GBK编码文件.txt"), "GBK");

        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            System.out.println(new String(chs, 0, len));
        }
        isr.close();
    }

    // 以GBK编码表为基准, 将内容写入到文件中
    private static void method1() throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day11\\GBK编码文件.txt"), "GBK");

        osw.write("初随林霭动\n");
        osw.write("稍共夜凉分\n");
        osw.write("窗迥侵灯冷\n");
        osw.write("庭虚近水闻\n");
        osw.flush();

        osw.close();
    }
}

对象操作流

对象操作流分为两类 :对象操作输入流对象操作输出流

  • 对象操作输出流(对象序列化流) :就是将对象写到本地文件中,或者在网络中传输对象
  • 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
  • 特点 :
    • 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中
  • 注意 :
    • 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
  • Serializable接口的含义 :
    • 是一个标记性接口 , 里面没有任何抽象方法
    • 只要一个类实现了此接口 , 表示此类的对象可以被序列化

在一个文件中读写一个对象

public class ObjectStreamDemo1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        method1(); // 将对象写入到本地文件中
        method2(); // 从本地文件中读取对象
    }

    private static void method1() throws IOException {
        // 1 创建对象操作输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day11\\user.txt"));

        User user = new User("xiaobo", "123456");

        // 2 把对象写入文件中
        oos.writeObject(user);

        // 3 释放资源
        oos.close();
    }

    private static void method2() throws IOException, ClassNotFoundException {
        // 1 创建对象操作输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day11\\user.txt"));

        // 2 读数据
        User user = (User)ois.readObject(); // 向下转型

        // 3 打印对象
        System.out.println(user);

        // 4 释放资源
        ois.close();
    }
}

在一个文件中读写多个对象

public class ObjectStreamTest1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        method1(); // 将对象写入到本地文件中
        method2(); // 从本地文件中读取对象
    }

    private static void method2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day11\\user.txt"));

        while(true) {
            try {
                Student s = (Student) ois.readObject();
                System.out.println(s);
            } catch(EOFException e) { // 如果对象已经读取完毕还继续读取的话会抛出此异常
                e.printStackTrace();
                break;
            }
        }

        ois.close();
    }

    private static void method1() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day11\\user.txt"));

        Student s1 = new Student("刘备", 30);
        Student s2 = new Student("关羽", 28);
        Student s3 = new Student("张飞", 26);

        oos.writeObject(s1);
        oos.writeObject(s2);
        oos.writeObject(s3);
        oos.flush();

        oos.close();
    }
}

在一个文件中读写一个对象列表

public class ObjectStreamTest2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        method1();
        method2();
    }

    private static void method2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day11\\user.txt"));

        ArrayList<Student> stuList = (ArrayList<Student>) ois.readObject();

        for (Student stu : stuList) {
            System.out.println(stu);
        }

        ois.close();
    }

    private static void method1() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day11\\user.txt"));

        ArrayList<Student> stuList = new ArrayList<>();
        stuList.add(new Student("刘备", 30));
        stuList.add(new Student("关羽", 28));
        stuList.add(new Student("张飞", 26));

        oos.writeObject(stuList);

        oos.close();
    }
}
  • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
    • 会出问题,会抛出InvalidClassException异常
  • 如果出问题了,如何解决呢?
    • 给对象所属的类加一个serialVersionUID
    • private static final long serialVersionUID = 42L;
  • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
    • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

commons-io

自己用IO流水线文件的复制等操作比较繁琐, 推荐第三方工具包 commons-io

public class homework11 {
    public static void main(String[] args) throws IOException {
        // 复制文件
        FileUtils.copyFile(new File("C:\\Users\\11946\\Desktop\\test.mp4"), new File("F:\\copy.mp4"));
        // 将java文件夹里的内容复制到copy文件夹中
        FileUtils.copyDirectory(new File("C:\\Users\\11946\\Desktop\\java"), new File("F:\\copy"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值