当前位置: 首页 > news >正文

Java:IO流——增强篇

目录

前言

一、缓冲流——让数据传输飞起来 🚀

1、缓冲思想

2、缓冲字节流

3、缓冲字符流

二、标准流——程序三大通道🚦

1、标准输入流(System.in)

2、标准输出流(System.out)

3、标准错误流(System.err)

4、PrintStream:输出的艺术

三、转换流——编码翻译官🌍

四、对象流——对象的休眠运输仓

1、序列化机制

2、对象流介绍

3、序列化接口

4、集合序列化

5、transient 关键字

6、序列化版本号

五、数据流——基本数据类型的专用通道

六、随机访问流——文件的任意门

七、Properties类——配置文件的管家


前言

上一篇我们介绍了 File类 IO流的节点流

Java:IO流——基础篇-CSDN博客https://blog.csdn.net/qq_73698057/article/details/150849027?spm=1001.2014.3001.5502接下来我们看一下IO流的增强流

一、缓冲流——让数据传输飞起来 🚀

缓冲流(Buffered Streams)也叫高效流,是一种非常有用的增强流

提供了缓冲功能,可以提高 IO 操作的效率

1、缓冲思想

通过在内存中引入一个缓冲区

将数据暂存到缓冲区中

然后按一定的块大小进行读取或写入操作

这样就可以减少频繁的 IO操作,提高效率

简单来说就是:

如果你需要去超市买 30个鸡蛋

🐌节点流 每次上下楼只能买一个鸡蛋

🚀增强流 通过 缓冲区(小篮子)

假设这个小篮子一次可以装20个鸡蛋

那么只需要两次就可以完成

📚常见缓冲流:

流类型功能特点
BufferedInputStream缓冲字节输入流提升读取效率
BufferedOutputStream缓冲字节输出流提升写入效率
BufferedReader缓冲字符输入流支持逐行读取
BufferedWriter缓冲字符输出流支持批量写入

2、缓冲字节流

既然说增强流很快,那么我们来验证一下,真的操作次数少就会效率高吗?

示例:

