八、Java I/O
8.1 File 类
File 类可以实现对文件或目录的新建、删除、重命名等操作
8.1.1 File 类概述
- 程序中,一个 File 对象代表一个文件或目录
- File 对象可以查出与文件相关的信息,如名称、修改日期、文件大小等
- 定义 File 类语法
File file对象名 = new File("文件路径")
- File 文件路径的注意点
- 文件路径可以中的路径分隔符可以使用正斜杠 “/”,如"C:/test.txt"
- 同时文件路径分隔符也可以使用反斜杠 “\”, 但是必须写成 “\\”,如"C:\\test.txt"
- 文件路径可以是相对路径和绝对路径
- 绝对路径是以根目录开头的完整路径
- 相对路径是以当前程序的文件的相对位置路径
- File 类的常用方法
方法 | 描述 |
---|---|
boolean createNewFile() | 创建新文件 |
boolean delete() | 删除文件 |
boolean exists() | 判断文件是否存在 |
Boolean isFile() | 判断是否是文件 |
boolean isDirectory() | 判断是否是目录 |
long length() | 返回文件长度,单位为字节若文件不存在,则返回0L |
String getPath() | 返回此对象文件名所对应的路径 |
String getAbsolutePath() | 返回此对象表示的文件的绝对路径名 |
8.1.2 使用File 类的各种方法
完成对 File 对象的基本判断。创建文件对象,判断文件是否存在,输出路径、文件名、长度及判断 File 对象存在内容是否为文件
import java.io.File;
public class Test01 {
public static void main(String[] args) {
//创建文件对象,输入文件相对路径
File file=new File("test.txt");
//创建文件
//createNewFile() 方法必须处理异常,否则无法通过编译
try {
file.createNewFile();
}catch (Exception e){
System.out.println("异常");
}
//判断是否有此文件
System.out.println("文件test.txt是否存在:"+file.exists());
//输出文件名
System.out.println("文件名为:"+file.getName());
//输出文件相对路径
System.out.println("文件相对路径为:"+file.getPath());
//输出文件绝对路径
System.out.println("文件绝对路径为:"+file.getAbsolutePath());
//判断文件是否是目录
System.out.println("文件是否是目录:"+file.isDirectory());
//判断文件是否是文件
System.out.println("文件是否是文件:"+file.isFile());
//输出文件长度
System.out.println("文件长度为:"+file.length());
}
}
运行结果
文件test.txt是否存在:true
文件名为:test.txt
文件相对路径为:test.txt
文件绝对路径为:C:\BeiDaQingNiao\S2\JavaOOP\test.txt
文件是否是目录:false
文件是否是文件:true
文件长度为:21
使用 createNewFile () 创建对象时,此处必须处理异常,否则程序无法通过编译
8.2 I/O 流概述
- I 即 input 指读入操作,读取来自磁盘、光盘、文件等存储设备的数据
- O 即 output 指写出操作,将程序数据存入磁盘、光盘、文件等存储设备中
8.2.1 流
- 一连串流动的字符,是以先进先出的方式发送和接受数据的通道
- 所谓 I/O 流就是实现数据输入和输出的流
- 流的分类
- 按方向划分:输入流和输出流
- 按处理单元划分:字节流和字符流
- 按流的角色划分:节点流和处理流
8.2.2 流的方向划分:输入流和输出流
- 流具有明确的方向性,按着流的流向来分,分为输入流和输出流
- 输入流:只能从中读取数据,不能写入数据的流
- 输出流:只能向其写入数据,而不能从中读数据的流
- 这里的 “出” 和 “入” 一定是从程序运行所在的角度来论述的
- Java 输入流以 InputStream 和 Reader 作为基类(父类)
- Java 输出流以 OutoutSetram 和 Writer 作为基类(父类)
8.2.3 按处理单元划分:字节流和字符流
- 按照处理单元划分,流可以分为字节流和字符流,区别在于操作的数据单元不同
- 字节流:以8位字节为操作数据单元的流,可用于操作二进制数据(如操作图片文件)
- 字符流:以16位的字符为操作数据单元的流,可用于操作文本数据
- 将I/O当作一个水管,水管里的每滴水就像一个单元,即字节或字符
8.2.4 按流的角色划分:节点流或处理流
- 按照流的角色划分,流可以分为节点流和处理流
- 节点流:直接向一个特定的存储介质读写数据的流,使用节点流读写数据时,程序直接连接到数据源
- 处理流:用于对一个已存在的流进行连接和封装,是通过封装后的流实现数据读写的操作的流
-
使用处理流进行读写操作时,程序不会直接连接到实际的数据源,使用处理流包装节点流,可以隐藏底层节点流的差异,并且对外提供更加方便的输入和输出的方法,因此节点流也成为包装流
-
处理流的效率更为高效
8.2.5 字节流和字符流
流按照存储单元分类主要有 字节流 和 字符流 两大类,两类都具有 输入/输出操作
- 字符流:读取文件中的文字信息
- 字节流:读取非文本文件
8.3 字节流
8.3.1 字节输出流基类:OutputStream
- OutputStream 类为抽象类,必须使用该类的子类进行实例化对象
- 操作文件,则使用 FileOutputStream 实例化
- OutputStream 类的主要操作方法
方法 | 描述 |
---|---|
void close() | 关闭输出流 |
void flush() | 刷新缓冲区 |
void write(byte[] b) | 将每个byte数组写入数据流 |
void write(byte[] b,int off,int len) | 将每一个指定范围的byte数组写入数据流 |
void write(int b) | 将一个字节数据写入数据流 |
8.3.2 字节输出流 FileOutputStream 类
- 实际应用中,一般使用 OutputS tream 类的 FileOutputStream 子类向文本文件写入数据
- FileOutputStream常用的构造方法
方法 | 描述 |
---|---|
FileOutputStream( File file) | 创建向指定File对象写数据的文件输出流file:指定目标文件的对象 |
FileOutputStream( String name) | 创建向指定路径的文件写数据的文件输出流name:指定目标文件的路径字符串 |
FileOutputStream( String name, boolean append) | 创建一个向指定路径的文件写入数据的文件输出流name:指定目标文件的路径字符串append:表示是否在文件末尾追加数据。如果为true,则表示可以在文件末尾追加数据 |
字节输出流 FileOutputStream 演示
public class Test02 {
public static void main(String[] args) throws IOException {
//创建File对象
File file=new File("test.txt");
//向指定文件输出数据
FileOutputStream fos=new FileOutputStream(file,true);
//输出数据
String str="Hello Word!";
//将字符串转换为byte数组
byte [] bs=str.getBytes();
//向文件写入数据 从第0个字节开始,到写入字符串的长度
fos.write(bs,0, bs.length);
if (fos!=null){
System.out.println("文件以成功写入");
fos.close();
}else{
System.out.println("文件不存在");
}
}
}
- FileOutputStream的前两种构造方法,都会直接覆盖原文件的内容
- FileOutputStream 类的构造方法创建对象时,如果相应的文件不存在,则会自动创建文件
- 在创建输出流对象、输入数据、关闭流时必须进行异常处理
8.3.3 字节输入流基类:InputStream
- 可以通过 InputStream 类从文件中读取数据,InputStream 类同样为抽象类
- InputStream 同样为抽象类
- InputStream 类定义的主要方法
方法 | 描述 |
---|---|
int read() | 读取一个字节数据,返回字节的阿斯玛值 |
int read(byte[] b) | 将数据读取到字节数组中,返回字节的长度 |
int read(byte[] b,int off,int len) | 从输入流中读取最多len长度的字节,保存到字节数组中,保存的位置从off开始 |
void close() | 关闭输入流 |
int available() | 返回输入流读取的字节数 |
8.3.4 字节输入流 FileInputStream 类
- 实际开发中,通常使用 InputStream 类的 FileInputStream 子类实现文本文件内容的读取
- FileInputStream 类的常用构造方法
方法 | 描述 |
---|---|
FileInputStream(File file) | 用于创建从指定File对象读取数据的文件输入流file:指定目标文件数据源对象 |
FileInputStream( String name) | 用于创建从指定路径的文件读取数据的文件输入流name:指定目标文件数据源路径字符串 |
字节输入流 FileInputStream 演示
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Test03 {
public static void main(String[] args) throws IOException {
//创建File对象
File file=new File("test.txt");
//创建 输入流对象
FileInputStream fis=new FileInputStream(file);
int data=0;
//如果读取不到数据 则会返回-1
while ((data=fis.read())!=-1){
System.out.print((char)data);
}
fis.close();
}
}
运行结果
//文件内的数据
Hello Word!
I LOVE YOU!
Thank you!!!
Welcome!!!
一次性读取所有数据
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/*
* FileInputStream
* */
public class Demo03 {
public static void main(String[] args) throws IOException {
//创建 File对象
File file=new File("Demo01.txt");
//创建 字节输入流 对象
FileInputStream fis=new FileInputStream(file);
//创建字节数组 接受文件内的字节
byte [] bs=new byte[1024];
//字节总长度
int len=0;
//将文件内的数据赋给字节数组,再将字节的长度赋给len变量
len=fis.read(bs);
if(len>0){
System.out.println(new String(bs,0,len));
}
}
}
- 使用 read() 方法读取文件中的数据,每次读取一个8位字节,把他转换为0~255的整数返回
- 可以将字节强制转换为 char类型
- read() 方法读取文件中的数据,当返回结果为-1时,即输入流已经读到末尾
- 与字节输出流用法相同,在创建输入流对象、读取数据、关闭流时必须进行异常处理
8.4 字符流
8.4.1 字符输出流基类:Writer
- Writer 类是字符输出流的抽象基类
- Writer 类中定义的常用方法
方法 | 描述 |
---|---|
void write(String str) | 将str字符串中包含的字符输出到输出流中 |
void write(String str, int off, int len) | 将字符串中从off位置开始,长度为len的字符输出到输出流中 |
void close() | 关闭输出流 |
void flush() | 刷新输出流 |
8.4.2 字符输出流 FileWriter 类
-
FileWriter 类是 Writer 类常用的子类
-
FileWriter 类可以以字符为数据处理单元向文本中写数据
-
FileWriter 类演示
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Test04 {
public static void main(String[] args) throws IOException {
//创建 File对象
File file=new File("test.txt");
//创建字符输出流对象
FileWriter fw=new FileWriter(file,true);
//写入数据
try {
fw.write("你好");
//刷新缓冲区
fw.flush();
System.out.println("文件写入成功");
}catch (Exception e){
System.out.println("文件不存在");
}finally {
if (fw!=null){
//如果字符输出流对象不为空,那么关闭字符输出流对象
fw.close();
}
}
}
}
- 使用 flush() 方法清空流。
- 将数据写入文件的过程中,会有一些残留的数据没有被写入,这是就需要使用 flush()方法清空输出流 FileWriter,可以强制将其中残余的数据写入文本文件中
8.4.3 字符输入流基类:Reader
- Rader 类是字符输入流的基类,它是抽象类
- Reader 类中的常用方法
方法 | 描述 |
---|---|
int read() | 从输入流中读取单个字符,返回所读取的字符数据 |
int read(char[] c) | 从输入流中最多读取c.length个字符数据,并将其存储在字符数组c中,返回实际读取的字符数 |
int read(char[] c, int off, int len) | 从输入流中最多读取len个字符的数据,并将其存储在字符数组c中存入数组c中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数 |
8.4.4 字符输入流 FileReader
- Reader 类同样是抽象类,可以使用其子类 FileReader 创建对象
- FileReader 可以实现读取文件中的数据
- FileReader 演示
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class Test05 {
public static void main(String[] args) throws IOException {
//创建 File 对象
File file=new File("test.txt");
//创建 字符输入流对象
FileReader fr=new FileReader(file);
int data=0;
//接受数据
char [] cs=new char[1024];
while((data=fr.read(cs))>0){
System.out.print(new String(cs,0,data));
}
//关闭字符输入流对象
fr.close();
}
}
运行结果
//文件内的数据
Hello Word!
I LOVE YOU!
Thank you!!!
Welcome!!!你好
8.5 缓冲流
- 前面所介绍的都属于 I/O 流中的节点流,可以之间与文件进行数据交互
- 此外,Java.io 包还提供了缓冲流,缓冲流处于处理流
- 接收输出输入流对象,对数据进行处理
8.5.1 字符缓冲输出流 BufferedWriter类
- BufferedWriter 类 Writer 类的子类
- BufferedWriter 类可以把一批数据写到缓冲区
- 当缓冲区满的时候,才会把数据写到目的地,可以减少物理写数据的次数,从而提高 I/O操作的执行效率
- BufferedWriter 类的常用的构造方法
方法 | 描述 |
---|---|
BufferedWriter(Writer out) | 创建一个缓冲字符输出流 |
- 演示代码
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Test06 {
public static void main(String[] args) throws IOException {
//创建文件对象
File file=new File("test.txt");
//创建字符输出流对象
FileWriter fw=new FileWriter(file);
//创建输出缓冲流
BufferedWriter bw=new BufferedWriter(fw);
//向文件中写入数据
bw.write("亲爱的可爱鬼:");
//换行
bw.newLine();
//刷新缓冲区
bw.flush();
//关闭缓冲区
bw.close();
//关闭字符输出流
fw.close();
}
}
8.5.2 字符缓冲输入流 BufferedReader 类
-
BufferedReader 类是 Reader 类的子类
-
BufferedReader 类与 FileReader 类的区别在于,BufferedReader 类有缓冲区,可以先把一批数据放入缓冲区
-
读取的操作都是从缓冲区内获取,避免每次都从数据源读取数据进行字符编码转换
-
BufferedReader 类常用的构造方法
方法 | 描述 |
---|---|
BufferedReader(Reader in) | 创建一个缓冲字符输入流 |
- 演示代码
import java.io.*;
public class Test07 {
public static void main(String[] args) throws IOException {
//创建 File 对象
File file=new File("test.txt");
//创建字符输入流 对象
FileReader reader=new FileReader(file);
//创建字符缓冲输入流对象
BufferedReader br=new BufferedReader(reader);
//获取数据
String line=br.readLine();
while(line!=null){
System.out.println(line);
line=br.readLine();
}
//关闭字符缓冲输入流
br.close();
//关闭字符输入流
reader.close();
}
}
8.6 数据操作流
- 实现对二进制文本的读写操作
- 使用数据操作流 DataOutputStream 和 DataInputStream进行操作
8.6.1 DataOutputStream 类和 DataInputStream 类
1、分类
- DataOutputStream类
- OutputStream类的子类
- OutputStream类的子类
- 使用DataOutputStream类写二进制文件的实现步骤与FileOutputStream类写文本文件相似
- DataInputStream类
- InputStream类的子类
- 与FileInputStream类结合使用读取二进制文件
- 使用DataInputStream类读取二进制文件的实现步骤与FileInputStream类读取文本文件相似
2、DataOutputStream类
- 按照与平台无关的方式向流中写入基本数据类型的数据
- 演示代码
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");
3、DataInputStream类
- 按照与平台无关的方式从流中读取基本数据类型的数据
- 演示代码
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());
8.7 序列化与反序列化
- 序列化就是将对象的状态更轻松简单的存储到特定的存储介质中的过程,将对象状态转换为可保持或可传输格式的过程
- 反序列话是将特定的存储介质中的数据重新构建成对象的过程,通过反序列化后得到相同的对象
- 序列化和反序列化的过程
8.7.1 对象输出流 ObjectOutputStream 实现序列化
- 对象的序列化,是把一个对象变为二进制的数据流的一种方法
- 如果一个类的对象需要被序列化,必须实现 java.IO.Serializable 接口
- 定义接口: public interf Serializable()
- 此接口中没有任何方法,他代表一个标识接口,一旦实现了此接口,代表该类的对象可以被序列化
- ObjectOutputStream 类的常用方法
方法 | 描述 | 类型 |
---|---|---|
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("姓名:"+name);
System.out.println("年龄:"+age);
System.out.println("性别:"+sex);
}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public Person() {
}
}
运行类
import java.io.*;
public class Test08 {
public static void main(String[] args) throws IOException {
//创建文件对象
File file=new File("test.txt");
//创建字节输出流
FileOutputStream ops=new FileOutputStream(file);
//创建对象输出流
ObjectOutputStream oos=new ObjectOutputStream(ops);
//创建Person 对象
Person person=new Person("艾琳",17,"男");
//向文件夹写入数据
oos.writeObject(person);
System.out.println("序列化成功");
//关闭
oos.close();
ops.close();
}
}
向文件添加后的数据
�� sr CH09.Test08.Person�MjԿRw I ageL namet Ljava/lang/String;L sexq ~ xp t 艾琳t 男
-
内容全部是二进制数据,不可直接修改文件本身,否则会造成数据丢失
-
当需要保存多个对象时,可以使用集合对象,最后将对象写入文件中
运行类2
import java.io.*;
import java.util.ArrayList;
public class Tets008 {
public static void main(String[] args) throws IOException {
//创建文件对象
File file=new File("test2.txt");
//创建输出流对象
FileOutputStream ops=new FileOutputStream(file);
//创建对象输出流对象
ObjectOutputStream oop=new ObjectOutputStream(ops);
//创建对象
Person p1=new Person("艾米",21,"女");
Person p2=new Person("艾伦",21,"男");
Person p3=new Person("杰克",24,"男");
//创建对象集合
ArrayList<Person> list=new ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(p3);
//向文件写入数据
oop.writeObject(list);
System.out.println("序列化成功");
//关闭
ops.close();
oop.close();
}
}
8.7.2 对象输入流 ObjectInputStream 实现反序列化
- 反序列化就是与序列化相反的操作,从特定存储介质中读取数据并重新构建成对象的过程
- 执行反序列化操作需要使用对象输出流ObjectInputStream,可以直接把序列化后的对象还原
- ObjectInputStream 类的常用方法
方法 | 描述 | 类型 |
---|---|---|
ObjectInputStream(InputStream in) | 创建对象输入流对象 | 构造方法 |
final Object readObject() | 从指定位置读取对象 | 实例方法 |
- 演示
读取文件内序列化的对象
import java.io.*;
public class Test0008 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建文件对象
File file=new File("test.txt");
//创建 字节输入流对象
FileInputStream fis=new FileInputStream(file);
//创建 对象输入流对象
ObjectInputStream ois=new ObjectInputStream(fis);
//读取数据
Person person=(Person)ois.readObject();
person.print();
//关闭
fis.close();
ois.close();
}
}
运行结果
姓名:艾琳
年龄:17
性别:男
读取文件内序列化的对象集合
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
public class Test00008 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建文件对象
File file=new File("test2.txt");
//创建 字节输入流
FileInputStream fis=new FileInputStream(file);
//创建 对象输入流
ObjectInputStream ois=new ObjectInputStream(fis);
//读取文件
ArrayList<Person> list=(ArrayList<Person>)ois.readObject();
//遍历集合
Iterator iterator=list.iterator();
while(iterator.hasNext()){
System.out.println("*****");
Person p=(Person)iterator.next();
p.print();
}
fis.close();
ois.close();
}
}
运行结果
*****
姓名:艾米
年龄:21
性别:女
*****
姓名:艾伦
年龄:21
性别:男
*****
姓名:杰克
年龄:24
性别:男
- 当对象中有某些属性不需要被序列化,可以使用 transient 关键字
- transient 语法:
private transient int age;
8.8 Java 体系中常用流的分类
分类 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
基类 | OutputStream | InputStream | Writer | Reader |
文件流 | FileOutputSteam | FileInputStream | FileWriter | FileReader |
缓冲流 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
对象流 | ObjectOutputStream | ObjectInputStream | - | - |
数据操作流 | DataOutputStream | DataInputStream | - | - |
年龄:24 | ||||
性别:男 |
- 当对象中有某些属性不需要被序列化,可以使用 **transient** 关键字
- **transient 语法:**
~~~java
private transient int age;
8.8 Java 体系中常用流的分类
分类 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
基类 | OutputStream | InputStream | Writer | Reader |
文件流 | FileOutputSteam | FileInputStream | FileWriter | FileReader |
缓冲流 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
对象流 | ObjectOutputStream | ObjectInputStream | - | - |
数据操作流 | DataOutputStream | DataInputStream | - | - |