JAVA基础06-IO及序列化
1 IO
IO是简称,全称是in/out.是相对程序而言的读取in和写出out动作
继承结构
java.io包:
File
字节流:针对二进制文件
InputStream
–FileInputStream
–BufferedInputStream
–ObjectInputStream
OutputStream
–FileOutputStream
–BufferedOutputStream
–ObjectOutputStream
字符流:针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8
Writer
–BufferedWriter
–OutputStreamWriter
Reader
–BufferedReader
–InputStreamReader
–PrintWriter/PrintStream
1.1 File
1.1.1 File的概述
- 封装一个磁盘路径字符串,对这个路径可以执行一次操作。
- 可以用来封装文件路径、文件夹路径、不存在的路径。
1.1.2 创建File对象
- File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
1.1.3 File的常用方法
1.1.3.1 文件、文件夹属性
- length():文件的字节量
- exists():是否存在,存在返回true
- isFile():是否为文件,是文件返回true
- isDirectory():是否为文件夹,是文件夹返回true
- getName():获取文件/文件夹名
- getParent():获取父文件夹的路径
- getAbsolutePath():获取文件的完整路径
1.1.3.2 创建、删除
- createNewFile():新建文件,文件夹不存在会异常,文件已经存在返回false
- mkdirs():新建多层不存在的文件夹\a\b\c
- mkdir():新建单层不存在的文件夹\a
- delete():删除文件,删除空文件夹
1.1.3.3 文件夹列表
- list():返回String[],包含文件名
- listFiles():返回File[],包含文件对象
1.1.4 递归求目录总大小
public class Test1 {
public static void main(String[] args) {
//1、把指定目录封装成File对象
File file = new File("D:\\teach\\a");
int size =count(file);
System.out.println(size);
}
private static int count(File file) {
//2、把文件夹列表列出来
File[] files = file.listFiles();
//2.1 遍历数组里的每个资源
int sum = 0;//记录文件的大小
for (int i = 0; i < files.length; i++) {
//3、判断,如果是文件,直接把f.length()相加
// files[i]表示每次遍历到的资源
if(files[i].isFile()) {
sum += files[i].length();//求文件的和
}else if(files[i].isDirectory()){
//4、判断,如果是文件夹,继续列表,继续判断,如果是文件相加,如果又是文件夹,继续列表,继续判断,如果是文件相加......
//5、如果是文件夹,递归调用方法本身的业务逻辑
sum += count(file[i]);//把当前遍历到的文件夹继续循环判断求和
}
}
return sum ;
}
}
1.1.4 递归删除文件夹
public class Test2 {
public static void main(String[] args) {
File file = new File("D:\\teach\\a");
int size =delete(file);
}
private static void deleteFile(File file) {
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
if(files[i].isFile()) {
files[i].delete();//删除文件
}else if(files[i].isDirectory()){
deleteFile(files[i]);//递归调用
}
}
file.delete();//删除空文件夹
}
}
1.2 字节流读取
- 字节流是由字节组成的,字符流是由字符组成的.Java里字符由两个字节组成.字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据
1.2.1 InputStream抽象类
此抽象类是表示字节输入流的所有类的超类/抽象类。
1.2.2 FileInputStream子类
1.2.2.1 FileInputStream的特点
直接插在文件上,直接读取文件数据
1.2.2.2 FileInputStream的构造方法
//通过File对象指定
FileInputStream(File file)
//通过路径名指定
FileInputStream(String pathname)
1.2.3 BufferedInputStream子类
1.2.3.1 BufferedInputStream的特点
底层维护了一个byte[],默认大小是8K;
优化了 把磁盘中数据读取到流里的过程, 把数组读满了,一次性给java程序展示;
减少了交互,提高效率
1.2.3.2 BufferedInputStream的构造方法
//传入一个InputStream对象
BufferedInputStream(InputStream in)
1.3 字符流读取
常用于处理纯文本数据
1.3.1 Reader抽象类
用于读取字符流的抽象类
1.3.2 InputStreamReader子类
1.3.2.1 InputStreamReader的特点
InputStreamReader 是字节流通向字符流的桥梁:
它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集
1.3.2.1 InputStreamReader的构造方法
//创建使用指定字符集的 InputStreamReader
InputStreamReader(InputStream in, String charsetName)
//创建一个使用默认字符集的 InputStreamReader
InputStreamReader(InputStream in)
1.3.3 FileReader子类
1.3.3.1 FileReader的特点
用来读取字符文件的便捷类
此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的
1.3.3.1 FileReader的构造方法
//通过File对象指定
FileReader(File file)
//通过路径名指定
FileReader(String fileName)
1.3.4 BufferedReader子类
1.3.4.1 BufferedReader的特点
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小,8192个字符
传入一个Reader对象
BufferedReader(Reader in)
1.4 字节流写出
1.4.1 OutputStream抽象类
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器
1.4.2 FileOutputStream子类
1.4.2.1 FileOutputStream的特点
直接插在文件上,直接写出文件数据
1.4.2.2 FileOutputStream的构造方法
//向具有指定名称的文件中写入数据
FileOutputStream(String name)
//向指定 File 对象表示的文件中写入数据
FileOutputStream(File file)
//追加向指定 File 对象表示的文件中写入数据的文件输出流
FileOutputStream(File file, boolean append)
1.4.3 BufferedOutputStream子类
1.4.3.1 BufferedOutputStream的特点
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统
1.4.3.1 BufferedOutputStream的构造方法
//传入一个OutputStream对象
BufferedOutputStream(OutputStream out)
1.5 字符流写出
1.5.1 Writer抽象类
写入字符流的抽象类
1.5.2 OutputStreamWriter子类
1.5.2.1 OutputStreamWriter的特点
OutputStreamWriter 是字符流通向字节流的桥梁
可使用指定的 charset 将要写入流中的字符编码成字节。
它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集
1.5.2.2 OutputStreamWriter的构造方法
// 创建使用指定字符集的 OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)
//创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out)
1.5.3 FileWriter子类
1.5.3.1 FileWriter的特点
用来写入字符文件的便捷类
此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的
1.5.3.2 FileWriter的构造方法
//根据给定的文件名构造对象
FileWriter(String fileName)
//根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造对象
FileWriter(String fileName, boolean append)
//根据给定的File对象构造对象。
FileWriter(File file)
1.5.4 BufferedWriter子类
1.5.3.1 BufferedWriter的特点
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小
1.5.3.1 BufferedWriter的构造方法
//创建一个使用默认大小输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out)
//创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
BufferedWriter(Writer out,int sz)
1.6 字节流和字符流的区别
- 字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;
- 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。
1.7 流式传输
流式传输主要指将整个音频和视频及三维媒体等多媒体文件经过特定的压缩方式解析成一个个压缩包,由视频服务器向用户计算机顺序或实时传送。在采用流式传输方式的系统中,用户不必等到整个文件全部下载完毕,而是只需经过几秒或几十秒的启动延时即可在用户的计算机上利用解压设备对压缩的A/V、3D等多媒体文件解压后进行播放和观看。此时多媒体文件的剩余部分将在后台的服务器内继续下载。
1.8 字符流读写乱码问题
1.8.1 乱码问题的原因
打开和写出的编码用的表不一致,会造成乱码。
1.8.1 乱码问题的解决方案
- 保存和打开统一码表
- 使用InputStreamReader指定字符集,传入BufferedReader
new BufferedReader(new InputStreamReader(?,”utf-8”));
- 使用OutputStreamReader指定字符集,传入BufferedWriter
new BufferedWriter(new OutputStreamWriter(?,”utf-8”));
1.9 文件复制
public class Test1 {
public static void main(String[] args) throws Exception {
// 1,创建读取文件和写出文件
File from = new File("D:\\teach\\a\\1.txt");
File to = new File("D:\\teach\\a\\to.txt");
//调用copy完成文件复制
copy(from, to);
}
// 一个数组一个数组的复制
private static void copy(File from, File to) throws Exception {
// 2,读取from,写出到to
InputStream in = new FileInputStream(from);
OutputStream out = new FileOutputStream(to);
// 3,批量的读和写
int b = 0;// 记录每次读取到的数据
//源码:数组默认的长度就是8*1024
byte[] bs = new byte[8*1024];//用来缓存数据
while ((b = in.read(bs)) != -1) {//读取数组中的内容
out.write(bs);// 把读到的数组里的内容写出去
}
// 4,关闭资源
in.close();
out.close();
}
}
1.10 IO中flush()和close()的区别
- close()默认包含了一次flush()操作,关闭之后,就不能再写入了。
- flush()刷新,flush()之后,可以接着写入。
1.11 BIO、NIO、AIO的区别
- 阻塞IO,BIO 就是传统的 java.io包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。
它的优点就是代码比较简单、直观;缺点就是IO 的效率和扩展性很低,容易成为应用性能瓶颈。 - 非阻塞IO,NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer
等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。 - 异步IO,AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它AIO(Asynchronous IO),异步 IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。但目前还不够成熟,应用不多
2.序列化
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
2.1 序列化与反序列化
- 序列化:利用ObjectOutputStream,对象的信息,按固定格式转成一串字节值输出并持久保存到磁盘化。
- 反序列化:利用ObjectInputStream,读取磁盘中序列化数据,重新恢复对象。
2.2 序列化的特点
- 需要序列化的文件必须实现Serializable接口以启用其序列化功能。
- 不需要序列化的数据可以被修饰为static的,由于static属于类,不随对象被序列化输出。
- 不需要序列化的数据也可以被修饰为transient临时的,只在程序运行期间,在内存中存在不会被序列化持久保存。
- 在反序列化时,如果和序列化的版本号不一致时,无法完成反序列化。
- 每个被序列化的文件都有一个唯一id,如果没有添加编译器会根据类的定义信息计算产生一个版本号。
2.3 序列化的应用场景
- 常用于服务器之间的数据传输,序列化成文件,反序列化读取数据。
- 常用于使用套接字流在主机之间传递对象
2.4 序列化与反序列化的实现
class Student implements Serializable{
//创建对象用
public Student(String name, int age, String addr) {
this.name = name;
this.age = age;
this.addr = addr;
}
String name;
int age;
String addr;
- 序列化
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream( "D:\\teach\\a\\student.txt"));
Student s = new Student("张三",20,"成都");
os.writeObject(s);
os.close();//关闭输出资源
- 反序列化
ObjectInputStream in =new ObjectInputStream(new FileInputStream("D:\\teach\\a\\student.txt"));
//读到的对象,默认是Object,需要强转成子类
Student s2 = (Student)in.readObject();
2.5 反序列化时子类和父类构造方法调用规则
- 若父类未实现序列化接口,会调用父类的默认构造方法作为默认父类对象,而子类已被序列化,不用再调用自己的构造方法
- 如果父类实现了序列化接口,则父类和子类都可以被序列化,在反序列子类对象时,不会再调用父类和子类的构造方法