目录
序列化和反序列化
序列化: 将数据结构或对象转换成二进制字节流的过程。
反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程。
序列化和反序列化的作用:将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送)的二进制字节流。
-
使用对象流读写引用类型的数据,需要相应类实现Serializable接口,并且有些需要为其指定序列化ID。
-
不需要序列化的属性和方法可以使用关键字transient修饰,也可以使用static,也不会被序列化。
File类
File类用来代表文件和文件夹。主要作用有两个:获取文件或者文件夹的属性; 实现对文件、文件夹的创建和删除。
File类的常用方法
操作文件:
createNewFile():创建文件
exists():判断文件是否存在
delete():删除文件
length() 获取文件的大小(字节数)
getName():获取文件名
getAbsolutePath():获取文件的绝对路径
renameTo(File dest):重命名文件夹
isFile():判断是否是一个文件
isHidden():判断是否是一个隐藏文件
getParent():获得父目录名
lastModified():获取最后一次被修改的时间。
static File[] listRoots()列出所有的根目录
list() 返回目录下的文件或者目录名
listFiles() 返回目录下的文件或者目录对象
list(FilenameFilter filter)返回指定当前目录中符合过滤条件的子文件或子目录。
listFiles(FilenameFilter filter)返回指定当前目录中符合过滤条件的子文件或子目录。
操作文件夹:
mkdir():创建一个单级文件夹
mkdirs():创建一个多级文件夹
exists():判断文件夹是否存在
length() 获取文件的大小(字节数)
getName():获取文件夹名
isDirectory():判断是否是个目录
isHidden():判断是否是一个隐藏文件夹
getAbsolutePath():获取文件夹的绝对路径
lastModified():获取最后一次被修改的时间。
IO流的分类
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流。
所有的IO流都是基于这四大抽象类来进行衍生的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
所谓输入流和输出流,如何理解呢?
它们的概念可以以程序为中心来定义,很好理解,从源目标输入到程序中,就是输入流,从程序中输出到目的目标,就是输出流。而字节和字符则是根据处理单元来决定的字节流是以byte为单位处理的,而字符流是以char为单位进行处理的。
节点流和处理流
节点流
是可以直接操作数据的流,而处理流
更多的是在节点流的基础上对数据的处理进行性能优化
或者功能拓展
。
操作方式划分
操作对象划分
经典面试题
不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
IO流的使用场景
- 键盘输入和输出就是使用IO流来实现的。
- 数据的持久化要使用IO流来做
- 网络通信需要使用IO流
而这些IO流分别能处理的类型如下:
- FileInputStream和FileOutputStream:处理字节文件
- FileReader和FileWriter:处理字符文件
- ByteArrayInputSteam和ByteArrayOutputStream:处理字节数组
- CharArrayInputStream和CharArrayOutputStream:处理字符数组
- BufferedInputStream和BufferedOutputStream:对字节流进行缓存性能优化,增加读写效率。
- BufferedReader和BufferedWriter:对字符流进行缓存性能优化,增加读写效率
- DateInputStream和DateOutputStream:以字节流为操作单位进行操作基本数据类型和String类型
- ObjectInputStream和ObjectOutputStream:以字节流为操作单位操作对象类型。
- InputStreamReader和OutputStream:将字节流转换为字符流
- PrintStream和PrintWriter:打印流。
文件流
字节流
FileInputStream和FileOutputStream是字节流,是以字节为单位来处理流的, 数据源和目的地是文件。
可以使用简单的文件拷贝来看它们的基本使用方式。
@Test
void contextLoads() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("d:/1.txt");
fos = new FileOutputStream("d:/2.txt");
int b = 0;
while ((b = fis.read()) != -1) {
fos.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fis.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用缓冲数组提升字节读写效率
我们每次只读取一个字节,这样是十分缓慢的,而使用一个byte类型的缓冲数组,可以每次读取指定数量的字节到缓冲数组中,再从缓冲数组中取出,从而实现我们性能的提升。这个数组最好为2的整数次幂,因为系统底层是以二进制流来处理文件的。
@Test
void contextLoads() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("d:/1.txt");
fos = new FileOutputStream("d:/2.txt");
byte[] b = new byte[1024];
while ((fis.read(b)) != -1) {
fos.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fis.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符流
FileWriter和FileReader是字符流,它是以字符char为单位来处理文件的。
@Test
void contextLoads() {
FileWriter fw = null;
FileReader fr = null;
try {
fw = new FileWriter("d:/2.txt");
fr = new FileReader("d:/1.txt");
char[] b = new char[1024];
while ((fr.read(b)) != -1) {
fw.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fr.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用缓冲数组提升字符读写效率
@Test
void contextLoads() {
FileWriter fw = null;
FileReader fr = null;
try {
fw = new FileWriter("d:/2.txt");
fr = new FileReader("d:/1.txt");
char[] b = new char[1024];
while ((fr.read(b)) != -1) {
fw.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fr.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
缓冲流
缓冲流是一种处理流,在节点流处理的基础上进行性能优化,和自定义一个缓冲数组类似,从而提高读写效率,它的输入缓冲区大小8192。
缓冲字节流
BufferedInputStream和BufferedOutputStrem
@Test
void contextLoads() {
BufferedInputStream br = null;
BufferedOutputStream bw = null;
try {
br = new BufferedInputStream(new FileInputStream("d:/1.txt"));
bw = new BufferedOutputStream(new FileOutputStream("d:/2.txt"));
int b = 0;
while ((b = br.read()) != -1) {
bw.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
br.close();
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
缓冲字符流
BufferedReader和BufferedWriter
@Test
void contextLoads() {
BufferedWriter bw = null;
BufferedReader br = null;
try {
bw = new BufferedWriter(new FileWriter("d:/2.txt"));
br = new BufferedReader(new FileReader("d:/1.txt"));
int b = 0;
while ((b = br.read()) != -1) {
bw.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
br.close();
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
基本数据流
DataInputStream和DataOutputStream
基本数据流是字节处理流,可以处理八大基本类型和String类型。想一想逻辑性也不可能存在字符流
使用基本数据流来处理基本数据类型,在节点流的基础上进行处理基本数据的处理。
@Test
void contextLoads() {
DataInputStream in = null;
DataOutputStream out = null;
try {
in = new DataInputStream(new FileInputStream("d:/1.txt"));
out = new DataOutputStream(new FileOutputStream("d:/1.txt"));
out.writeInt(12);
out.writeChars("a");
out.writeUTF("你好");
System.out.println(in.readInt());
System.out.println(in.readChar());
System.out.println(in.readUTF());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
对象操作流
对象操作流ObjectInputStream和ObjectOutputStream是处理字节流。它需要在节点流的基础上进行对象操作,可以处理一切经过序列化的对象,也可以处理基本对象。
// 序列化对象
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 写入对象
@Test
void contextLoads() {
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream("d:/1.txt"));
out.writeInt(12);
out.writeObject(new User("张三", 12));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 读取对象
@Test
void contextLoads1() {
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new FileInputStream("d:/1.txt"));
System.out.println(in.readInt());
System.out.println(in.readObject().toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
数组操作流
字节数组操作流
ByteArrayOutputStream和ByteArrayInputStream可以将一些字节数据整合为字节数组,也可以将字节数组逐个转换为字节。数据源是字节数组,可以实现各种基本和引用数据类型与字节数组之间的相互转换
@Test
void contextLoads() {
ByteArrayOutputStream out = null;
ByteArrayInputStream in = null;
try {
out = new ByteArrayOutputStream();
// 字节数组输出
out.write('a');
out.write('b');
out.write('c');
byte[] bytes = out.toByteArray();
System.out.println(bytes);
//字节数组写入
byte[] b = bytes;
int temp = 0;
in = new ByteArrayInputStream(b);
while ((temp=in.read())!=-1){
System.out.println(temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符数组操作流
CharArrayReader和CharArrayWriter则是字符节点流,可以将字符转换为字符数组,也可以将字符数组转换为逐个字符,用法和字节数组类似,这里不作演示。
转换流
InputStreamReader和OutputStreamWriter是字节转字符转换流,它可以将一个字节流对象转换为字符流对象。
栗子它是一个字符处理流。
@Test
void contextLoads() {
InputStreamReader ir = null;
OutputStreamWriter or = null;
try {
ir = new InputStreamReader(new FileInputStream("d:/1.txt"));
or = new OutputStreamWriter(new FileOutputStream("d:/2.txt"));
int b = 0;
while ((b = ir.read()) != -1){
or.write(b);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ir.close();
or.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
换行操作
不管是写入或者写出的时候,我们都希望能够控制它是否换行写入或者按行读出,而缓存字符流为我们提供了这个方法。
@Test
void contextLoads() {
BufferedReader bi = null;
BufferedWriter bo = null;
try {
bi = new BufferedReader(new FileReader("d:/1.txx"));
bo = new BufferedWriter(new FileWriter("d:/1.txt"));
String b = "";
// 写入一行
while ((b = bi.readLine()) != null){
bo.write(b);
// 换行
bo.newLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bi.close();
bo.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印控制流
PrintStream和PrintWriter用于打印的一个流,比如我们进场使用的System.out
就是PrintStream
的一个引用,
public final static PrintStream out = null;
而System.in则是InputStream的一个实现,可以简单的进行实现一下输入和输出。
public final static InputStream in = null;
栗子
@Test
void contextLoads() {
BufferedReader bi = null;
BufferedWriter bo = null;
try {
InputStream in = System.in;
bi = new BufferedReader(new InputStreamReader(in));
bo = new BufferedWriter(new FileWriter("d:/1.txt"));
String b = "";
while (!("quit".equals((b = bi.readLine())))){
bo.write(b);
bo.newLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bi.close();
bo.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}