菜鸟之路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();
    }
}
目录
相关文章
|
12月前
|
开发框架 移动开发 前端开发
如何选择适合的网站模版制作网站?
选择模板时需要考虑行业匹配度、设计美观性、功能性、响应式设计、易于编辑以及SEO优化等因素。 PageAdmin网站模版是推荐的,拥有丰富的模版资源、高度可自定义性、良好的兼容性与响应式设计、强大的安全性与技术支持,性价比高。
254 4
|
9月前
|
SQL 存储 关系型数据库
菜鸟之路Day29一一MySQL之DDL
本文《菜鸟之路Day29——MySQL之DDL》由作者blue于2025年5月2日撰写,主要介绍了MySQL中的数据定义语言(DDL)。文章详细讲解了DDL在数据库和表操作中的应用,包括数据库的查询、创建、使用与删除,以及表的创建、修改与删除。同时,文章还深入探讨了字段约束(如主键、外键、非空等)、常见数据类型(数值、字符串、日期时间类型)及表结构的查询与调整方法。通过示例代码,读者可以更好地理解并实践MySQL中DDL的相关操作。
310 11
|
11月前
|
算法
蓝桥杯16天刷题计划一一Day02
这是蓝桥杯16天刷题计划的第二天内容,由作者blue于2025年3月28日整理。当天训练重点为二分法,包含多道经典题目解析与代码实现,如有序数组查找、砍树问题、木材加工等。文章针对二分法的应用场景进行了深入讲解,并通过实例演示了如何优化算法效率,适合对二分法不熟悉的初学者学习和练习。
265 5
|
9月前
|
SQL Java 数据库连接
菜鸟之路Day34一一Mybatis-基础操作
本文介绍了MyBatis的基础操作,包括删除、插入、修改和查询功能的实现。通过`@Delete`、`@Insert`、`@Update`和`@Select`注解完成对应操作,支持主键自动生成与返回。同时探讨了`#{}`和`${}`的区别,前者用于预编译SQL提升安全性,后者直接拼接但存在SQL注入风险。文章还提供了根据ID查询及条件查询的示例,并介绍了实体类属性与数据库字段不一致时的解决方案,如使用驼峰命名规则或配置映射关系,确保数据封装准确。
286 32
|
9月前
|
SQL XML Java
菜鸟之路Day33一一Mybatis入门
本文是《菜鸟之路Day33——Mybatis入门》的教程,作者blue于2025年5月18日撰写。文章介绍了MyBatis作为一款优秀的持久层框架,如何简化JDBC开发。通过创建SpringBoot工程、数据库表`user`及实体类`User`,引入MyBatis依赖并配置数据库连接信息,使用注解方式编写SQL语句实现查询所有用户数据的功能。此外,还展示了如何通过Lombok优化实体类代码,减少冗余的getter/setter等方法,提高开发效率。最后通过单元测试验证功能的正确性。
339 19
|
8月前
|
XML SQL 前端开发
菜鸟之路Day37一一Web开发综合案例(员工管理)
本文介绍了基于Web开发的员工管理综合案例,涵盖分页查询、条件分页查询、删除员工和新增员工四大功能模块。通过前后端交互,前端传递参数(如页码、每页记录数、查询条件等),后端使用MyBatis与PageHelper插件处理数据查询与操作。代码结构清晰,包括Controller层接收请求、Service层业务逻辑处理以及Mapper层数据访问,并结合XML动态SQL实现灵活的条件查询。此外,新增与删除功能分别通过POST与DELETE请求完成,确保系统功能完整且高效。
274 7
|
机器学习/深度学习 算法 数据可视化
利用SVM(支持向量机)分类算法对鸢尾花数据集进行分类
本文介绍了如何使用支持向量机(SVM)算法对鸢尾花数据集进行分类。作者通过Python的sklearn库加载数据,并利用pandas、matplotlib等工具进行数据分析和可视化。
1142 70
|
10月前
|
存储 API C++
Cpp实现window上cmd执行效果
这段代码实现了一个简单的 Windows 命令行模拟器,支持用户输入命令并显示执行结果。程序通过 `GetCurrentDirectoryA` 获取当前目录,并用 `_popen` 执行命令,支持 `cd` 切换目录和 `exit` 退出功能。用户输入的命令会通过管道捕获输出并打印,返回码用于判断命令执行是否成功。代码结合了 C++ 标准库与 Windows API,展示了如何在 Windows 环境下操作命令行。
236 19
|
11月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
495 23
|
8月前
|
算法
数论基础一一同余定理
同余定理是数论中的重要概念,用于判断两数对同一模数的余数是否相同,记作 \(a \equiv b \ (\text{mod } m)\)。其核心性质包括加减性和乘性,广泛应用于优化前缀和相关问题。本文通过三道例题详细解析同余定理的应用:1)蓝“k倍区间”,利用哈希表记录余数出现次数,将时间复杂度从 \(O(n^2)\) 降至 \(O(n+k)\);2)题“Subsequences Summing to Sevens S”,通过正序与倒序遍历寻找最长区间;3)AtCoder D题“Pedometer”,断环为链结合同余定理解决环形步数统计问题。这些实例展示了同余定理在算法竞赛中的高效应用。
421 0