菜鸟之路Day16一一IO流(二)

简介: 1. **缓冲流**:介绍了字节缓冲流和字符缓冲流的种类及其性能提升原理,重点讲解了`BufferedReader`和`BufferedWriter`的特有方法。2. **转换流**:讲解了如何使用`InputStreamReader`和`OutputStreamWriter`实现字符集编码转换,避免乱码问题。3. **对象序列化与反序列化**:详细说明了如何将Java对象保存到文件中及从文件中读取对象,并讨论了相关细节,如`Serializable`接口、`serialVersionUID`和`transient`关键字的使用。

菜鸟之路Day16一一IO流(二)

作者:blue

时间:2025.2.17

0.概述

文章内容学习自黑马程序员BV1yW4y1Y7Ms

image-20250217212856927.png

1.缓冲流

1.缓冲流有几种?

​ 字节缓冲输入流:BufferedInputStream

​ 字节缓冲输出流:BufferedOuputStream

​ 字符缓冲输入流:BufferedReader

​ 字符缓冲输出流:BufferedWriter

2.缓冲流为什么能提高性能?

​ 缓冲流自带长度为8192的缓冲区

​ 可以显著提高字节流的读写性能

​ 对于字符流提升不明显,对于字符缓冲流而言关键点是两个特有的方法

3.字符缓冲流两个特有的方法是什么?

​ 字符缓冲输入流BufferedReader:readLine()

​ 字符缓冲输出流BufferedWriter:newLine()

image-20250217215132899.png

1.1字节缓冲流

一次拷贝一个字节

public class BufferedIODemo1 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 需求:利用字节缓冲流拷贝文件
        *
        * 字节缓冲输入流的构造方法:public BufferedInputStream(InputStream is)
        *
        * 字节缓冲输出流的构造方法:public BufferedOutputStream(OutputStream os)
        * */

        //1.创建缓冲流的对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy.txt"));

        //拷贝(一次读取一个字节)
        int b;
        while ((b=bis.read())!=-1) {
   
            bos.write(b);
        }
        bos.close();
        bis.close();
    }
}

一次拷贝多个字节

public class BufferedIODemo2 {
   
    public static void main(String[] args) throws IOException {
   
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy2.txt"));
        //一次拷贝多个字节
        byte[] bytes = new byte[1024];
        int len;
        while((len= bis.read(bytes))!=-1){
   
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }
}

1.2字符缓冲流

字符缓冲输入流

public class BufferedIODemo3 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 字符缓冲输入流:
        *   构造方法:public BufferedReader(Reader r)
        * 特有方法:
        *   public String readLine() 读一整行
        * */

        BufferedReader br = new BufferedReader(new FileReader("src\\a.txt"));
        String line;
        while((line= br.readLine())!=null){
   
            System.out.println(line);
        }
        br.close();
    }
}

字符缓冲输出流

public class BufferedIODemo4 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 字符缓冲输出流
        *   构造方法:
        *       public BufferedWriter(Writer r)
        *   特有方法:
        *       public void newLine() 跨平台换行
        * */
        BufferedWriter bw = new BufferedWriter(new FileWriter("src//a.txt",true));
        bw.write("你的微笑,百度搜索不到");
        bw.newLine();//换行
        bw.write("我结婚的时候你一定要来哦,没有新娘,我很尴尬");
        bw.close();
    }
}

1.3综合练习

练习一:拷贝文件,四种方式拷贝文件,并统计各自用时

字节流的基本流:一次读写一个字节

字节流的基本流:一次读取一个字节数组

字节缓冲流:一次读写一个字节

字节缓冲流:一次读写一个字节数组

public class TestOne {
   
    public static void main(String[] args) throws IOException {
   
        long start = System.currentTimeMillis();
        //method1();//0.051秒
        //method2();//0.001秒
        //method3();//0.006秒
        method4();//0.0秒
        long end = System.currentTimeMillis();
        System.out.println((end-start)/1000.0+"秒");
    }

    //1.字节流的基本流:一次读写一个字节
    private static void method1() throws IOException {
   
        FileInputStream fis = new FileInputStream("src\\a.txt");
        FileOutputStream fos = new FileOutputStream("src\\copy1.txt");
        int b;
        while((b= fis.read())!=-1){
   
            fos.write(b);
        }
        fos.close();
        fis.close();
    }

    //2.字节流的基本流:一次读取一个字节数组
    private static void method2() throws IOException {
   
        FileInputStream fis = new FileInputStream("src\\a.txt");
        FileOutputStream fos = new FileOutputStream("src\\copy2.txt");
        byte[] bytes = new byte[1024];
        int len;
        while((len=fis.read(bytes))!=-1){
   
            fos.write(bytes,0,len);
        }
        fos.close();
        fis.close();
    }

