菜鸟之路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();
    }
}
目录
相关文章
|
存储 NoSQL 关系型数据库
|
缓存 前端开发 JavaScript
Vue项目打包部署Nginx配置及前端缓存问题解决
Vue项目打包部署Nginx配置及前端缓存问题解决
2362 0
Vue项目打包部署Nginx配置及前端缓存问题解决
|
10月前
|
安全 API 区块链
API是“印钞机”还是“陷阱”?解码数据资产化的隐藏密码
在数字经济时代,数据已成为核心资产,而API作为连接数据与业务的桥梁,正成为企业释放数据价值、驱动增长的关键引擎。本文通过电商、金融、医疗与政务等典型案例,解析API如何助力企业实现数据资产化、优化业务流程并开拓新商业模式,深入探讨其带来的效率提升、成本降低与生态扩展等商业价值,同时剖析API实施中的挑战与应对策略,展望其与AI、区块链等技术的融合前景,为企业把握数字化转型机遇提供洞见。
432 100
|
API 索引
HarmonyOs开发:导航tabs组件封装与使用
主页的底部导航以及页面顶部的切换导航,无论哪个系统,哪个App,都是最常见的功能之一,虽然说在鸿蒙中有现成的组件tabs可以很快速的实现,但是在使用的时候,依然有几个潜在的问题存在,第一,当导航较少时,tabs是默认居中模式,目前无法进行居左,在有这样功能的时候,难以满足需求;第二,导航右侧需要展示按钮的时候,tabs也是无法满足的;除此之外,还有很多人都非常关心的问题,底部的指示器可以跟随页面的滑动而滑动;面对着种种问题的存在,系统的tabs改进之路仍然很艰巨。
489 5
HarmonyOs开发:导航tabs组件封装与使用
|
机器学习/深度学习 算法 数据可视化
利用SVM(支持向量机)分类算法对鸢尾花数据集进行分类
本文介绍了如何使用支持向量机(SVM)算法对鸢尾花数据集进行分类。作者通过Python的sklearn库加载数据,并利用pandas、matplotlib等工具进行数据分析和可视化。
1296 70
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
604 23
|
SQL 数据安全/隐私保护 索引
SQL语句速成
《SQL语句速成》由blue编写,涵盖建表、插入、查询、更新、删除、视图创建、权限管理及索引操作等核心内容。通过具体示例介绍SQL基本语法和常用聚合函数,帮助读者快速掌握SQL编程技巧。发布于2024年7月19日。
317 7
|
Java 程序员
菜鸟之路Day22一一反射与动态代理
本文介绍了Java反射机制和动态代理的基本概念及应用。反射允许编程访问类的成员变量、构造方法和成员方法,通过三种方式获取Class对象,并演示了如何使用反射创建对象、调用方法和修改字段值。动态代理则通过接口实现无侵入式功能增强,展示了如何利用`Proxy`类和`InvocationHandler`接口生成代理对象并拦截方法调用。结合实例代码,详细讲解了反射在实际开发中的应用场景,如保存对象信息到文件和根据配置文件动态创建对象。 反射的主要作用包括: 1. 获取类的所有信息。 2. 结合配置文件动态创建对象。 动态代理的核心优势在于能够在不修改原有代码的情况下,为对象添加额外功能。
248 0
|
网络协议 Java
菜鸟之路Day21一一网络编程
《菜鸟之路 Day21:网络编程(一)》由 blue 于 2025 年 3 月 2 日发布。本文介绍了网络编程的基础知识,包括 CS/BS 架构的区别、网络编程三要素(IP、端口号、协议),并详细讲解了 InetAddress 类的使用。接着通过 UDP 和 TCP 协议演示了数据发送与接收的过程,涵盖单播、组播和广播通信方式。最后,通过多个综合练习(如多发多收、文件上传等)巩固所学内容,并引入多线程和线程池优化服务器性能。
254 2
|
JSON Java 程序员
菜鸟之路Day17一一IO流(三)
本文主要介绍了Java中的打印流、压缩/解压缩流以及Commons-io和Hutool工具包的使用。打印流包括字节打印流(PrintStream)和字符打印流(PrintWriter),支持数据原样写出、自动刷新与换行。压缩/解压缩流通过ZipInputStream和ZipOutputStream实现文件和文件夹的压缩与解压。Commons-io和Hutool工具包提供了高效的IO操作方法,简化了文件复制、删除等常见任务。文中还展示了System.out.println()作为打印流的应用示例。
313 2