JavaSE进阶——玩转IO流

news/2024/11/15 2:07:24/

文章目录

  • 前言
  • 一、File类介绍
    • 1、概念引入
    • 2、实际应用
      • 2.1 操作文件
      • 2.2 操作文件夹
  • 二、IO流介绍
  • 三、字符流
    • 1、读文件
      • 1.1 一次读一个
      • 1.2 一次读多个,使用char数组去装
    • 2、写文件
      • 2.1 一次写一个
      • 2.2 一次写完,使用字符数组
    • 3、文件复制
      • 3.1 综合应用
      • 3.2 使用缓冲流提高效率
  • 四、字节流
    • 1、读文件
      • 1.1 一次读一个
      • 1.2 一次读多个,使用byte数组去装
    • 2、写文件
      • 2.1 一次写一个
      • 2.2 一次写多个,使用byte数组
    • 3、使用缓冲流完成文件复制
  • 五、缓冲流
  • 六、转换流
    • 1、读文件
    • 2、复制文件
  • 七、System流
    • 1、System.in
      • 1.1 从键盘录入
      • 1.2 Scanner
    • 2、System.out
    • 3、从键盘录入内容输出到文件中
  • 八、对象流
    • 1、写文件
      • 1.1 写String和基本数据类型
      • 1.2 写自定义对象
    • 2、读文件
    • 3、序列化&反序列化
      • 3.1 什么是序列化?反序列化又是什么?
      • 3.2 序列化是全部对象内容都序列化?
      • 3.3 认识serialVersionUID
  • 九、数据流

前言

📢 大家好,我是程序员Forlan,本篇内容主要分享IO流方面的知识,属于基础内容,一方面是新人必备知识,另一方面也方便自己和大家后续查找相关资料

一、File类介绍

1、概念引入

首先来介绍两个概念

  • 文件:为了便于数据的管理和检索,引入了文件,本质上都是字节组成,0和1,只不过有不同的形态(文本、图片、视频…)
  • 文件夹:为了便于对文件进行管理和使用,引入了文件夹,不同文件放到不同文件夹,分门别类

对于面向对象编程,我们操作的是对象,盘符上的文件/文件夹,需要封装为对象,我们才能进行操作,这个对象就是File,通过File类,我们可以获取文件/文件夹的各种信息,操作文件/文件夹

2、实际应用

2.1 操作文件

File f1 = new File("D:\\text.txt");
File f2 = new File("D:/text.txt");
/*** File.separator,本地文件系统的名称分隔符:在UNIX系统上,这个字段的值是'/';在Microsoft Windows系统上,它是'\\'* 建议使用这种*/
File f3 = new File("d:" + File.separator + "text.txt");
if (f1.exists()) {// 如果文件存在,将文件删除操作f1.delete();
} else {// 如果文件不存在,就创建这个文件f1.createNewFile();
}
System.out.println("文件是否可读:" + f1.canRead());
System.out.println("文件是否可写:" + f1.canWrite());
System.out.println("文件的名字:" + f1.getName());
System.out.println("上级目录:" + f1.getParent());
System.out.println("是否是一个目录:" + f1.isDirectory());
System.out.println("是否是一个文件:" + f1.isFile());
System.out.println("是否隐藏:" + f1.isHidden());
System.out.println("文件的大小:" + f1.length());
System.out.println("是否存在:" + f1.exists());
System.out.println("绝对路径:" + f1.getAbsolutePath()); // 完整路径
System.out.println("相对路径:" + f1.getPath()); // 相对某个参照物的路径,在junit的测试方法中,相对路径指的就是相对模块,就是模块名之后的路径
System.out.println("toString:" + f1.toString()); // 相对路径
System.out.println(f1 == f2);//比较两个对象的地址
System.out.println(f1.equals(f2));//比较两个对象对应的文件的路径

2.2 操作文件夹

基本文件的命令,它也可以用,这里就不演示重复的了

File f = new File("D:\\forlan");
// 创建单层目录,前提:之前的目录已经存在,不然创建失败
f.mkdir();
// 创建多层目录
f.mkdirs();
// 删除:只会删除一层,前提:这层目录是空的,里面没有内容,如果有内容就不会被删除
f.delete();
// 获取当前文件夹下的文件或文件夹,不包括下一级
// File[] files = f.listFiles();
String[] list = f.list();
for (String s : list) {System.out.println(s);
}