    //3.字节缓冲流:一次读写一个字节
    private static void method3() throws IOException {
   
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy3.txt"));
        int b;
        while((b=bis.read())!=-1){
   
            bos.write(b);
        }
        bos.close();
        bis.close();
    }

    //4.字节缓冲流:一次读写一个字节数组
    private static void method4() throws IOException {
   
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy4.txt"));
        byte[] bytes = new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
   
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }
}

练习二

需求:《滕王阁序》的顺序被打乱了,请恢复他的顺序并写到新的文件中

public class TestTwo {
   
    public static void main(String[] args) throws IOException {
   
        //为了读取一整行的数据,我们再这里利用字符缓冲流,为的是readLine方法
        BufferedReader br = new BufferedReader(new FileReader("src\\a.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("src\\copy5.txt"));
        ArrayList<String> list = new ArrayList<>();
        String line;
        while((line=br.readLine())!=null){
   
            list.add(line);
        }

        //排序
        Collections.sort(list, new Comparator<String>() {
   
            @Override
            public int compare(String o1, String o2) {
   
                int i1 = Integer.parseInt(o1.split("\\.")[0]);
                int i2 = Integer.parseInt(o2.split("\\.")[0]);
                return i1-i2;
            }
        });

        for (String s : list) {
   
            bw.write(s);
            bw.newLine();
        }

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

练习三

实现一个验证程序运行次数的小程序,要求:当程序运行超过三次提示,本软件只能免费使用3次

思想:创建一个txt文件来记录使用的次数,注意此处创建BufferedWriter的位置是有考究的,因为如果文件已经存在,则会清空文件,所以如果直接在开头创建的话会导致读不到数据。

public class TestThree {
   
    public static void main(String[] args) throws IOException {
   
        BufferedReader br = new BufferedReader(new FileReader("src\\count.txt"));
        String str = br.readLine();//读取文件中的数字
        int x = Integer.parseInt(str);//转为整数
        if(x<3){
   
            System.out.println("第"+(x+1)+"次免费使用");
        }
        else{
   
            System.out.println("3次免费次数已经用完,请充值后使用");
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter("src\\count.txt"));
        bw.write(x+1+"");//写回,以字符串形式
        bw.close();
        br.close();
    }
}

2.转换流

转换流是字符流和字节流之间的桥梁,他可以包装字节流,使原本的字节流可以根据字符集一次读取多个字节,读取数据不会乱码。

1.转换流的名字是什么?

​ 字符转换输入流:InputStreamReader

​ 字符转换输出流:OutputStreamWriter

2.转换流的作用是什么?

​ 指定字符集读写数据(JDK11之后已淘汰)

​ 字节流想要使用字符流中的方法

Demo1:按照指定编码读取文件

gbktest.txt是一个GBK编码的文件,由于IDEA默认UTF-8编码所以我们可以创建一个FileReader对象,来指定读取的字符集

public class ConvertDemo1 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 按照指定编码读取文件
        * */
        FileReader fr = new FileReader("src\\gbktest.txt", Charset.forName("GBK"));
        int ch;
        while((ch=fr.read())!=-1){
   
            System.out.print((char)ch);
        }
        fr.close();
    }
}

Demo2:按指定编码写出文件

public class ConvertDemo2 {
   
    public static void main(String[] args) throws IOException {
   
        //按指定编码写出文件
        FileWriter fw = new FileWriter("src\\gbkouttest.txt", Charset.forName("GBK"));
        fw.write("你好你好");
        fw.close();
    }
}

Demo3:将本地中的GKB文件,以UTF-8的形式写出

public class ConvertDemo3 {
   
    public static void main(String[] args) throws IOException {
   
        //将本地中的GKB文件,以UTF-8的形式写出
        FileReader fr = new FileReader("src\\gbktest.txt", Charset.forName("GBk"));
        FileWriter fw = new FileWriter("src\\c.txt",Charset.forName("UTF-8"));
        int ch;
        while((ch=fr.read())!=-1){
   
            fw.write((char)ch);
        }
        fw.close();
        fr.close();
    }
}

转换流练习

public class ConvertDemo4 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
        * 1.字节流读中文数据必然会有乱码-->我们可以利用转换流,将字节流转为字符流
        * 2.字符流没有办法读取一整行数据-->我们可以将字符流包装为字符缓冲流
        * */
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src\\copy1.txt")));
        String str;
        while((str=br.readLine())!=null){
   
            System.out.println(str);
        }
        br.close();
    }
}

