菜鸟之路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();
    }
}
目录
相关文章
|
10月前
|
开发框架 移动开发 前端开发
如何选择适合的网站模版制作网站?
选择模板时需要考虑行业匹配度、设计美观性、功能性、响应式设计、易于编辑以及SEO优化等因素。 PageAdmin网站模版是推荐的,拥有丰富的模版资源、高度可自定义性、良好的兼容性与响应式设计、强大的安全性与技术支持,性价比高。
208 5
|
3月前
|
负载均衡 监控 安全
5 个 IIS 日志记录最佳实践
IIS日志记录是监控Web服务器性能与安全的关键。本文介绍启用日志、应用池配置、负载均衡、敏感数据防护、日志集中管理及保留策略等五大最佳实践,助力高效分析与合规审计。
280 1
|
11月前
|
机器学习/深度学习 算法 数据可视化
利用SVM(支持向量机)分类算法对鸢尾花数据集进行分类
本文介绍了如何使用支持向量机(SVM)算法对鸢尾花数据集进行分类。作者通过Python的sklearn库加载数据,并利用pandas、matplotlib等工具进行数据分析和可视化。
1004 70
|
11月前
|
存储 Java 程序员
菜鸟之路Day10一一集合进阶(三)
《菜鸟之路Day10——集合进阶(三)》由blue编写,发布于2025年1月28日。文章基于黑马程序员教程,介绍了Java中双列集合的特点、体系结构及Map接口的常见API和遍历方式。重点讲解了HashMap、LinkedHashMap和TreeMap的特性与应用场景,并通过多个实例演示了它们的使用方法,帮助读者深入理解集合类的高级用法。
244 73
|
9月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
383 23
|
11月前
|
算法 Java 程序员
菜鸟之路Day06一一Java常用API
《菜鸟之路Day06——Java常用API》由blue编写,发布于2025年1月24日。本文详细介绍了Java中常用的API,包括JDK7的时间类(Date、SimpleDateFormat、Calendar)和JDK8新增的时间API(ZoneId、Instant、DateTimeFormatter等),以及包装类的使用。通过多个实例练习,如时间计算、字符串转整数、十进制转二进制等,帮助读者巩固所学内容,提升编程技能。文章强调了理论与实践结合的重要性,鼓励读者多做练习以提高学习效率。
206 28
|
10月前
|
JSON Java 程序员
菜鸟之路Day17一一IO流(三)
本文主要介绍了Java中的打印流、压缩/解压缩流以及Commons-io和Hutool工具包的使用。打印流包括字节打印流(PrintStream)和字符打印流(PrintWriter),支持数据原样写出、自动刷新与换行。压缩/解压缩流通过ZipInputStream和ZipOutputStream实现文件和文件夹的压缩与解压。Commons-io和Hutool工具包提供了高效的IO操作方法,简化了文件复制、删除等常见任务。文中还展示了System.out.println()作为打印流的应用示例。
214 2
|
10月前
|
SQL 数据安全/隐私保护 索引
SQL语句速成
《SQL语句速成》由blue编写,涵盖建表、插入、查询、更新、删除、视图创建、权限管理及索引操作等核心内容。通过具体示例介绍SQL基本语法和常用聚合函数,帮助读者快速掌握SQL编程技巧。发布于2024年7月19日。
194 7
|
9月前
|
安全 Java 程序员
菜鸟之路Day20一一多线程(二)
### 菜鸟之路Day20——多线程(二) 作者:blue 日期:2025.2.27 本文通过多个练习详细介绍了Java多线程的应用,包括买票、送礼、抢红包等场景的模拟。重点讲解了`Thread`类和`Runnable`接口的使用,以及如何利用锁机制确保线程安全。此外,还探讨了线程池的概念及其优势,并提供了创建和配置线程池的具体示例。最后,讨论了如何根据CPU并行数合理设置线程池大小,以优化性能。 通过这些练习,读者可以深入理解多线程编程的核心概念和实际应用技巧。
112 1
|
10月前
|
Java 程序员 调度
菜鸟之路Day19一一多线程(一)
什么是多线程:进程与线程的区别,线程是操作系统调度的最小单位,多线程可提高效率。 并发与并行:并发是单个CPU上交替执行多个指令,并行是多个CPU同时执行。 多线程的实现方式: 线程常用方法:如`getName()`、`setName()`、`sleep()`等。 线程生命周期:从新建到终止的六个状态。 线程安全问题:通过同步代码块和方法解决共享数据的竞争问题。 Lock锁:手动控制锁的获取与释放。 死锁:产生原因及必要条件。 等待唤醒机制:生产者消费者模型及其优化。 多线程六种状态:新建、就绪、阻塞、等待、计时等待和结束。 通过实例代码详细讲解了多线程的应用场景和解决方案。
108 2