20、IO流
前言
在变量、数组和对象中存储的数据是暂时存在的,程序结束后就会丢失。需要永久地存储程序创建的数据,需要将其保存在磁盘文件中,这样就可以在其他文件中使用该文件。
Java的I/O技术可以将数据保存到文本文件,二进制文件甚至ZIP压缩文件中,以达到永久性存储数据的要求。
1 Java IO流的概念
Java的IO是实现输入和输出的基础。
在Java中对输入/输出的操作是以流的方式实现的。
Java.io
包提供了大量的供使用的操作【流】的方法和接口,用于进行各类数据的处理和传输。
计算机的输入和输出是通过二进制来完成的。
2 文件
2.1 关于文件
(1)一切皆文件
(2)文件流
文件在程序中是以流的形式来操作的
- 流:数据在数据源(文件)和程序(内存)之间经历的路程
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
2.2 File类
2.2.1 简介
在Java中,File类是Java.io包中唯一代表磁盘文件本身的对象
File类处理文件和文件系统的相关信息,File类不具有从文件读取信息和向文件写入信息的功能,它仅描述文件本身的属性
2.2.2 构造方法
构造器 | 描述 |
---|---|
File(String pathname) | 通过给定路径的字符串来创建一个新的File实例 |
File(String parent, String child) | 通过指定父路径的字符串和文件路径创建一个File对象实例 |
File(File Parent, String child) | 通过指定的父路径对象和文件路径创建File对象实例 |
File file = new File("D:\\code\\a.txt");
File file = new File("D:\\code\\", "a.txt");
File fatherFile = new File("D:\\code");
File childFile = new File(fatherFile, "a.txt");
2.2.3 File类创建和删除功能
返回值类型 | 方法名 | 功能 |
---|---|---|
boolean | creatNewFile() | 指定路径不存在该文件时创建文件,返回true,存在则返回false |
boolean | mkdir() | 当指定的单级文件夹不存在时创建文件夹并返回true,存在则返回false |
boolean | mkdirs() | 当指定的多级文件夹在某一级文件夹不存在时,创建多级文件夹并返回true,都存在则返回false |
boolean | delete() | 删除文件或者删除单级文件夹 |
2.2.4 File类的判断功能
返回值类型 | 方法名 | 功能 |
---|---|---|
boolean() | exists() | 判断指定路径的文件或文件夹是否存在 |
boolean() | isAbsolute() | 判断当前路径是否为绝对路径 |
boolean() | isDirectory() | 判断当前的目录是否存在 |
boolean() | isFile() | 判断当前的目录是否是一个文件 |
boolean() | isHidden() | 判断当前路径是否是隐藏文件 |
【目录】指的是File对象
2.2.5 File类的获取功能和修改名字功能
返回值类型 | 方法名 | 功能 |
---|---|---|
File | getAbsoluteFile() | 获取文件的绝对路径,返回File对象 |
String | getAbsolutePath() | 获取文件的绝对路径,返回路径的字符串 |
String | getParent() | 获取当前路径的父级路径,以字符串形式返回该父级路径 |
String | getName() | 获取文件或文件夹的名称 |
String | getPath() | 获取File对象中封装的路径 |
long | lastModifiled() | 以毫秒值返回最后的修改时间 |
long | length() | 返回文件的字节数 |
boolean | renameTo(File dest) | 将当前File对象所指向的路径修改为指定File所指向的路径 |
2.2.6 文件夹列表操作
返回值类型 | 方法名 | 功能 |
---|---|---|
String[] | list() | 返回一个字符串数组,如果此抽象路径名不表示一个目录,那么此方法将返回 null。否则返回一个字符串数组,每个数组元素对应目录中的每个文件或目录。表示目录本身及其父目录的名称不包括在结果中。每个字符串是一个文件名,而不是一条完整路径 |
String[] | list(FilenameFilter filter) | 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。除了返回数组中的字符串必须满足过滤器外,此方法的行为与 list() 方法相同。如果给定 filter 为 null,则接受所有名称 |
File[] | listFiles() | 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件 |
File[] | listFiles(FileFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。除了返回数组中的路径名必须满足过滤器外,此方法的行为与 listFiles() 方法相同。如果给定 filter 为 null,则接受所有路径名。 |
File[] | listFiles(FilenameFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
3 IO流的分类
Java中一切皆为对象,【流】也是对象。
按照不同的分类方式,【流】有不同的类型,常用分类有三种。
(1)按流向分
- 输入流:只能从中读取数据,而不能向其写入数据
- 输出流:只能从中写入数据,而不能向其读取数据
(2)按操作单元划分
- 字节流:以一个字节为单位进行读取或写入
- 字符流:以一个字符【两个字节】为单位来处理字符
(3)按角色划分
- 节点流:直接从一个特定的IO设备【如磁盘,网络】读取或写入的数据的流
- 处理流:“链接”已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能的流
(4)输入/输出流体系中常用的流的分类表
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类(接口) | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流(处理流) | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
操作对象 | ObjectInputStream | ObjectOutputStream |
3.1 字节流
字节流:以一个字节为单位进行读取或写入,程序使用字节流执行8位字节的输入和输出,所有字节流均来自InputStream和OutputStream
3.1.1 OutputStream常用方法
public abstract void write(int b) throws IOException;//写一个字节
public void write(byte[] b) throws IOException;//将给定的字节数组b全部写入文件中
public void write(byte[] b, int off, int len) throws IOException;//将给定的字节数组b中指定的偏移量和长度之间的内容写入文件中
public void flush() throws IOException;//强制将通道中的数据全部写出
public void close() throws IOException;//关闭通道
3.1.2 FileOutputStream文件输出流构造方法
FileOutputStream继承了OutputStream类,其他方法都相似,不同在于构造方法
public FileOutputStream(String name) throws FileNotFoundException;//根据提供的文件路径构建一条输出通道
//根据提供的文件路径构建一条文件输出通道,并根据append的值决定是否将内容追加到末尾,否则默认是直接覆盖
public FileOutputStream(String name, boolean append) throws FileNotFoundException;
public FileOutputStream(File file) throws FileNotFoundException;//根据提供的文件对象构建一条文件输出通道
//根据提供的文件对象构建一条文件输出通道,并根据append的值决定是否将内容追加到末尾,否则默认是直接覆盖
public FileOutputStream(File file, boolean append) throws FileNotFoundException;
3.1.3 InputStream常用方法
public abstract int read() throws IOException;//读取一个字节
public int read(byte[] b) throws IOException;//读取多个字节存储至给定字节数组b中
public int read(byte[] b, int off, int len) throws IOException;//读取多个字节按照给定的偏移量及长度存储在给定的字节数组b中
public void close() throws IOException;//关闭流
public int avaliable() throws IOException;//获取通道中的数据长度
3.1.4 FileInputStream文件输入流构造方法
public FileInputStream(String name) throws FileNotFoundException;//根据提供的文件路径构建一条文件输入通道
public FileInputStream(File file) throws FileNotFoundException;//根据提供的文件信息构建一条文件输入通道
3.1.5 总结
(1)什么时候使用字节流?
字节流仅仅适用于读取原始数据(即基本数据类型)
3.2 字符流
3.2.1 字符流简介
Java平台使用Unicode约定存储字符,字符流I/O会自动将此内部格式与本地字符转换,在西方语言环境中,本地字符通常是ASCII的8位字符集
所有字符流类均来自Reader和Writer。与字节流一样,也由专门用于文件I/O 的字符流:FileReader和FileWriter
3.2.2 Writer常用方法
public void write(int c) throws IOException;//输出一个字符
public void write(char[] cbuf) throws IOException;//将给定的字符数组写入到文件中
abstract public void write(char[] cbuf, int off, int len) throws IOException;//将给定的字符数组中给定偏移量和长度的内容输出到文件中
public void write(String str) throws IOException;//将字符串写入到文件中
abstract public void flush() throws IOException;//强制将通道中的数据全部写出
abstract pubic void close() throws IOException;//关闭通道
3.2.3 FileWriter构造方法
public FileWriter(String fileName) throws IOException;//根据提供的文件路径构建一条文件输出通道
public FileWriter(String fileName, boolean append) throws IOException;//根据提供的文件路径构建一条文件输出通道,并根据append的值决定时将内容追加到末尾还是覆盖
public FileWriter(File file) throws IOException;//根据提供的文件信息构建一条文件输出通道
public FileWriter(File file, boolean append) throws IOException;//同上
3.2.3 Reader常用方法
public int read() throws IOException;//读取一个字符
public int read(char[] cbuf) throws IOException;//读取字符到给定字符数组中
abstract public int read(char[] chuf, int off, int len) throws IOException;//off:偏移量,len:长度
abstract public void close() throws IOException;//关闭通道
3.2.4 FileReader构造方法
public FileReader(String fileName) throws FileNotFoundException;//根据文件路径构建文件的输入通道
public FileReader(File file) throws FileNotFoundException;//根据文件对象构建一条文件的输入通道
3.3 缓冲流(处理流)
在之前的实践中,都是用的是无缓冲的I/O,这意味着每个读取或写入的请求都由基础操作系统直接处理,由于每个这样的请求通常都会触发硬盘访问,网络活动或某些其他相对昂贵的操作,可能会使效率大大降低。
为了减少这种开销,Java平台实现了缓冲的I/O 流。缓冲的输入流从成为缓冲区的存储区中读取数据,仅当缓冲区为空时调用本机输入API。同样的,缓冲的输出流将数据写入缓冲区,并且仅在缓冲区已满时才调用本机输出API。
由四种用于包装非缓冲流的缓冲流类:BufferedInputStream和BufferedOutputStream创建缓冲的字节流, BufferedReader 和BufferedWriter创建缓冲的字符流。
3.3.1 BufferedOutputStream构造方法
public BufferedOutputStream(OutputStream out);//根据给定的字节输出流创建一个缓冲输出流,缓冲区大小使用默认大小
public BufferedOutputStream(OutputStream out, int size);//根据给定的字节输出流创建一个缓冲输出流,并指定缓冲区大小
3.3.2 BufferInputStream构造方法
public BufferInputStream(InputStream in);//根据给定的字节输入流创建一个缓冲输入流,缓冲区大小默认
public BufferInputStream(InputStream in, int siez);//根据给定的字节输入流创建一个缓冲输入流,并指定缓冲区大小
3.3.3 BufferedWriter构造方法
public BufferedWriter(Writer out);//根据给定的字符输出流创建一个缓冲字符输出流,缓冲区大小默认
public BufferedWriter(Writer out, int sz);//根据给定的字符输出流创建一个缓冲字符输出流,并指定缓冲区的大小
3.3.4 BufferedReader构造方法
public BufferedReader(Reader in);//根据给定的字符输入流创建一个缓冲字符输入流,缓冲区大小默认
public BufferedReader(Reader in, int sz);//根据给定的字符输入流创建一个缓冲字符输入流,并指定缓冲区的大小
3.4 数据流
数据流支持原始数据类型值(布尔值,char,byte,short,int, long,float和double)以及String值的二进制I/O,所有数据流都实现了DataInput和DataOutput接口。重点学习这些接口的最广泛的使用的实现,即DataInputStream和DataOutputStream
3.4.1 DataOutput接口常用方法
void writeBoolean(boolean v) throws IOException;//将布尔值作为一个字节写入底层输出通道
void writeByte(int v) throws IOException;//将字节写入底层输出通道
void writeShort(int v) throws IOException;//将短整数作为2个字节(高位在前)写入底层输出通道
void writeChar(int v) throws IOException;//将字符作为2个字节(高位在前)写入底层输出通道
void writeInt(int v) throws IOException;//将整数作为4个字节(高位在前)写入底层输出通道
void writeLong(int v) throws IOException;//将长整数作为8个字节(高位在前)写入底层输出通道
void writeFloat(int v) throws IOException;//将单精度浮点数作为4个字节(高位在前)写入底层输出通道
void writeDouble(int v) throws IOException;//将双精度浮点数作为8个字节(高位在前)写入底层输出通道
void writeUTF(int v) throws IOException;//将UTF-8编码格式的字符串以与机器无关的方式写入底层输出通道
3.4.2 DataOutputStream常用方法
public DataOutputStream(OutputStream out);//根据给定的字节输出流创建一个二进制输出流
示例
public class Example01 {
public static void main(String[] args) {
File file = new File("F:\\test\\xyz\\A.txt");
writeData(file);
readData(file);
}
private static void readData(File file) {
try(InputStream is = new FileInputStream(file);
DataInputStream dis = new DataInputStream(is);) {
while (true) {
byte b = dis.readByte();
System.out.println("读取字节" + b);
short s = dis.readShort();
System.out.println("读取短整数" + s);
int i = dis.readInt();
System.out.println("读取整数" + i);
long l = dis.readLong();
System.out.println("读取长整形" + l);
float f = dis.readFloat();
System.out.println("读取单精度附浮点数" + f);
double d = dis.readDouble();
System.out.println("读取双精度浮点数" + d);
char c = dis.readChar();
System.out.println("读取字符" + c);
boolean br = dis.readBoolean();
System.out.println("读取布尔值" + br);
String sf = dis.readUTF();
System.out.println(sf);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) {//EOF:end of file
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeData(File file) {
File parent = file.getParentFile();
if (!(parent.exists())) parent.mkdirs();
try (OutputStream os = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(os);) {
dos.writeByte(-1);
dos.writeShort(-2);
dos.writeInt(1);
dos.writeLong(100);
dos.writeFloat(1.2f);
dos.writeDouble(100.0);
dos.writeChar('a');
dos.writeBoolean(true);
dos.writeUTF("这是UTF-8格式的字符串");
dos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.5 对象流
**正如二进制数据流支持原始数据的I/O,对象流也支持对象的I/O。大多数(不是全部)标准类支持其对象的序列化,只有实现了Serializable序列化标记接口才能够序列化 **
对象存储在内存中,通过对象流写入到磁盘文件中,这个过程叫做【序列化】,序列化必须要求该对象实现序列化接口Serializable
将磁盘中存储的对象输入至内存中的过程成为【反序列化】,需要注意的是,反序列化必须保证与序列化时使用JDK版本一致
3.5.1 ObjectOutput接口常用方法
public void writeObject(Object obj) throws IOException;//将对象写入底层输出通道
3.5.2 ObjectOutputStream构造方法
public ObjectOutputStream(OutputStream out) throws IOException;//根据给定的字节输出流创建一个对象输出流
示例
private static void writeObj() {
File file = new File("F:\\test\\stu.obj");
File parent = file.getParentFile();
if (!(parent.exists())) parent.mkdirs();
// if (!(file.exists())) file.createNewFile();
try (OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);) {
oos.writeObject(new Student("张三", 20));
oos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
3.5.3 ObjectInput接口常用方法
public Object readObject() throws ClassNotFoundException, IOException;//读取一个对象
3.5.4 ObjectinputStream构造方法
public ObjectInputStream(InputStream in) throws IOException;//根据给定的字节输入流创建一个对象输入流
示例
private static void readObj() {
File file = new File("F:\\test\\stu.obj");
File parent = file.getParentFile();
if (!(parent.exists())) parent.mkdirs();
try(InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);) {
Student student = (Student) ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
4 【思考】字节流怎么转换为字符流
package com.zyhstu.io_;
import java.io.*;
/**
* @author DarkSky
* @version 1.0
* 将字节流转换为字符流,以便于使用字符流中的方法,比如读行或写行
* (1)public InputStreamReader(InputStream in){}
* InputStreamReader接收InputStream字节输入流为参数
* (2)public class InputStreamReader extends Reader
* InputStreamReader继承了Reader类
* (3)public BufferedReader(Reader in){}
* BufferedReader可以接收Reader及其子类实例
*/
public class IOConverter {
public static void main(String[] args) {
write();
read();
}
private static void write() {
try (OutputStream out = new FileOutputStream("F:\\test\\xyz\\abc.txt", true);
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter writer = new BufferedWriter(osw);) {
String[] lines = {
"读书改命",
"自力更生",
"知恩图报",
"轻装前行"
};
for (String line:
lines) {
writer.write(line);
writer.newLine();
}
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void read() {
try (InputStream in = new FileInputStream("F:\\test\\xyz\\abc.txt");
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr)) {
while (true) {
String line = br.readLine();
if (line == null) break;
System.out.println(line);
}
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}