3.序列化流/对象操作输出流

image-20250219212144387.png

序列化流的作用:把java中的对象写到本地文件中

序列化流的小细节:使用对象输出流将对象保存文件时会出现NotSerializableException异常

解决方案:让Javabean类实现Serializable接口,Serializable接口里面是没有抽象方法的,它属于一个标记型接口,

一旦实现了这个接口就表示当前的Student类可以被序列化。

public class myObjectStreamDemo1 {
   
    public static void main(String[] args) throws IOException {
   
        /*
        * 需求:利用序列化流/对象操作输出流,把一个对象写到本地文件中
        * 构造方法:public ObjectOutputStream(OutputStream out) 把基本流变成高级流
        * 成员方法:public final void writeObject(Object obj) 把对象序列化(写出)到文件中去
        * */
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\a.txt"));
        Student stu = new Student(13,"zhangsan");
        oos.writeObject(stu);
        oos.close();
    }
}

4.反序列化流/对象操作输入流

public class myObjectStreamDemo2 {
   
    public static void main(String[] args) throws IOException, ClassNotFoundException {
   
        /*
        * 需求:利用反序列化流/对象操作输入流,把文件中的对象读到程序当中
        * 构造方法:public ObjectInputStream(InputStream out) 把基本流变成高级流
        * 成员方法:public Object readObject() 把序列化到本地文件中的对象,读取到程序中来
        * */
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\a.txt"));
        Student stu = (Student)ois.readObject();
        System.out.println(stu);
        ois.close();
    }
}

序列化流/反序列化流的细节汇总

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

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

③序列化对象后,修改了javabean类,再次反序列化,会不会有问题?

会出问题,会抛出InvalidClassException异常

解决方案:给Javabean类添加serialVersionUID(序列号,版本号)

private static final long serialVersionUID

④如果一个对象中某个成员变量的值不想被序列化,又该如何实现呢?

解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

private transient String 变量名

综合练习

需求:将多个自定义对象序列化到文件中,但是对象个数不确定

public class myObjectStreamDemo3 {
   
    public static void main(String[] args) throws IOException {
   
        //需求:将多个自定义对象序列化到文件中,但是对象个数不确定
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\a.txt"));

        Student s1 = new Student(13,"zhangsan");
        Student s2 = new Student(23,"lisi");
        Student s3 = new Student(24,"wangwu");

        // 由于对象个数是不确定的,这造成了读对象时的麻烦
        // 所以我们可以将所有的对象都放进一个集合中
        // 这样方便读取

        ArrayList<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);

        oos.writeObject(list);//序列化

        oos.close();
    }
}
public class myObjectStreamDemo4 {
   
