菜鸟之路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();
    }
}
目录
相关文章
|
Java Spring 容器
@Resource 这个注解什么用啊
@Resource 这个注解什么用啊
994 0
|
存储 Java 数据库连接
MyBatis-Plus 基础操作指南:实现高效的增删改查
MyBatis-Plus 基础操作指南:实现高效的增删改查
1026 0
|
SQL 数据安全/隐私保护 索引
SQL语句速成
《SQL语句速成》由blue编写,涵盖建表、插入、查询、更新、删除、视图创建、权限管理及索引操作等核心内容。通过具体示例介绍SQL基本语法和常用聚合函数,帮助读者快速掌握SQL编程技巧。发布于2024年7月19日。
359 7
|
SQL 关系型数据库 MySQL
菜鸟之路Day30一一MySQL之DML&DQL
本文介绍了MySQL中DML(数据操作语言)和DQL(数据查询语言)的核心用法。DML主要包括插入(insert)、更新(update)和删除(delete)语句,通过具体示例演示了如何对表数据进行增删改操作。DQL则聚焦于数据查询,涵盖基本查询、条件查询、聚合函数、分组查询、排序查询和分页查询等内容。文章通过丰富的SQL语句实例,帮助读者掌握如何高效查询和操作数据库中的数据,适合初学者学习和实践。
621 12
|
Java 程序员
菜鸟之路Day22一一反射与动态代理
本文介绍了Java反射机制和动态代理的基本概念及应用。反射允许编程访问类的成员变量、构造方法和成员方法,通过三种方式获取Class对象,并演示了如何使用反射创建对象、调用方法和修改字段值。动态代理则通过接口实现无侵入式功能增强,展示了如何利用`Proxy`类和`InvocationHandler`接口生成代理对象并拦截方法调用。结合实例代码,详细讲解了反射在实际开发中的应用场景,如保存对象信息到文件和根据配置文件动态创建对象。 反射的主要作用包括: 1. 获取类的所有信息。 2. 结合配置文件动态创建对象。 动态代理的核心优势在于能够在不修改原有代码的情况下,为对象添加额外功能。
267 0
|
前端开发 Java
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
文章通过一个表白墙/留言墙的初级SpringBoot项目实例,详细讲解了如何进行前后端开发,包括定义前后端交互接口、创建SpringBoot项目、编写前端页面、后端代码逻辑及实体类封装的全过程。
613 3
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
10月前
|
安全 Java API
Java日期时间API:从Date到Java.time
本文深入解析了Java 8中引入的全新日期时间API,涵盖LocalDate、LocalTime、LocalDateTime、ZonedDateTime等核心类的使用,以及时间调整、格式化、时区处理和与旧API的互操作。通过实例对比,展示了新API在可变性、线程安全与易用性方面的显著优势,并提供迁移方案与实战技巧,助你掌握现代Java时间处理的最佳实践。
|
SQL XML Java
菜鸟之路Day35一一Mybatis之XML映射与动态SQL
本文介绍了MyBatis框架中XML映射与动态SQL的使用方法,作者通过实例详细解析了XML映射文件的配置规范,包括namespace、id和resultType的设置。文章还对比了注解与XML映射的优缺点,强调复杂SQL更适合XML方式。在动态SQL部分,重点讲解了`&lt;if&gt;`、`&lt;where&gt;`、`&lt;set&gt;`、`&lt;foreach&gt;`等标签的应用场景,如条件查询、动态更新和批量删除,并通过代码示例展示了其灵活性与实用性。最后,通过`&lt;sql&gt;`和`&lt;include&gt;`实现代码复用,优化维护效率。
1255 5
|
存储 Java 程序员
菜鸟之路Day26一一Maven
本文由blue撰写,发布于2025年3月25日,主要介绍Maven工具的使用。Maven是Apache旗下的开源项目,用于管理和构建Java项目,基于项目对象模型(POM)概念。文章详细讲解了Maven的安装配置、IDEA中集成Maven的方法、依赖管理(包括依赖配置、传递与排除、依赖范围)、以及Maven的生命周期(clean、default、site)。通过学习,读者可掌握Maven的基本功能及其在项目中的应用。
627 12
|
存储 Java 程序员
菜鸟之路Day09一一集合进阶(二)
《菜鸟之路Day09——集合进阶(二)》由blue撰写于2025年1月27日。本文总结了Java集合框架的高级用法,重点介绍了泛型、Set系列集合等内容。泛型特性自JDK5引入,允许在编译阶段约束数据类型,避免运行时异常,并通过泛型类、方法和接口的应用增强了代码灵活性。Set系列集合包括HashSet、LinkedHashSet和TreeSet,分别实现了无序、有序及可排序的元素存储,支持多种遍历方式如迭代器、增强for循环和Lambda表达式。此外,文章详细解析了TreeSet的自然排序与比较器排序机制,提供了丰富的代码示例帮助理解。
275 17