二、IO流介绍

前面提到的FIle类,可以获取以及操作文件/文件夹表层信息,但拿不到里面的内容,这时候就出现了I/O流,用于处理设备之间的数据的传输,可以简单理解为一根“管”,连接不同东西进行传输

分类

  • 按照流的方向,分为:输入流、输出流
  • 按照处理数据的单位,分为:字节流、字符流
    注:字节流可以理解细管,字符流可以理解为粗管,传输大小不一样

所以按照不同的方向组合,就出现了4个基类:字节输入流、字节输出流、字符输入流、字节输出流
针对这4个抽象基类,又可以延伸出来不同类型的实现,具体如下图:

在这里插入图片描述

下面我们就针对不同的实现,来看看具体代码如何写

三、字符流

Windows的记事本默认保存的ANSI,也就是GBK,我们要转为UTF-8,它不支持,但我们可以使用转换流,这个后面会讲

1、读文件

1.1 一次读一个

// 读文件,先得到文件对象
File f = new File("D:\\text.txt");
// Windows的记事本默认保存的ANSI,也就是GBK,我们要转出UTF-8
try (FileReader fr = new FileReader(f);) {int n;// 如果到了文件的结尾处,那么读取的内容为-1while ((n = fr.read()) != -1) {System.out.print((char) n);}
} catch (IOException e) {e.printStackTrace();
}

1.2 一次读多个,使用char数组去装

// 读文件,先得到文件对象
File f = new File("D:\\text.txt");
try (FileReader fr = new FileReader(f);) {// 使用数组,一次性读10个char[] ch = new char[10];int len = fr.read(ch);// 如果到了文件的结尾处,那么读取的内容为-1while (len != -1) {System.out.print(new String(ch, 0, len));len = fr.read(ch);}
} catch (IOException e) {e.printStackTrace();
}

代码中的new String(ch, 0, len)可以写成new String(ch, 0, ch.length)?
答案肯定是不行的,会重复读取内容,比如最后len=2,实际上只需要展示2个字符,但还是展示了10个字符,8个是之前的字符

2、写文件

