BCSP-玄子JAVA开发之面向对 象编程CH09_ Java IO流笔记
9.1 File 类
是java.io包下代表操作与平台无关的文件和目录的类,实现对文件或目录的新建、删除、重命名等操作
File file = new File( String pathname );
// 创建File类对象时,必须设置文件路径
// pathname 文件路径
绝对路径:以根目录开头的完整路径
相对路径:相对于当前目录文件的路径
.
表示当前目录
..
表示上级目录
Windows文件路径名中,分隔符可以使用正斜杠
/
,也可以使用反斜杠\
。但必须写成\\
,其中第一个\
表示转义符
9.1.1 File类的常用方法
方法名 | 描述 |
---|---|
boolean createNewFile() | 创建新文件 |
boolean delete() | 删除文件 |
boolean exists() | 判断文件是否存在 |
Boolean isFile() | 判断是否是文件 |
boolean isDirectory() | 判断是否是目录 |
long length() | 返回文件长度,单位为字节 若文件不存在,则返回0L |
String getPath() | 返回此对象文件名所对应的路径 |
String getAbsolutePath() | 返回此对象表示的文件的绝对路径名 |
在实际开发中,如需完成对File类的更多操作,可随时查阅API帮助文档
package CH09_IO.IO;
import java.io.File;
import java.io.IOException;
public class MyIO {
public static void main(String[] args) {
File file = new File("XuanZi.txt");
if (file.exists()) {
if (file.isFile()) {
System.out.println("文件名:" + file.getName() + ",文件长度:" + file.length() + "字节。");
System.out.println("文件路径是:" + file.getPath());
System.out.println("文件绝对路径是:" + file.getAbsolutePath());
}
if (file.isDirectory()) {
System.out.println("此文件是目录");
}
} else {
System.out.println("此文件不存在!");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUMkpVnq-1676731747226)(./assets/image-20230216232759203.png)]
package CH09_IO.IO;
import java.io.File;
import java.io.IOException;
public class MyFile {
public static void main(String[] args) {
File file = new File("resources/XuanZi.txt");
// 指定文件路径
File dirs = file.getParentFile();
// 获取上级文件路径
if (!dirs.exists()) {
// 判断上级文件夹是否存在
dirs.mkdirs();
// 创建上级文件夹
}
try {
file.createNewFile();
// 创建文件
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void deleteFile(File file) {
if (file.exists()) {
file.delete();
//删除文件
System.out.println("文件删除成功!");
}
}
9.2 IO 流概述
IO流是指一连串流动的字符,以先进先出方式发送信息的通道
-
I:input,指读入操作
-
O:output,指写出操作
Java把所有流类型都封装到java.io包中,以实现输入/输出操作
9.2.1 Java 流的分类
-
按流向划分:输入流和输出流
-
按处理单元划分:字节流和字符流
-
按流的角色划分:节点流和处理流
9.2.2 按流向划分:输入流和输出流
输入流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mJVxBR7-1676731747229)(./assets/image-20230217004345541.png)]
只能从中读取数据,而不能写入数据的流,实现程序从数据源中读数据
输出流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juimZCrw-1676731747229)(./assets/image-20230217004349220.png)]
只能向其写入数据,而不能从中读数据的流,实现程序向目标数据源中写数据
-
输入流主要由InputStream和Reader作为基类
-
输出流则主要由OutputStream和Writer作为基类
-
都是抽象类,无法直接实例化对象
按照流的流向进行分类时,输入流完成数据读入操作,输出流完成数据写出操作,这里的
出
和入
一定是从程序运行所在内存的角度来论述
9.2.3 按处理单元划分:字节流和字符流
字节流
-
以8位字节为操作数据单元的流,可操作二进制数据
-
可细分为字节输入流、字节输出流
字符流
-
以16位字符为操作数据单元的流,可操作文本数据
-
可细分为字符输入流、字符输出流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHAThTWu-1676731747229)(./assets/image-20230217004450803.png)]
区别
-
操作的数据单元不同
-
使用方法几乎相同
9.2.4 按流的角色划分:节点流和处理流
节点流(包装流)
-
可以直接向一个特定的存储介质(如磁盘、文件)读写数据的流
-
使用节点流进行读写数据操作时,程序直接连接到数据源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tsvE7GEr-1676731747230)(./assets/image-20230216234219675.png)]
处理流
-
对已存在的流进行连接和封装,通过封装后的流实现数据读写操作的流
-
使用处理流进行读写操作时,程序并不会直接连接到实际的数据源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPg7ppGq-1676731747230)(./assets/image-20230216234211113.png)]
使用处理流包装节点流,通过处理流执行输入和输出功能,让节点流与文件或磁盘等存储设置交互,可隐藏底层节点流的差异
9.3 字节流
具有输入和输出操作
主要操作byte类型数据
基类
-
字节输出流类: OutputStream
-
字节输入流类:InputStream
9.3.1 字节输出流的基类:OutputStream
是抽象类,必须使用该类的子类进行实例化对象
常用方法
方法 | 描述 |
---|---|
void close() | 关闭输出流 |
void flush() | 刷新缓冲区 |
void write(byte[] b) | 将每个byte数组写入数据流 |
void write(byte[] b,int off,int len) | 将每一个指定范围的byte数组写入数据流 |
void write(int b) | 将一个字节数据写入数据流 |
如果需要操作文件,则使用FileOutputStream实例化
9.3.2 字节输出流FileOutputStream类
使用OutputStream类的FileOutputStream子类向文本文件写入数据
常用构造方法
方法 | 描述 |
---|---|
FileOutputStream( File file) | 创建向指定File对象写数据的文件输出流 file:指定目标文件的对象 |
FileOutputStream( String name) | 创建向指定路径的文件写数据的文件输出流 name:指定目标文件的路径字符串 |
FileOutputStream( String name, boolean append) | 创建一个向指定路径的文件写入数据的文件输出流 name:指定目标文件的路径字符串 append:表示是否在文件末尾追加数据。如果为true,则表示可以在文件末尾追加数据 |
9.3.3 创建文件输出流对象的常用方式
//方式一:使用File对象构造对象
File file = new File("D:\\mydoc\\test.txt");
OutputStream fos = new FileOutputStream(file);
//方式二:使用文件路径构造对象
OutputStream fos = new FileOutputStream("D:\\doc\\test.txt");
//方式三:使用文件路径构造对象,且可向文件末尾追加数据
OutputStream fos = new FileOutputStream("D:\\doc\\test.txt",true);
使用FileOutputStream类的构造方法创建对象时
如果相应的文件不存在,就会自动新建一个文件
如果参数file或name表示的文件路径是一个目录,则会抛出FileNotFoundException异常
9.3.4实现步骤
- 导入相关的类
- 构造文件输出流FileOutputStream对象
- 把数据写入文本文件
- 关闭文件输出流对象
package CH09_IO.IO;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class MyFileOutputStream {
public static void main(String[] args) throws IOException {
OutputStream fos = new FileOutputStream("resources/XuanZi.txt", true);
//准备一个字符串
String str = "I love Java!";
//将字符串转换为byte数组
byte[] words = str.getBytes();
fos.write(words, 0, words.length);
System.out.println("文件已写入成功!");
if (fos != null) {
fos.close();
}
}
}
创建输出流对象、执行写操作、关闭输出流时都可能会有IOException异常发生
因此,除处理FileNotFoundException异常外,还需处理IOException异常
9.3.5 字节输入流的基类:InputStream
从文件中读数据,与OutputStream一样,也是抽象类
常用方法
方法 | 描述 |
---|---|
int read() | 读取一个字节数据 |
int read(byte[] b) | 将数据读取到字节数组中 |
int read(byte[] b,int off,int len) | 从输入流中读取最多len长度的字节,保存到字节数组中,保存的位置从off开始 |
void close() | 关闭输入流 |
int available() | 返回输入流读取的字节数 |
9.3.6 字节输入流 FileInputStream 类
使用InputStream类的FileInputStream子类实现文本文件内容的读取
常用构造方法
方法 | 描述 |
---|---|
FileInputStream(File file) | 用于创建从指定File对象读取数据的文件输入流 file:指定目标文件数据源对象 |
FileInputStream( String name) | 用于创建从指定路径的文件读取数据的文件输入流 name:指定目标文件数据源路径字符串 |
9.3.7 创建文件输入流对象的常用方式
//方式一:使用File对象构造对象
File file = new File("D:\\doc\\test.txt");
InputStream fis = new FileInputStream(file);
//方式二:使用文件路径构造对象
InputStream fis = new FileInputStream("D:\\doc\\test.txt");
从文件读取到计算机内存中的过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGHmuMLw-1676731747230)(./assets/image-20230217005442035.png)]
read()方法
-
每次读取一个字节(0~255的整数)
-
每次读取多个字节
读取文件中全部数据
while( (data=fis.read()) != -1 ) {
// 判断是否到文件尾
System.out.print( (char) data);
// 强制类型转换
}
按字节读取并显示数据时需进行强制类型转换
使用read()读取文件中的数据时,当返回结果为-1时,即输入流已经读到末尾
在创建输入流对象、读取数据、关闭流时必须进行异常处理
9.3.8 实现步骤
- 构造文件输入流FileInputStream对象
- 读取文本文件中数据
- 关闭文件输入流对象
package CH09_IO.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyFileInputStream {
public static void main(String[] args) throws IOException {
File file = new File("resources/XuanZi.txt");
FileInputStream fis = new FileInputStream(file);
System.out.println("可读取的字节数:" + fis.available());
System.out.println("----------");
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
String s = new String(bytes, 0, len);
System.out.println(s);
}
fis.close();
}
}
文本文件保存时,采用GBK编码方式,每个中文占2个字节
read()方法一次读取1个字节(半个中文字符)
当数组长度不足时,可能导致中文乱码
9.4 字符流
一个字符占用内存的两个字节
-
当输入和输出是文本文件时,尽量使用字符流
-
使用Reader类和Writer类操作字符
基类
-
字符输出流类: Writer
-
字符输入流类:Reader
使用字符流读写文本更合适
9.4.1 字符输出流的基类:Writer
抽象类
常用方法
方法 | 描述 |
---|---|
void write(String str) | 将str字符串中包含的字符输出到输出流中 |
void write(String str, int off, int len) | 将字符串中从off位置开始,长度为len的字符输出到输出流中 |
void close() | 关闭输出流 |
void flush() | 刷新输出流 |
9.4.2 字符输出流FileWriter类
- 导入相关的类
- 创建FileWriter对象
- 使用write()方法向文本文件写数据
- 清空流
- 关闭流
package CH09_IO.IO;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class MyFileWrite {
public static void main(String[] args) throws IOException {
File file = new File("resources/XuanZi.txt");
FileWriter fw = new FileWriter(file,true);
fw.write("我能写字符串了");
fw.flush();
// 刷新
fw.close();
}
}
9.4.3 字符输入流基类:Reader
是抽象类
常用方法
方法 | 描述 |
---|---|
int read() | 从输入流中读取单个字符,返回所读取的字符数据 |
int read(char[] c) | 从输入流中最多读取c.length个字符数据,并将其存储在字符数组c中,返回实际读取的字符数 |
int read(char[] c, int off, int len) | 从输入流中最多读取len个字符的数据,并将其存储在字符数组c中 存入数组c中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数 |
9.4.4 字符输入流FileReader类
- 导入相关的类
- 创建FileReader对象
- 循环使用read()方法读取文件中数据
- 关闭流
package CH09_IO.IO;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class MyFileReader {
public static void main(String[] args) throws IOException {
File file = new File("resources/XuanZi.txt");
FileReader fr = new FileReader(file);
System.out.println("-----------");
char[] c = new char[1024];
int len = 0;
while ((len = fr.read(c)) != -1) {
String str = new String(c, 0, len);
System.out.println(str);
}
fr.close();
}
}
9.5 缓冲流
java.io包提供了缓冲流,高读写文件数据的执行效率
Java缓冲流自身并不具有IO功能,只是在别的流上增加缓冲,以提高程序性能
分类
-
字节缓冲流
-
字符缓冲流
-
BufferedWriter类
-
BufferedReader类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VUyMqVoE-1676731747231)(./assets/image-20230218193712672.png)]
9.5.1 BufferedWriter类
是Writer类的子类,带有缓冲区
默认情况下,只有缓冲区满的时候,才会把缓冲区的数据真正写到目的地,能减少物理写数据的次数,提高输入/输出操作的执行效率
常用的构造方法
方法 | 描述 |
---|---|
BufferedWriter(Writer out) | 创建一个缓冲字符输出流 |
使用FileWriter类与BufferedWriter类,可提高字符流写文本文件的效率
9.5.2 实现步骤
- 导入相关的类
- 构造FileWriter对象和BufferedWriter对象
- 调用write()方法写数据
- 清空、关闭流对象
package CH09_IO.IO;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class MyBufferedWriter {
public static void main(String[] args) throws IOException {
File file = new File("resources/XuanZi.txt");
FileWriter fw = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("玄子啊啊啊");
bw.flush();
bw.close();
}
}
9.5.3 BufferedReader类
Reader类的子类
带有缓冲区,提高文件读取的效率
-
把一批数据读到缓冲区
-
从缓冲区内获取数据
常用构造方法
方法 | 描述 |
---|---|
BufferedReader(Reader in) | 创建一个缓冲字符输入流 |
readLine()方法
- 按行读取文件中数据
9.5.4 实现步骤
- 导入相关的类
- 构造FileReader对象和BufferedReader对象
- 调用readLine()方法读取数据
- 清空、关闭流对象
package CH09_IO.IO;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReader {
public static void main(String[] args) throws IOException {
File file = new File("resources/XuanZi.txt");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
// br.readLine();
String str = "";
while ((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
}
}
9.6 数据操作流
对二进制文件读写操作
DataOutputStream类
-
OutputStream类的子类
-
与FileOutputStream类结合使用写二进制文件
使用DataOutputStream类写二进制文件的实现步骤与FileOutputStream类写文本文件相似
DataInputStream类
-
InputStream类的子类
-
与FileInputStream类结合使用读取二进制文件
使用DataInputStream类读取二进制文件的实现步骤与FileInputStream类读取文本文件相似
9.6.1DataOutputStream类
按照与平台无关的方式向流中写入基本数据类型的数据,如int、float、long、double和boolean等
使用writeUTF()方法能写入采用utf-8字符编码的字符串
FileOutputStream out1 = new FileOutputStream("D:\\doc\\test.txt");
BufferedOutputStream out2 = new BufferedOutputStream(out1);
DataOutputStream out = new DataOutputStream(out2);
out.writeByte(1);
out.writeLong(2);
out.writeChar('c');
out.writeUTF("hello");
9.6.2 DataInputStream类
按照与平台无关的方式从流中读取基本数据类型的数据,如int、float、long、double和boolean等
使用readUTF( )方法能读取采用utf-8字符编码的字符串
FileInputStream in1 = new FileInputStream("D:\\doc\\test.txt");
BufferedInputStream in2 = new BufferedInputStream(in1);
DataInputStream in = new DataInputStream(in2);
System.out.println(in.readByte());
System.out.println(in.readLong());
System.out.println(in.readChar());
System.out.println(in.readUTF());
9.6.3 实现步骤
写二进制文件
- 导入相关的类
- 构造数据输出流对象
- 调用write()方法写二进制文件
- 关闭数据输出流
读二进制文件
- 导入相关的类
- 构造数据输入流对象
- 调用read ()方法读取二进制文件
- 关闭数据输入流
package CH09_IO.IO;
import java.io.*;
public class MyData {
public static void main(String[] args) throws IOException {
DataOutputStream dos = null;
DataInputStream dis = null;
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//创建输入流文件
fis = new FileInputStream("D:\\doc\\User.class");
dis = new DataInputStream(fis);
//创建输出流文件
fos = new FileOutputStream("D:\\doc\\newUser.class");
dos = new DataOutputStream(fos);
int temp;
while (((temp = dis.read()) != -1)) {
fos.write(temp);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
//省略异常处理代码
dis.close();
fis.close();
fos.close();
dos.close();
}
}
}
9.7 序列化与反序列化
为每个对象属性一一编写读写代码,过程很繁琐且非常容易出错
使用序列化和反序列化,方便数据传输和存储
-
序列化是将对象的状态存储到特定的存储介质中的过程
-
反序列化是将特定的存储介质中数据重新构建对象的过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJUqg3Lo-1676731747231)(./assets/image-20230218194926286.png)]
对象输出流( ObjectOutputStream ):实现序列化
对象输入流( ObjectInputStream):实现反序列化
9.7.1 使用ObjectOutputStream类实现序列化
序列化的对象所属类须为可序列化的类
一个类实现java.io.Serializable接口,该类的对象是可序列化的
public interface Serializable{}
JDK1.8类库中,有些类实现了java.io.Serializable接口
- 如:String类、包装类和日期时间类等
常用方法
方法 | 描述 | 类型 |
---|---|---|
ObjectOutputStream(OutputStream out) | 创建对象输出流对象 | 构造方法 |
final void writeObject(Object obj) | 将指定对象写入流 | 实例方法 |
创建一个Person类,并标记该类的对象是可序列化的
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
private String sex;
public void print() {
System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
}
}
9.7.2 解决序列化和反序列化的版本不一致问题
引入serialVersionUID常量
-
serialVersionUID常量为long类型
-
JVM在编译时自动生成serialVersionUID常量,也可显式定义
在Person类中显式定义serialVersionUID常量
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID=1L;
// 序列化和反序列化时serialVersionUID常量值需一致
private String name;
private int age;
private String sex;
public void print() {
System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
}
}
9.7.3 实现步骤
- 导入相关的类
- 创建可序列化的类,要求实现Serializable接口
- 创建对象输出流(ObjectOutputStream)
- 输出可序列化对象
- 关闭对象输出流
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:\\doc\\persons.bat"));
// 创建流
Person person = new Person("杰米", 25, "男");
Person person2 = new Person("Lisa", 30, "女");
ArrayList<Person> list = new ArrayList<Person>();
list.add(person);
list.add(person2);
oos.writeObject(list);
// 集合对象序列化
System.out.println("序列化成功!");
}catch(IOException ex){
ex.printStackTrace();
}finally {
if(oos!=null){
try{
oos.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
}
9.7.4 使用ObjectInputStream类实现反序列化
使用对象输出流ObjectInputStream可以还原序列化的对象
常用方法
方法 | 描述 | 类型 |
---|---|---|
ObjectInputStream(InputStream in) | 创建对象输入流对象 | 构造方法 |
final Object readObject() | 从指定位置读取对象 | 实例方法 |
final Object readObject()
返回一个Object类型的对象,如果确定该Object对象的真实类型,则可以将该对象强制转换成其真实类型
9.7.5 实现步骤
- 导入相关的类
- 创建一个对象输入流(ObjectInputStream)
- 输出反序列化后对象信息
- 关闭对象输入流
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("D:\\doc\\persons.bat"));
ArrayList<Person> list = (ArrayList<Person>)ois.readObject();
// 反序列化,进行强制类型转换
for(Person person:list) {
person.print();
}
//输出转换反序列化后的对象信息
}catch(ClassNotFoundException ex) {
ex.printStackTrace();
} catch(FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (ois != null)
ois.close();
}catch (IOException ex) {
ex.printStackTrace();
}
}
反序列化时,readObject()方法返回的是Object对象需强制类型转换为Person对象
如果使用序列化方式向文件中写入多个对象,那么反序列化恢复对象时,也是按照写入的顺序读取
如果一个可序列化的类有多个父类(包括直接或间接父类),则这些父类要么是可序列化的,要么有无参数的构造;否则,将会抛出异常
9.7.6 限制序列化
出于安全考虑,对于一些比较敏感的信息(如用户密码),应限制被序列化
使用transient关键字修改不需要序列化的对象属性
希望Person类对象中的年龄信息不被序列化
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private transient int age;
// 被限制序列化
private String sex;
public void print() {
System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
}
}
9.8 IO 总结
9.8.1 Java常用IO流
分类 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
基类 | OutputStream | InputStream | Writer | Reader |
文件流 | FileOutputSteam | FileInputStream | FileWriter | FileReader |
缓冲流 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
对象流 | ObjectOutputStream | ObjectInputStream | - | - |
数据操作流 | DataOutputStream | DataInputStream | - | - |
-
所有的基类都是抽象类,无法直接创建实例,需要借助其实现类
-
所有输出流实现写数据,所有输入流实现读数据
-
输入和输出是相对程序而言
-
所有的文件流直接与存储介质关联,需指定物理节点
在操作文本文件时,应使用字符流
字节流可以处理二进制数据,它的功能比字符流更强大
如果是二进制文本,应考虑使用字节流
BCSP-玄子JAVA开发之面向对 象编程CH09_ Java IO流笔记 2.18