    public static void main(String[] args) throws IOException, ClassNotFoundException {
   
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\a.txt"));

        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();//直接接收集合对象,这样就不用理会个数了

        for (Student student : list) {
   
            System.out.println(student);
        }

        ois.close();
    }
}
目录
相关文章
|
9月前
|
开发框架 移动开发 前端开发
如何选择适合的网站模版制作网站?
选择模板时需要考虑行业匹配度、设计美观性、功能性、响应式设计、易于编辑以及SEO优化等因素。 PageAdmin网站模版是推荐的,拥有丰富的模版资源、高度可自定义性、良好的兼容性与响应式设计、强大的安全性与技术支持,性价比高。
178 4
|
6月前
|
Java 数据库 网络架构
菜鸟之路Day36一一Web开发综合案例(部门管理)
本文详细记录了基于Spring Boot的Web开发综合案例——部门管理功能的实现过程。从环境搭建到功能开发,涵盖数据库表设计、Spring Boot项目创建、依赖引入、配置文件设置以及Mapper、Service、Controller的基础结构构建。文章重点讲解了查询、删除、新增和修改部门信息的业务逻辑实现,遵循RESTful规范设计接口,并通过统一响应结果类`Result`优化前后端交互体验。借助Spring的IoC容器管理与MyBatis的SQL映射,实现了高效的数据操作与业务处理,最终完成部门管理的全功能开发。
176 12
|
6月前
|
SQL 关系型数据库 MySQL
菜鸟之路Day30一一MySQL之DML&DQL
本文介绍了MySQL中DML(数据操作语言)和DQL(数据查询语言)的核心用法。DML主要包括插入(insert)、更新(update)和删除(delete)语句,通过具体示例演示了如何对表数据进行增删改操作。DQL则聚焦于数据查询,涵盖基本查询、条件查询、聚合函数、分组查询、排序查询和分页查询等内容。文章通过丰富的SQL语句实例,帮助读者掌握如何高效查询和操作数据库中的数据,适合初学者学习和实践。
214 12
|
6月前
|
SQL XML Java
菜鸟之路Day33一一Mybatis入门
本文是《菜鸟之路Day33——Mybatis入门》的教程,作者blue于2025年5月18日撰写。文章介绍了MyBatis作为一款优秀的持久层框架,如何简化JDBC开发。通过创建SpringBoot工程、数据库表`user`及实体类`User`,引入MyBatis依赖并配置数据库连接信息,使用注解方式编写SQL语句实现查询所有用户数据的功能。此外,还展示了如何通过Lombok优化实体类代码,减少冗余的getter/setter等方法,提高开发效率。最后通过单元测试验证功能的正确性。
239 19
|
5月前
|
XML SQL 前端开发
菜鸟之路Day37一一Web开发综合案例(员工管理)
本文介绍了基于Web开发的员工管理综合案例,涵盖分页查询、条件分页查询、删除员工和新增员工四大功能模块。通过前后端交互,前端传递参数(如页码、每页记录数、查询条件等),后端使用MyBatis与PageHelper插件处理数据查询与操作。代码结构清晰,包括Controller层接收请求、Service层业务逻辑处理以及Mapper层数据访问,并结合XML动态SQL实现灵活的条件查询。此外,新增与删除功能分别通过POST与DELETE请求完成,确保系统功能完整且高效。
164 7
|
6月前
|
关系型数据库 MySQL 程序员
菜鸟之路day31一一MySQL之多表设计
本文由blue撰写于2025年5月9日,主要介绍了MySQL多表设计的三种关系:一对多、一对一和多对多。一对多通过在“多”的一方添加关联字段实现,如部门与员工的关系;一对一通常用于单表拆分,通过唯一外键关联,例如学生与学生证的关系;多对多则需创建中间表,包含两个外键分别关联两方主键,如学生与课程的关系。文中还提供了实际案例,包括分类表、菜品表、套餐表及它们之间的关联设计,详细展示了多表设计的应用场景与实现方法。
178 14
|
5月前
|
存储 JSON 前端开发
菜鸟之路Day39一一登录
本文介绍了登录功能的实现及其相关技术细节,包括会话管理、令牌认证和异常处理等内容。作者通过 Java 实现了一个基于用户名和密码的登录接口,调用服务层和数据库层完成用户验证。同时,文章深入探讨了三种会话跟踪技术:Cookie、Session 和 JWT 令牌。 在 JWT 部分,详细讲解了其生成与校验流程,实现了登录成功后返回 JWT 令牌的功能。此外,文章还介绍了过滤器(Filter)和拦截器(Interceptor)的概念及应用,演示了如何利用它们实现登录校验。 最后,为解决前后端交互中异常响应不统一的问题,定义了一个全局异常处理器 将系统异常以统一的 JSON 格式返回给前端。
139 0
|
8月前
|
存储 算法 C语言
高精度算法
本文详细介绍了高精度算法的实现,涵盖加法、减法、乘法、除法及取模等操作。通过字符串与数组结合的方式,解决了大数运算中超出数据类型范围的问题。每种运算均提供完整的C++代码示例,包括输入处理、位运算模拟、进位/借位逻辑以及结果输出。其中,高精度加法和减法通过逆序存储数字简化计算;乘法利用双重循环模拟手算过程;除法分为低精度和高精度两种情况,分别采用逐位试商与减法模拟;取模则通过逐位累加求余实现。这些方法为处理大规模数值运算提供了有效工具,适用于竞赛编程与实际开发场景。
310 24
|
8月前
|
存储 自然语言处理 算法
蓝桥杯16天刷题计划一一Day01(STL练习)
本文介绍了蓝桥杯16天刷题计划的第一天内容,主要练习STL相关算法。涵盖队列、优先队列、单调队列、单调栈和链表等数据结构的应用。通过经典题目如机器翻译(队列模拟内存)、约瑟夫问题(链表模拟报数)、滑动窗口(单调队列)、Look Up(单调栈)、合并果子(优先队列)和最小函数值(优先队列结构体排序),详细解析了每种数据结构的实现与优化方法,并附有完整代码示例。适合初学者掌握STL核心用法及算法思想。
187 10
|
9月前
|
运维 并行计算 数据处理
量子计算的基本原理与传统计算的区别
量子计算的基本原理与传统计算的区别
427 5