FileWriter说明:

  • 如果目标文件不存在,会自动创建文件
  • 如果目标文件存在:
    • new FileWriter(f),:对原文件覆盖
    • new FileWriter(f,true) : 在原文件末尾追加`

2.1 一次写一个

// 写的文件对象
File f = new File("D:\\text.txt");
try (FileWriter fw = new FileWriter(f, true);) {String content = "你好,程序员Forlan";for (int i = 0; i < content.length(); i++) {fw.write(content.charAt(i));}
} catch (IOException e) {e.printStackTrace();
}

2.2 一次写完,使用字符数组

// 写的文件对象
File f = new File("D:\\text.txt");
try (FileWriter fw = new FileWriter(f, true);) {String content = "你好,程序员Forlan";// 转为字符数据,一次性写完char[] chars = content.toCharArray();fw.write(chars);
} catch (IOException e) {e.printStackTrace();
}

3、文件复制

3.1 综合应用

// 源文件
File f1 = new File("D:\\text.txt");
// 目标文件
File f2 = new File("D:\\textCopy.txt");
try (FileReader fr = new FileReader(f1); FileWriter fw = new FileWriter(f2);) {char[] ch = new char[10];int len = fr.read(ch);while (len != -1) {fw.write(ch, 0, len);len = fr.read(ch);}
} catch (IOException e) {e.printStackTrace();
}

3.2 使用缓冲流提高效率

缓冲流:BufferedReader、BufferedWriter ,这个后面会讲到

// 源文件
File f1 = new File("D:\\text.txt");
// 目标文件
File f2 = new File("D:\\textCopy.txt");
try (FileReader fr = new FileReader(f1); FileWriter fw = new FileWriter(f2);BufferedReader br = new BufferedReader(fr); BufferedWriter bw = new BufferedWriter(fw);) {char[] ch = new char[10];int len = br.read(ch);while (len != -1) {bw.write(ch, 0, len);len = br.read(ch);}
} catch (IOException e) {e.printStackTrace();
}

四、字节流

字节流,一般用来操作非文本文件,因为中文字符,不同编码,所占用的字节不同,文本文件,建议使用字符流操作
文本文件:txt、java、c…
非文本文件:jpg、mp3 、mp4…

1、读文件

1.1 一次读一个

File f = new File("D:\\forlan.png");
try (FileInputStream fis = new FileInputStream(f);) {int len = fis.read();// 如果到了文件的结尾处,那么读取的内容为-1while (len != -1) {System.out.print(len);len = fis.read();}
} catch (IOException e) {e.printStackTrace();
}

1.2 一次读多个,使用byte数组去装

File f = new File("D:\\forlan.png");
try (FileInputStream fis = new FileInputStream(f);) {byte[] bytes = new byte[1024];int len = fis.read(bytes);while (len != -1) {System.out.print(new String(bytes, 0, len));len = fis.read(bytes);}
} catch (IOException e) {e.printStackTrace();
}

2、写文件

非文本很难写完整,下面我们以复制的例子来演示写操作

2.1 一次写一个

File f1 = new File("D:\\forlan.png");
File f2 = new File("D:\\forlanCopy.png");
try (FileInputStream fis = new FileInputStream(f1); FileOutputStream fos = new FileOutputStream(f2);) {int len = fis.read();while (len != -1) {fos.write(len);len = fis.read();}
} catch (IOException e) {e.printStackTrace();
}

2.2 一次写多个,使用byte数组

File f1 = new File("D:\\forlan.png");
File f2 = new File("D:\\forlanCopy.png");
try (FileInputStream fis = new FileInputStream(f1); FileOutputStream fos = new FileOutputStream(f2);) {byte[] bytes = new byte[1024];int len = fis.read(bytes);while (len != -1) {fos.write(bytes, 0, len);len = fis.read(bytes);}
} catch (IOException e) {e.printStackTrace();
}

3、使用缓冲流完成文件复制

File f1 = new File("D:\\forlan.png");
File f2 = new File("D:\\forlanCopy.png");
try (FileInputStream fis = new FileInputStream(f1); FileOutputStream fos = new FileOutputStream(f2);BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos);) {byte[] bytes = new byte[1024];int len = bis.read(bytes);while (len != -1) {bos.write(bytes, 0, len);len = bis.read(bytes);}
} catch (IOException e) {e.printStackTrace();
}

五、缓冲流

无论是字符流,还是字节流,为什么要用缓冲区?
假设有500b的文件,我们来看不同写法,具体是怎么操作的
一个个读写,要操作500次
利用byte[10],要操作50次
利用缓冲区,直接一次性全部读到缓冲区,底层会帮我们刷新缓冲区到磁盘
所以,很简单,缓冲区效率高

六、转换流

作用:将字节流和字符流进行转换,属于一个处理流
InputStreamReader :字节输入流 ——》字符的输入流
OutputStreamWriter :字符输出流 ——》字节的输出流

1、读文件

前面我们提到,我们读取的文件内容为乱码,使用转换流可以指定编码,正确展示

File f = new File("D:\\text.txt");
try (FileInputStream fis = new FileInputStream(f);InputStreamReader isr = new InputStreamReader(fis);) {// 可以指定一个编码,不知道的话,默认是程序本身的编码utf-8// InputStreamReader isr = new InputStreamReader(fis,"utf-8");char[] ch = new char[10];int len = isr.read(ch);while (len != -1) {System.out.print(new String(ch, 0, len));len = isr.read(ch);}
} catch (IOException e) {e.printStackTrace();
}

2、复制文件

// 源文件
File f1 = new File("D:\\text.txt");
// 目标文件
File f2 = new File("D:\\textCopy.txt");
try (FileInputStream fis = new FileInputStream(f1); InputStreamReader isr = new InputStreamReader(fis, "utf-8");FileOutputStream fos = new FileOutputStream(f2); OutputStreamWriter osw = new OutputStreamWriter(fos);) {char[] ch = new char[10];int len = isr.read(ch);while (len != -1) {osw.write(ch, 0, len);len = isr.read(ch);}
} catch (IOException e) {e.printStackTrace();
}

七、System流

System.in:标准输入流——》默认从键盘录入
System.out:标准输出流

1、System.in

1.1 从键盘录入

读到的东西,会被转为ascall码

InputStream in = System.in;
int n = in.read();

1.2 Scanner

扫描键盘

Scanner sc = new Scanner(System.in);
int num = sc.nextInt();

扫描文件

Scanner sc = new Scanner(new FileInputStream(new File("D:\\text.txt")));
while (sc.hasNext()) {System.out.print(sc.next());
}

2、System.out

// 写法1
PrintStream out = System.out;
out.print("程序员forlan");// 写法2
System.out.println("程序员forlan");

3、从键盘录入内容输出到文件中

// 目标文件
File f = new File("D:\\text.txt");
// 属于字节流
InputStream in = System.in;
try (InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr);FileWriter fw = new FileWriter(f); BufferedWriter bw = new BufferedWriter(fw);) {String s = br.readLine();while (!s.equals("quit")) {bw.write(s);bw.newLine();s = br.readLine();}
} catch (Exception e) {e.printStackTrace();
}

八、对象流

对象流:ObjectInputStream,ObjectInputStream
作用:用于存储和读取基本数据类型数据或对象的处理流
亮点:可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来

1、写文件

可以指定写入什么数据类型
在这里插入图片描述

1.1 写String和基本数据类型

File f = new File("D:\\text.txt");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));) {oos.writeUTF("程序员forlan");oos.writeInt(100);
} catch (IOException e) {e.printStackTrace();
}

1.2 写自定义对象

1)自定义对象

public class Forlan implements Serializable {private String name;private int level;public Forlan(String name, int level) {this.name = name;this.level = level;}
}

2)写入文件

File f = new File("D:\\text.txt");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));) {Forlan forlan = new Forlan("程序员Forlan", 100);oos.writeObject(forlan);
} catch (IOException e) {e.printStackTrace();
}

我们注意到一个细节,自定义对象实现了Serializable接口 ,不实现就会报错,如下:

java.io.NotSerializableException: cn.lw.io.Forlanat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at cn.lw.io.ObjectStream.writeObject(ObjectStream.java:31)at cn.lw.io.ObjectStream.main(ObjectStream.java:13)

2、读文件

我们写入什么内容,都可以把指定类型的数据读出来
在这里插入图片描述
具体代码

File f = new File("D:\\text.txt");
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));) {System.out.println(ois.readUTF());
} catch (Exception e) {e.printStackTrace();
}

3、序列化&反序列化

前面我们提到,自定义对象必须实现Serializable接口,否则,去写文件就会报错
这个接口里是空的,是一个标识接口,实现这个接口的类对象才能序列化

public interface Serializable {
}

3.1 什么是序列化?反序列化又是什么?

像ObjectOutputStream类,是把内存中的Java对象转换成二进制数据,然后就可以把数据保存在磁盘上,相反,ObjectInputStream类,是把磁盘上的二进制数据转换成Java对象进行操作

序列化:数据被转换成二进制数据的过程
反序列化:数据从二进制数据恢复成原样的过程

注:写文件的对象需要序列化,网络传输的对象也需要序列化(比如rpc请求)

3.2 序列化是全部对象内容都序列化?

1)被序列化的类,内部的所有属性,必须是可序列化的

基本数据类型、String都是可序列化的,但自定义对象需要实现Serializable

2)static,transient修饰的属性不可以被序列化

不可以被序列化,指的是属性内容不被保存或传输,反序列化出来的内容为空

3.3 认识serialVersionUID

凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态常量,serialVersionUID,用来表明类的不同版本间的兼容性
1)作用
对序列化对象进行版本控制,有关反序加化时会检测是否兼容
2)生成时机
没有显示声明的话,它的值是Java运行时环境根据类的内部细节自动生成的,若类的实例变量做了修改,serialVersionUID 可能发生变化,所以建议还是显式声明

简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)

举个栗子:
原来Forlan类不加toString方法,进行序列化,也就是写文件

public class Forlan implements Serializable {private String name;private int level;public Forlan(String name, int level) {this.name = name;this.level = level;}// @Override// public String toString() {// 	return "Forlan{" +// 			"name='" + name + '\'' +// 			", level=" + level +// 			'}';// }
}

打开注释,再加上toString方法,进行反序列化会报错

java.io.InvalidClassException: cn.lw.io.Forlan; local class incompatible: stream classdesc serialVersionUID = 113793900596523614, local class serialVersionUID = -7076236456819894490at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1880)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1746)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2037)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1568)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)at cn.lw.io.ObjectStream.readObject(ObjectStream.java:20)at cn.lw.io.ObjectStream.main(ObjectStream.java:14)

解决,加serialVersionUID,因为值固定后,无论我们怎么改变类内容,都不会变,也就不会报错了,如果不加,serialVersionUID会隐式加,跟随类内部细节自动生成

IDEA中配置序列化版本号
在这里插入图片描述

九、数据流

数据流:用来操作基本数据类型和字符串的
DataInputStream:将文件中存储的基本数据类型和字符串 写入 内存的变量中
DataOutputStream: 将内存中的基本数据类型和字符串的变量 写出 文件中

写文件

File f = new File("D:\\text.txt");
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(f));) {dos.writeUTF("程序员Forlan");dos.writeBoolean(false);dos.writeInt(100);
} catch (Exception e) {e.printStackTrace();
}

读文件

File f = new File("D:\\text.txt");
try (DataInputStream dis = new DataInputStream(new FileInputStream(f));) {System.out.println(dis.readUTF());System.out.println(dis.readBoolean());System.out.println(dis.readInt());
} catch (Exception e) {e.printStackTrace();
}

http://www.ppmy.cn/news/367540.html

相关文章

三星N148上网本上安装windows 2000

一、问题的提出&#xff1a; 买了一本三星N148上网本半年了&#xff0c;心情好&#xff0c;6月25日要安装WINDOWS2000系统&#xff0c;发现附带的驱动光盘唯一可以安装的仅仅是有线网卡&#xff0c;其它的均无法进行驱动安装。 二、问题的解决&#xff1a; 1.无线网卡问题 由于…

三星N148上网本如何设置从U盘启动

三星N148上网本买来时只有DOS系统&#xff0c;本人又没有USB光驱&#xff0c;只能从U盘安装WINDOWS。 先得把启动优先设为U盘。方法如下&#xff1a; 1、开机&#xff0c;等引导的三星徵标出来后&#xff0c;按F2进入BIOS设置程序。 2、选其中的Boot页面&#xff0c;选中Boot D…

输电线路可视化监拍装置硬件解决方案

老旧输电线路可视化监控装置 随着我国人口的增长&#xff0c;电力设施的规模也变得越发庞大&#xff0c;人工运检的负担也越来越沉重&#xff0c;而且巡检的时效性也是痛点&#xff0c;于是电网提出智慧可视化管理通道运检的方案&#xff0c;线路在线监测装置成为其基础&#x…

STM32开发环境搭建工程创建(嵌入式学习)

STM32开发环境搭建&工程创建 1. 开发环境搭建1.1 STM32CubeMX简介安装 1.2 Keil5简介安装 1.3 ST_LINK简介安装 2. 创建STM32工程 1. 开发环境搭建 1.1 STM32CubeMX 简介 STM32CubeMX是STMicroelectronics公司提供的一款集成开发环境&#xff08;IDE&#xff09;工具&…

android手机照片设壁纸太大,Android手机上设置一个特定的图片作为壁纸会导致手机崩溃...

上周&#xff0c;我们报道了在一些Android手机上设置一个特定的图片作为壁纸会导致手机崩溃&#xff0c;并陷入显示开关的循环中&#xff0c;使其无法通过锁定屏幕。它影响了大多数安卓手机&#xff0c;尤其是三星智能手机。 人们发现问题在于图像的格式。它没有使用Android首选…

手机全部零件名称图片_手机“透视”壁纸来了,换上试试

最近拆机壁纸非常受欢迎&#xff0c;将iPhone的拆机图片作为锁屏或主屏壁纸&#xff0c;看起来就像是将手机拆开了一般&#xff0c;内部主板和零件全部都能清晰可见&#xff0c;瞬间拥有“透视”效果&#xff01; - 本期壁纸机型只有iPhone&#xff0c;安卓版暂无 - 这组iPhone…

Spring Security--会话管理

就像登录qq一样&#xff0c;一个手机登录会将另外一个手机挤下线&#xff0c;这个就叫会话管理。 这个东西非常简单&#xff0c;在默认情况下可以登录n多次&#xff0c;一旦开启&#xff0c;就不允许登录多个。 什么是一个会话。 我们简单理解就是一个浏览器的同一个用户算一…

Python学习笔记酷游的地址: 操作word docx文件之docx模组功能快览及程式示范

今天我们来讲Python&#xff0c;【酷游娜娜K͜W͜9㍠N͜E͜T͜地址提供】操作文件以及模组功能快览方式&#xff0c;文章最后有秀程式的示范给大家看哦&#xff01;话不多说让我们继续看下去 Document constructor (文件建构器) 语法说明document Document()开启新文件docum…