拷贝一张图片

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class TestBuffered {public static void main(String[] args) throws IOException {//普通流(3100ms左右)FileInputStream fis = new FileInputStream("day29/testBuffered/img.jpg");FileOutputStream fos = new FileOutputStream("day29/testBuffered/img_copy.jpg");long start = System.currentTimeMillis();int data;while((data = fis.read()) != -1){fos.write(data);}long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start) + "ms");fos.flush();fos.close();fis.close();//增强流(高效流)(0-1ms)BufferedInputStream bis = new BufferedInputStream(fis);BufferedOutputStream bos = new BufferedOutputStream(fos);long start1 = System.currentTimeMillis();byte[] bytes = new byte[1024];int pos;while((pos = bis.read(bytes)) != -1){bos.write(bytes,0,pos);}long end1 = System.currentTimeMillis();System.out.println("耗时:" + (end1 - start1));bos.flush();bos.close();bis.close();}
}

注意:

缓冲流是增强流

底层借助其他流实现功能

所以构造器要求一定要传一个字节流对象

3、缓冲字符流

专为文本处理而生

提供了很多便利功能:

  • readLine():一次性读取整行数据(不包含换行符)
  • newLine():智能换行
  • ready():检查是否还有数据可读

示例:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;public class TestBufferedChar {public static void main(String[] args) throws Exception{BufferedReader br = new BufferedReader(new FileReader("day29/testBufferedChar/test"));BufferedWriter bw = new BufferedWriter(new FileWriter("day29/testBufferedChar/test1"));String line;while((line = br.readLine()) != null){bw.write(line);//ready()返回false,可以理解为读到文件最后一行了if(br.ready()){bw.newLine();} }bw.flush();bw.close();br.close();}
}

二、标准流——程序三大通道🚦

Java为每个程序预设了三个流对象

用于处理标准输入、标准输出、标准错误

这些标准流在Java中自动创建,无需显式打开或关闭

流名称对象类型用途
标准输入流System.inInputStream接收用户输入(键盘)
标准输出流System.outPrintStream正常信息输出(控制台)
标准错误流System.errPrintStream错误信息输出(控制台)

1、标准输入流(System.in)

它是一个字节流(InputStream)

可以使用 Scanner BufferedReader 等类来读取标准输入流的数据

比如我们之前提到的 Scanner sc = new Scanner(System.in);

大学生入门:Random类及其易踩坑的点-CSDN博客https://blog.csdn.net/qq_73698057/article/details/149612440?spm=1001.2014.3001.5502

2、标准输出流(System.out)

它也是一个字节流(PrintStream)

可以使用 System.out.println(); System.out.print(); 等方法来输出数据

3、标准错误流(System.err)

它还是一个字节流(PrintStream)

比如我们异常处理时用到的 printStackTrace()方法:

public void printStackTrace(){printStackTrace(System.err);
}

4、PrintStream:输出的艺术

它可以改变输出的方向

System.out.println("默认输出到控制台");// 重定向输出到文件
PrintStream ps = new PrintStream("src/dir/c.txt");
System.setOut(ps);System.out.println("现在输出到文件了!");  // 这行会写入文件

所以拷贝文件还可以这样写,更加优雅

public class TestPrintStream{public static void main(String[] args) throws Exception{BufferedReader br = new BufferedReader(new FileReader("day29/testBufferedChar/test"));PrintStream ps = new PrintStream("day29/testBufferedChar/test1");String line;while((line = br.readLine()) != null){if(br.ready()){ps.println(line);    }else{ps.print(line);}}ps.close();br.close();}
}

这里大家可能会有疑问

既然 PrintStream 是增强流

为什么它实例化对象的时候,不需要通过字节流对象呢?

而是直接写入文件路径

PrintStream ps = new PrintStream("day29/testBufferedChar/test1");

原因可以通过源码解答:

public PrintStream(String fileName) throws FileNotFoundException{this(false, new FileOutputStream(fileName));
}

三、转换流——编码翻译官🌍

当遇到不同编码格式的文件时

转换流就像一位精通多国语言的翻译官

java.io.OutputStreamWriter:将字节输出流转换为字符输出流,并指定编码

java.io.InputStreamReader:将字节输入流转换为字符输入流,并指定编码

话不多说,直接上代码:

// 读取GBK编码文件,写入UTF-8编码文件
InputStreamReader isr = new InputStreamReader(new FileInputStream("GBK.txt"), "GBK");
BufferedReader br = new BufferedReader(isr);OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF8.txt"), "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);String line;
while((line = br.readLine()) != null) {bw.write(line);if(br.ready()) bw.newLine();
}

四、对象流——对象的休眠运输仓

为什么要叫它休眠运输仓呢?

这主要是源于它的一个机制:

1、序列化机制

用于将对象和字节序列之间进行转换

  • 序列化:将活生生的对象“冷冻”成字节序列
  • 反序列化:将冷冻的对象“解冻”复活

2、对象流介绍

  • java.io.ObjectOutputStream:将Java对象转换为字节序列,并输出到内存、文件、网络等地方
  • java.io.ObjectInputStream:从某个地方读取出对象的字节序列,并生成对应的对象

示例:

准备一个Student类,使用对象流将学生对象保存在文件中,并读取出来

public class Student {private String name;private int age;private double score;public Student(){}public Student(String name,int age){this.name = name;this.age = age;}public Student(String name,int age,double score){this.name = name;this.age = age;this.score = score;}public String getName() {return name;}public int getAge() {return age;}public double getScore() {return score;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";}
}import java.io.ObjectInputStream;public class TestObject {public static void main(String[] args) throws Exception{Student s1 = new Student("张三", 18);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day29/testObject/object.txt"));oos.writeObject(s1);oos.close();}}

看上去好像没什么问题

但是一运行发现事情不对

报错了!

抛出异常:java.io.NotSerializableException

异常信息显示 Student 的类型无法进行序列化

那我们还需要做哪些准备工作呢?

3、序列化接口

Java中,只有实现了 Serializable接口 的对象才可以进行序列化和反序列化

源码:

package java.iopublic interface Serializable{
}

我们发现这只是一个“标识”接口,并没有抽象方法需要实现

这个时候我们就可以解决上面的问题了:

public class Student implements Serializable{private String name;private int age;private double score;public Student(){}public Student(String name,int age){this.name = name;this.age = age;}public Student(String name,int age,double score){this.name = name;this.age = age;this.score = score;}public String getName() {return name;}public int getAge() {return age;}public double getScore() {return score;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";}
}//写入
import java.io.ObjectInputStream;public class TestObject {public static void main(String[] args) throws Exception{Student s1 = new Student("张三", 18);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day29/testObject/object.txt"));oos.writeObject(s1);oos.close();}
}//读取
import java.io.FileInputStream;public class TestObject {public static void main(String[] args) throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day29/testObject/object.txt"));Student s = (Student)ois.readObject();System.out.println(s);ois.close();}
}

不要掉以轻心,还有隐藏的问题:

写入对象次数要和读取次数一致

否则会抛出 EOFException 异常

4、集合序列化

现在我们实现了写入读取单个对象了

那么要是一次性写很多呢?手写不得累死

所以我们可以用到之前学习的集合来进行操作

Java集合 之 单列集合-CSDN博客

java集合 之 多列集合-CSDN博客

java 集合 之 集合工具类Collections-CSDN博客

操作思路:

  • 这时我们可以先将多个对象添加到一个集合中
  • 然后序列化集合对象
  • 反序列化就先读取集合对象
  • 然后从集合中获取所有对象

写入:

public class Test_WriteList {public static void main(String[] args) throws Exception {//1.实例化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/test/list.txt"));//2.准备多个Student对象并加入List集合Student s1 = new Student("tom",20);Student s2 = new Student("zs",21);Student s3 = new Student("jack",19);List<Student> list = new ArrayList<>();list.add(s1);list.add(s2);list.add(s3);//3.将集合写入文件oos.writeObject(list);System.out.println("write list success!");//4.操作完成后,关闭流oos.close();}
}//运行结果:
write list success!

读取:

public class Test_ReadList {public static void main(String[] args) throws IOException, ClassNotFoundException {ObjectInputStream oos = new ObjectInputStream(new FileInputStream("src/com/test/list.txt"));//2.读取集合对象List<Student> list = (List<Student>) oos.readObject();if(list == null) {System.out.println("read null");return;}System.out.println("read list size: " + list.size());//3.遍历输出for (Student stu : list) {System.out.println(stu);}//4.关闭资源oos.close();}
}//运行结果:
read list size: 3
Student{name='tom', age=20}
Student{name='zs', age=21}
Student{name='jack', age=19}

5、transient 关键字

用于修饰类中的属性

可以使对象在序列化时忽略掉这个属性

比如一些敏感属性password

我们不想它被序列化

就可以用 transient 修饰

拿上述案例为例的话,如果把 age 忽略掉

那么反序列化之后遍历,会发现年龄都是 0

6、序列化版本号

继续拿刚才的案例来说

假如我们突然反悔了

想继续序列化age了

然后把transient删了

结果编译运行报错了!

抛出异常:java.io.InvalidClassExceotion (无效的类)

异常具体信息中提到了:serialVersionUID,这个就是序列化版本号

序列化版本号:

        Java中,serialVersionUID 是一个用于序列化的静态变量

        用于表示序列化类的版本号

        它是一个长整型数字

        用于确保序列化和反序列化前后类的版本一致性

        

        当一个类实现 Serializable接口 并进行序列化时,如果没有显式定义 serialVersionUID,那么系统就会根据类的结构和内容自动生成一个默认序列化版本号

但是我们一旦修改了类的结构(新增或删除类的成员变量,修改类的继承关系等等),那么这个默认版本号就变了!所以我们刚刚会报错

我们只需要给之前的 Student类 中加上这个就OK了:

private static final long serialVersionUID = 1L;

IO流的主要流讨论完毕

接下来我们看一些特殊流

五、数据流——基本数据类型的专用通道

二进制数据有字节流

文本数据有字符流

那么基本数据类型和字符串也不能受冷落

  • DataInputStream:把读取到的字节,转化为指定类型的数据
  • DataOutputStream:把指定类型的数据,转化为字节写出去
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeLong(1000L);
dos.writeInt(5);
dos.writeUTF("Hello");DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
long l = dis.readLong();      // 1000
int i = dis.readInt();        // 5
String s = dis.readUTF();     // "Hello"out.close();
in.close();

六、随机访问流——文件的任意门

java.io.RandomAccessFile

诶!这玩意长得和之前的不一样!

它并没有继承之前提到的四个抽象父类

它既可读又可写

源码:

package java.io;public class RandomAccessFile implements DataOutput,DataInput,Closeable{//省略...//传入文件及读写方式:public RandomAccess(File file, String mode) throws FileNotFoundException;  //跳过pos个字节:public void seek(long pos) throws IOException;
}

String mode:操作的模式

毕竟它又可读又可写,所以需要确定一下具体要读还是写

        

  • “r”模式:以只读方式来打开指定文件夹,如果试图写入,会抛出:IOException异常
  • “rw”模式:以读写方式打开,如果文件不存在,则试图创建该文件
  • “rws”模式:以读写方式打开,还要求对文件内容元数据的每个更新都同步写入底层设备
  • “rwd”模式:以读写方式打开,还要求文件内容每个更新都同步写入到底层设备

元数据:描述数据的数据,比如文件大小、文件名称、图片大小等等

RandomAccessFile raf = new RandomAccessFile("file.txt", "rw");
raf.seek(6);  // 跳到第6个字节位置
raf.write("briup".getBytes());  // 在指定位置写入内容

七、Properties类——配置文件的管家

它是专门用来处理配置文件的工具类,继承自 Hashtable

配置文件通常以 .properties 为扩展名

Properties props = new Properties();
props.load(new FileInputStream("config.properties"));  // 加载配置
String value = props.getProperty("key");              // 读取配置
props.setProperty("key", "new_value");                // 修改配置
props.store(new FileOutputStream("config.properties"), "更新配置");  // 保存配置

示例:

将配置文件信息读取出来,然后修改配置文件

原配置文件信息:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;public class Test {public static void main(String[] args) {String path = "day29/homework/T3/db.properties";Properties p = new Properties();try {p.load(new FileInputStream(path));System.out.println("原始配置:");String driver = p.getProperty("driver");String url = p.getProperty("url");String username = p.getProperty("username");String password = p.getProperty("password");System.out.println("driver:" + driver);System.out.println("url:" + url);System.out.println("username:" + username);System.out.println("password:" + password);p.setProperty("driver", "com.day29.homework.T3.MyDriver");p.setProperty("url", "127.0.0.1:6666");p.setProperty("username", "root");p.setProperty("password", "123456");p.store(new FileOutputStream(path), "更新配置");} catch (Exception e) {}}
}

修改之后:

http://www.dtcms.com/a/354224.html

相关文章:

  • anaconda本身有一个python环境(base),想用别的环境就是用anaconda命令行往anaconda里创建虚拟环境
  • 英伟达 spectrum xgs 以太网 的含义和解释
  • 互联网大厂AI面试:从大模型原理到场景应用的深度解析
  • WPF 程序用户权限模块利用MarkupExtension实现控制控件显示
  • 嵌入式分层架构下的AT指令模块设计与实现
  • 使用Nginx搭建图片传输服务:配置与优化指南
  • Content-Type是application/x-www-form-urlencoded表示从前端到后端提交的是表单的形式
  • 微服务的编程测评系统17-判题功能-代码沙箱
  • 除自身以外数组的乘积是什么意思
  • 算法刷题常见错误
  • Linux 打包及压缩基础知识总结
  • 车间生产管理遇到的问题及改善方案有哪些?
  • 在 Windows 上部署 Go 语言开发环境
  • Go语言与Docker 开发的核心应用领域
  • 源码分析unexpected EOF on client connection with an open transaction
  • 分治法——二分答案
  • 深入探索Vue:前端开发的强大框架
  • Android10 音频系统之AudioPlaybackConfiguration
  • JVM之CMS、G1|ZGC详解以及选型对比
  • SynClub-百度在海外推出的AI社交产品
  • A-Level物理课程全解析:知识点、学习计划与培训机构推荐
  • 网络编程-连接、发送、接收数据学习
  • React Hooks 完全指南:从基础到高级的实战技巧
  • C++ 由 std::thread 初始化想到的
  • TencentOS Server 4.4 下创建mysql容器无法正常运行的问题
  • wireshark解析FLV插件分享
  • 嵌入式Linux(Exynos 4412)笔记
  • 3459. 最长 V 形对角线段的长度
  • 设计模式理解
  • Nishang PowerShell工具:原理详解+使用方法+渗透实战