Java核心类库-IO
一、文件读写
1、java.io.File
1.1 概述
1.2 构造方法
- 构造方法的声明
- 应用常用构造方法
A.File(String pathname)
import java.io.File;
import java.io.IOException;
public class Demo_IO_1 {
public static void main(String[] args) throws IOException {
File file = new File("E:\\study01"); // 创建文件对象
boolean flag = file.createNewFile();// creatNewFile():判断是否创建成功;---true:如果指定的文件不存在且已成功创建; false:如果指定的文件已存在
System.out.println(flag?"study01文件 创建成功!":"study01文件 创建失败!");
}
}
运行结果:【前提是E盘不存在此文件】
B.File(File parent, String child)
上面的File parent指的是父文件夹
结果显示:
C.File(String parent, String child)
上面String parent指的是父文件
结果显示:
1.3 字段
不同操作系统路径分隔符可能不同,"//"不会适用于所有操作系统
1.4 常用方法
Java使用File工具操作磁盘文件,只要在构造方法中填写某个文件的完整路径,即可通过创建好的文件对象开展各项处理。相关处理有以下4大类。
tips:所有的路径都是抽象路径;绝对路径的对立面是相对路径
绝对路径:从系统根目录或者盘符开始的路径就是绝对路径,就是一条完整的路径比如“c:\program files\abc.txt”
相对路径:是相对当前项目的,你只写a.txt,它就会建立在当前项目的根目录下路径 aaaa\a.txt,没有盘符,就是相对你项目根目录下一个aaaa目录下的一个a.txt
1G=1024MB=1024KB=1024字节
1.4.1 检查文件状态
File工具既可操作某个文件又可操作某个目录。
- exists:判断当前文件/目录是否存在,若存在则返回true。
- canExecute:判断当前文件是否允许执行,若允许返回true。
- canRead:判断当前文件是否允许读取,若允许返回true。
- canWrite:判断文件是否允许写入,若允许返回true。
- isHidden:判断当前文件/目录是否隐藏,若隐藏返回true。
- isDirectory:判断当前是否为目录,若为目录返回true。
- isFile:判断当前是否为文件,若为文件则返回true。
1.4.2 获取文件信息
只要磁盘中存在某个文件/目录,就能调用相关方法获取该文件/目录的基本信息。
- getAbsolutePath:获取当前文件/目录的绝对路径
- getPath:获取当前文件/目录的相对路径
- getName:如果当前为文件,就返回文件名称;如果为目录,则返回目录名称
- getParent:获取当前文件/目录的上级目录路径
- length:如果当前为文件,就返回文件大小;如果为空目录,就返回0;如果当前目录非空,就返回目录的索引空间大小,索引保存了目录内部文件的基本信息。
- lastModified:获取当前文件/目录的最后修改时间,单位为毫秒
1.4.3 管理文件操作
对文件进行创建,删除,更名等管理操作。
- mkdir:只创建最后一级目录,如果上级目录不存在,就返回false
- mkdirs:创建文件路径中所有不存在的目录
- createNewFile:创建新文件。如果文件目录不存在,那就仍出异常IOException
- delete:删除文件,也可以删除空目录
- renameTo:重命名文件,把源文件的名称改为目录名称
1.4.4 遍历某目录下的文件
文件遍历操作是专门提供给目录专用的,主要遍历方法有list和listFiles两个。其中前者返回的文件路径数组是String类型,后者返回的文件路径数组是File类型的。
注意,listFiles还有三个同名的重载方法,要加以区分。
tips:FileFilter 与 FilenameFiler都是函数式接口,所以它们的实例可以采用Lambda表达式来改写。
2、文件遍历案例
import java.io.File;
/**
* 文档的遍历
*/
public class Demo_IO_4 {
private static File[] file;
public static void main(String[] args) {
File file = new File("E:\\"); // E盘下的文件对象
File[] files = file.listFiles(); // E盘下文件夹的文件数组
listFile(files); // 遍历
}
/**
* 遍历文件数组
* @param file
*/
public static void listFile(File[] file) {
if (file != null && file.length > 0) {
// 文件存在且不为空
for (File file_1 : file) {
if (file_1.isFile()) {
// 判断是否为文件
if (file_1.getName().endsWith(".mp4")) {
// 判断是否有以.mp4结尾的文件
if (file_1.length() > 900 * 1024 * 1024) {
// 文件大小大于900M
System.out.println("找到了一个mp4文件" + file_1.getAbsolutePath()); // 输出文件的路径
}
}
} else {
// 文件夹
File[] file2 = file_1.listFiles(); // 文件数组
listFile(file2); // 递归
}
}
}
}
}
3、文件过滤器(了解)
3.1 概述
3.2 应用实例
简单粗暴:
package com.kaikeba;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File e = new File("d://");
listFiles(e);
}
public static void listFiles(File file){
// 自定义的方法
if(file != null && file.length() != 0){
// 文件存在且不为空
// 1,创建一个过滤器,并描述规则
FileFilter filter = new AVIFileFilter();
// 2,通过文件获取子文件夹
File [] files = file.listFiles(filter); // 对象的方法
// 3,递归遍历所有文件
for(File f : files) {
if(f.isDirectory()) {
listFiles(f);
}else {
System.out.println("发现一个txt文件:" + f.getAbsolutePath());
}
}
}
}
static class AVIFileFilter implements FileFilter{
// 为实现过滤器接口 需要定义一个类 这里为静态内部类
@Override
public boolean accept(File pathname) {
// 只保留txt文件和文件夹
if(pathname.getName().endsWith(".txt") || pathname.isDirectory()) {
return true;
}
return false;
}
}
}
改进:
用匿名内部类实现FileFilter接口,并直接作为参数,调用listFiles获得筛选过后的文件
package com.kaikeba;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File e = new File("d://");
listFiles(e);
}
public static void listFiles(File file){
if(file != null && file.length() != 0){
// 文件存在且不为空
File [] files = file.listFiles(new FileFilter() {
// 通过匿名内部类作为参数的方法 实现过滤
@Override
public boolean accept(File pathname) {
// 只保留txt文件和文件夹
if(pathname.getName().endsWith(".txt") || pathname.isDirectory()) {
return true;
}
return false;
}
});
// 递归遍历所有文件
for(File f : files) {
if(f.isDirectory()) {
listFiles(f);
}else {
System.out.println("发现一个txt文件:" + f.getAbsolutePath());
}
}
}
}
}
4、相对路径与绝对路径
绝对路径:以盘符开始,是一个完整的路径,例如c://a.txt
相对路径:在Java中是相对于本项目目录路径(即 本项目的根目录),这是一个不完整的路径,在Java开发中很常用 ,例如 a.txt
tips:这里讲的相对路径只适用于Java中的,而之后学习的网络编程等就有另外定义!!!
import java.io.File;
/**
* 相对路径与绝对路径
*/
public class Demo_IO_5 {
public static void main(String[] args) {
File file1 = new File("c:\\a.txt");// 绝对路径
File file2 = new File("a,txt");// 相对路径
System.out.println("file1:" + file1.getAbsolutePath());
System.out.println("file2:" + file2.getAbsolutePath());
}
}
运行结果:
---------------------------------------------------------
二、I/O输入输出流
这里的出和入是个相对的概念,出就是相对程序的出,入也是相对程序的入
(即:从程序里出去就输出;从别处进到程序叫输入 )
1. IO流概述
理解:可以将这种数据传输操作,看做一种数据的流动 , 按照流动的方向分为输入Input和输出Output
Java中的IO操作主要指的是 java.io包下的一些常用类的使用;通过这些常用类对数据进行读取(输入Input) 和 写出(输出Output)
tips:一切皆字节:
- 计算机中的任何数据(文本,图片,视频,音乐等等)都是以二进制的形式存储的.
- 在数据传输时 也都是以二进制的形式存储的.
- 后续学习的任何流 , 在传输时底层都是二进制.
2. IO流的分类
按照流的方向来分,可以分为: 输入流 和 输出流.
按照流动的数据类型来分,可以分为: 字节流 和 字符流.[其都包含输入/输出流]
字节流:(顶级父类)
- 输入流 : InputStream
- 输出流 : OutputStream
字符流:(顶级父类)
- 输入流 : Reader
- 输出流 : Writer
3. IO流的类----字节流
3.1 java.io.OutputStream
3.1.1 输出字节流的概述
输出字节流 — OutputStream是抽象类
说白了,就是从控制台输出到磁盘文件中去。
3.1.2 常用方法
注意:
写完一定要采用关闭close()方法;
有关写入的方法中,write方法传入int类型的参数解释:
从上可知,说白了就是将int类型变为8位的字节
3.2 java.io.FileOutputStream
java.io.FileOutputStream是OutputStream抽象类的常用直接类,其读写方法与抽象类没啥区别。
3.2.1 概述
3.2.2 构造方法
tip: 其中append为true则表示,在文件末尾添加数据,否则表示重新写入数据
3.2.3 方法应用
有关读写以及构造方法的运用
import java.io.FileOutputStream;
import java.io.IOException;
/**
* OutputStream抽象类的实体类
* FileOutputStream
*/
public class Demo_IO_6 {
public static void main(String[] args) throws IOException {
FileOutputStream file = new FileOutputStream("E:\\a.txt");// 没有添加append参数,表示默认false 即覆盖原数据
//写法一
file.write(65);
//写法二
byte[] bytes = {
'A','B','C'};
file.write(bytes);
//写法三
byte[] bytes_1 = {
'A','B','C','D'};
file.write(bytes_1,1,3);// off:从索引位置开始 len:索引的长度
//file.close(); //如果添加这行,则会报错:Stream Closed 流被关闭
byte[] bytes_3= {
'A','B','C'};
file.write(bytes);
file.close(); //写在哪在哪关闭
System.out.println("已写出");
}
运行结果:
通过字符串+getBytes函数,获得字节数组:
import java.io.FileOutputStream;
import java.io.IOException;
/**
* OutputStream抽象类的实体类
* FileOutputStream
*/
public class Demo_IO_6 {
public static void main(String[] args) throws IOException {
FileOutputStream file = new FileOutputStream("E:\\a.txt");// 没有添加append参数,表示默认false 即覆盖原数据
byte[] bytes = "ABCDEF".getBytes();
file.write(bytes);
file.close();
System.out.println("已写出");
}
}
运行结果:
3.3 java.io.InputStream
3.3.1 输入字节流的概述
输入字节流—IntputStream是抽象类
说白了就是从磁盘把文件输入到控制台上。
3.3.2 常用方法
理解最后两个方法:
3.4 java.io.FileInputStream
java.io.FileIntputStream是IntputStream抽象类的常用直接类,其读写方法与抽象类没啥区别。
3.4.1 概述
3.4.2 构造方法
3.4.3 方法应用
- read()
import java.io.FileInputStream;
import java.io.IOException;
/**
* a.txt 文件有 abcdef 文字
*/
public class Demo_IO_7 {
public static void main(String[] args) throws IOException {
FileInputStream file = new FileInputStream("E:\\a.txt");
while(true){
byte a = (byte)file.read();// 每次只读取一个字节,且为整型,但可以做强转
if(a == -1){
// 用于判断如果文本读取到最后(末尾),则会返回-1的标记
break;
}
System.out.println(a);
}
file.close();
}
}
运行结果:
- read(byte[] b)
import java.io.FileInputStream;
import java.io.IOException;
/**
* a.txt 文件有abcdefghijklmnopqrstuvwxyz 文字
*/
public class Demo_IO_8 {
public static void main(String[] args) throws IOException {
FileInputStream file = new FileInputStream("E:\\a.txt");
byte[] byte_1 = new byte[10];
file.read(byte_1);
System.out.println(new String(byte_1));
file.read(byte_1);
System.out.println(new String(byte_1));
file.read(byte_1);
System.out.println(new String(byte_1));
}
}
运行结果:
从上面运行结果可以看出:
read(byte[] buff) 这个方法,是传一个数组做容器,将读到的每个byte数据,都放到这个数组里,一个位置放一个。如果流里没那么多了,自然后面就空着。
如果上面这种用过了的byte_1又接着去用来存新数据,必然是有可能被上一次读到的数据干扰。所以第二次读10个,第三次读6个,第三次只会覆盖掉前面6个,后面4个位置还是上一次的。
所以我们读是用read(buff),里面的真实数据是new String(buff,0,len)。len就是到底这次读取有多少个数据是有效的,是本次读取到的。这个len对应你这个demo来说,第一次是10,第二次是10,第三次是6.
改进代码如下:
import java.io.FileInputStream;
import java.io.IOException;
/**
* a.txt 文件有abcdefghijklmnopqrstuvwxyz 文字
*/
public class Demo_IO_8 {
public static void main(String[] args) throws IOException {
FileInputStream file = new FileInputStream("E:\\a.txt");
byte[] byte_1 = new byte[10];
int len = file.read(byte_1);
System.out.println(new String(byte_1,0,len));
len = file.read(byte_1);
System.out.println(new String(byte_1,0,len));
len = file.read(byte_1);
System.out.println(new String(byte_1,0,len));
}
}
运行结果:
3.5 文件加密和解密工具
import java.io.*;
import java.util.Scanner;
/**
* 加密或解密
*/
public class Demo_IO_9 {
public static void main(String[] args) throws IOException {
System.out.println("请您输入您要加密/解密文件的全路径:");
Scanner input = new Scanner(System.in);
String s = input.nextLine();
// 原文件 :study.text
File oldFile = new File(s);
// 新文件 : new_study.text
File newFile = new File(oldFile.getParentFile(),"new_" + oldFile.getName());
// 输入流 :从磁盘输入到控制台
FileInputStream in = new FileInputStream(oldFile);
// 输出流:从控制台输出到磁盘
FileOutputStream out = new FileOutputStream(newFile);
while(true){
byte bytes = (byte)in.read();
// 判断是否读取到内容
if(bytes == -1){
break;
}
// 异或特点:任何数据异或 相同的数字异或两次,结构都是其本身
out.write(bytes^10);
}
in.close();
out.close();
System.out.println("加密或解密成功!");
}
}
加密
tip:异或的特点
任何数据异或 相同的数字异或两次,结构都是其本身
运行结果:
再次运行程序进行解密
3.6 字节流读取文字
由于工程使用的是UTF-8字符编码,所以在读取工程中的文本文件时,不会出现乱码
由于提前限定了一次读取的字节数为10,所以出现了读取不到一个完整汉字的情况,因而出现乱码。但是UTF-8使用动态编码表,由于提前不知道每个字符需要多少字节,所以此方法不行。
故下面引入了字符流,用来解决读取半字的问题。
4. IO流的类----字符流
4.1 java.io.Writer
4.1.1 写入字符流的概述
写入字符流 — Writer是抽象类
4.1.2 常用方法
- write:往文件写入字符串。注意该方法存在多个同名的重载方法
- append:也是往文件写入字符串。按字面意思,append方法是往文件末尾追加字符串,然而并非如此,append方法与write方法的写入位置是同样的,二者的区别在于,append方法会把空指针当作null写入文件,而write方法不知此写入空指针
- close:关闭文件写入器
4.2 java.io.FileWriter
java.io.FileWriter是Writer抽象类的常用直接类,其读写方法与抽象类没啥区别。
4.2.1 概述
4.2.2 构造方法
4.2.3 方法应用
- write()
import java.io.FileWriter;
import java.io.IOException;
/**
* 字符流Writer
*/
public class Demo_IO_11 {
public static void main(String[] args) throws IOException {
FileWriter file = new FileWriter("E:\\study_1.txt");
//写入一
file.write("我是爱摸鱼的TT");
//写入二
file.write('爱');
//写入...
file.close();
System.out.println("写入成功");
}
}
运行结果:
- append()
import java.io.FileWriter;
import java.io.IOException;
/**
* 字符流Writer
*/
public class Demo_IO_11 {
public static void main(String[] args) throws IOException {
FileWriter file = new FileWriter("E:\\study_1.txt");
file.write("我是爱摸鱼的TT");
file.append(',').append("很久不见").append('!');
file.close();
System.out.println("写入成功");
}
}
运行结果:
注意:
决定是否在原文件基础上追加的,是声明字符流对象是的append属性(为true则是追加模式);
append与write在实际实现上,没有区别,但是append会返回Writer对象(即自己本身);
append方法的返回值(Writer类型)可以强转为该对象(FileWriter类型【上转型】),所以可以继续调用append,因而称为“追加”;
4.3 java.io.Reader
4.3.1 读取字符流的概述
4.3.2 常用方法
- skip:跳出若干字符。注意FileReader的skip方法跳过的是字符数,不是字节数。
- read:从文件读取数据到字节数组。注意该方法存在多个同名的重载方法。
- close:关闭文件读取器
4.4 java.io.FileReader
4.4.1 概述
java.io.FileReader是Reader抽象类的常用直接类,其读写方法与抽象类没啥区别。
4.4.2 构造方法
4.4.3 方法应用
5. flush刷新管道—Writer
字符输出时,以字符为单位,但计算机中都是以字节为单位。当一个字符占用多个字节时,字符输入流未读取单个字符全部字节之前,会将已读取字节放入缓存;
字符输出流fw.flush()会将缓存中字符强制写入到文件中,fw.close()也会有此效果;
如果不执行的话,就不会将字符写入文件中,如图:
加入其中一个方法,即会写入文件中:
6.字节转换字符流
转换流:将字节流装饰为字符流:使用了装饰者模式;
为什么要使用字节流+转换流?直接字符流不香吗?:由于平常使用时,可能获取的是字节流,所以才有这种转换方式.
- 字节流的输出转换为字符流的写入
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* 字节转换字符流:写入
*/
public class Demo_IO_14 {
public static void main(String[] args) throws IOException {
FileOutputStream file = new FileOutputStream("E:\\study_1.txt"); // 字节流的输出流
OutputStreamWriter out = new OutputStreamWriter(file); // 转换流
out.write("我是爱摸鱼的TT,很久不见!");
System.out.println("写入成功!");
out.flush();// 字符流更新
file.close(); // 字节流关闭
}
}
运行结果:
- 字节流的输入转换为字符流的读取
7. Print与缓冲区
7.1 打印流
7.1.1 打印字节流与打印字符流
- 打印字节流
- 打印字符流(记得flush或close)
注意:
打印字节流和打印字符流在使用上差别不大,但字符流(Writer)需要调用flush方法,否则不会写入到文件中;
7.1.2 字节流转换为打印流
7.2 缓冲I/O字符流
Java设计了缓冲区就是预留下来的做为急用的那一部分,为暂时置放输出或输入资料的内存。
7.2.1 缓存写入流(BufferedWriter)
7.2.1.1 概述
Java设计了缓冲区写入器BufferedWriter,它的write方法并不是直接写入文件,而是先写入一块缓存,等到缓存写满了再将缓存上的数据写入文件。由于缓存空间位于内存,写入缓存等同于访问内存,这样大大提高了写文件的性能,比FileWriter好多了。
7.2.1.2 构造方法
7.2.1.3 方法应用
与FileWriter相比,新增的方法:
- newLine:在当前位置添加换行标记(Win系统是回车加换行)
- flush:立即将缓冲区中的数据写入磁盘。默认情况下,要等缓冲区满了才会写入磁盘中,或者是调用了close方法,但有时一定要立即写入,那就强用flush方法强行写磁盘
import java.io.*;
/**
* 缓存区读写
*/
public class Demo_IO_16 {
public static void main(String[] args) throws IOException {
FileWriter file_1 = new FileWriter("E:\\study_1.txt");
BufferedWriter bwriter = new BufferedWriter(file_1);// 根据文件读取器创建缓存写入器
bwriter.write("我是爱摸鱼的TT!");
bwriter.newLine(); // 另起一行
bwriter.write("很久不见呀~");
bwriter.close();
file_1.close();
}
}
7.2.1.4 小结
- FileWriter的每次write调用都会直接写入磁盘,不但效率低,性能也差
- BufferedWriter的每次write调用会先写入缓冲区,知道缓冲区满了才写入磁盘
- 缓冲区大小默认是8KB,查看源码defaultCharBufferSize = 8192
- 资源释放的close方法会把缓冲区的剩余数据写入磁盘
- 或者中途调用flush方法也可以提前将缓冲区的数据写入磁盘
7.2.2 缓存读取流(BufferedReader)
7.2.2.1 概述
将字符输入流转换为带有缓存可以一次读取一行的缓存字符读取流
字节流 —> 字符流 —> 缓存读取流
7.2.2.2 构造方法
7.2.2.3 方法应用
补充readLine()方法:从文件中读取一行数据
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* 缓存读取流
*/
public class Demo_IO_16 {
public static void main(String[] args) throws IOException {
FileReader file_2 = new FileReader("E:\\study_1.txt");
BufferedReader read = new BufferedReader(file_2);
for(int i = 1;;i++){
String s = read.readLine();
if(s == null){
// 读到了空指针,表示已经到了文件末尾了
break;
}
System.out.println(s);
}
read.close();
file_2.close();
}
}
运行结果:
注意:
- FileReader只能一个一个字符地读,或者一次性读进字符数组
- BufferedReader还支持一行一行地读
7.3 缓冲I/O字节流
文件输出流FileOutputStream跟FileWriter同样有个毛病,每次调用write都会直接写到磁盘,使得频繁地写操作,性能极其低下,缓存输出流的用法与缓存写入器非常相似,主要体现在以下4点:
- 每次创建缓存输出流对象之前,都要先构建文件输出流对象,然后据此构建缓存输出流对象。
- 它的write方法先把数据写到缓存,等到缓存满了才写入磁盘,或者调用close方法时自动将缓存数据写入磁盘
- 缓存输出流仍然提供flush方法,该方法可将缓存中的数据立即写入磁盘。
- 由于字节流操作的数据形式为字节数组,因此无论是缓存输出流还是缓存输入流,都不提供按行读写的功能。
import java.io.*;
/**
* 缓存I/O字节流
*/
public class Demo_IO_19 {
public static void main(String[] args) {
// 缓存流输入
try (FileInputStream file = new FileInputStream("E:\\study.txt")){
BufferedInputStream bis = new BufferedInputStream(file);
byte[] bytes = new byte[100];
while(true){
int n = bis.read(bytes);
if(n == -1){
break;
}
System.out.println(new String(bytes));//把字节数组转换为字符串
}
} catch (Exception e) {
e.printStackTrace();
}
// 缓存流输出
try (FileOutputStream file_2 = new FileOutputStream("E:\\study.txt")) {
BufferedOutputStream bof = new BufferedOutputStream(file_2);
String str = "我是爱摸鱼的TT~";
bof.write(str.getBytes());// 把字节数组写入缓存输出流
}catch(Exception e){
e.printStackTrace();
}
}
}
8. 收集异常日志
普通的异常控制台显示
可以将异常信息保存在txt文档中,并加上日期,便于后期核查
这种收集异常日志方法作为了解,后续会有专门成熟异常收集的框架,依赖库等等,就不用编写。
9. Properties类
9.1 概述
Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置文件中很多变量是经常改变的,为了方便用户的配置,能够让用户脱离程序本身去修改相关的变量设置。就像在Java中,其配置文件常为**.properties文件**,是以键值对的形式进行参数配置的。
properties继承HashTable属于Map集合(键值对),但其扩展部分含有IO相关用法(配置文件)
9.2 常用方法
既然其属于Map集合,那么Map集合的所有操作都可用于Properties类中,但是还有部分补充的方法,这样便于理解,见名知意。
- store方法:将properties对象内容写入字节流/字符流所指的文件中
//字符流的写入
- load方法:将字节流/字符流指向的文件内容加载到properties对象中
//字符流的读取
- get与getProperty
get返回Object对象,getProperty返回字符串。
看其源码:
//字符流的读取
10. 序列化技术(了解)
10.1 概述
由于垃圾回收机制(GC)的存在,一些属性或对象,在程序关闭之后,便消失,无法重复利用,所以有人在想能不能将把对象完整的存储在文件中,使用时再取出来,即对象在内存中存储的字符序列(看上去像是乱码);
虽然序列化很方便,但是却十分容易产生Bug(占Bug总数将近三分之一),所以Java官方提出近几年将要进行整改,建议大家不要使用此方法;(了解下也是有必要的)
编程应注意:
读写序列化对象的专用I/O流包括对象输入流ObjectInputStream 和 对象输出流ObjectOutputStream。其中,前者用于从文件中读取对象信息,它的readObject方法完成了读对象操作;后者用于将对象信息写入文件,它的writeObject方法完成了写对象操作。
10.2 序列化
序列化:将内存对象存储在磁盘文件数据的过程。
Java官方规定,所有对象均不能序列化,想要序列化,需添加标记—Serializable接口
添加标记:实现接口Serializable。添加代码后没有任何飘红,即说明不需要实现任何方法,因此这个接口被称为标记接口。
10.3 反序列化
反序列化:将磁盘文件内容转化成内存对象的过程。
11. try-with-resources的表达式
这块知识既用到了IO流的知识,又用到了异常处理的知识
11.1 问题引入
在上面所举例代码里都是直接throws抛出异常,没有进行异常处理,而我们写项目时,经常都是要进行异常处理的try
-catch(-finally),不能直接throws抛出,所以就存在以下的问题。
1)在文件流使用完毕后需要关闭
2)为了使close一定被执行,需要将其放在finally中
3)所以需要将fr提到try之前,但有可能没东西传入,导致产生空指针异常
4)又得继续try-catch处理
5)综上,为了获取一个字符,需要进行这么多操作,不仅空间上浪费,时间也得浪费很多,因此就需要采取对应的解决方案。
11.2 概述
在Java7开始,try语句支持try-with-resources的表达式,意思是携带某些资源去尝试干活,并在尝试结束自动释放这些资源。具体做法:
- 在try(…)里面声明的资源会在try/catch结束自动释放
- try(…)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔
- 相当于编译器自动补充了finally代码块中的资源释放操作
- 资源类必须实现java.lang.(Auto)Closeable接口,这样close方法才会由系统调用
- 一般来说,文件I/O、套接字、数据库等均实现该接口
11.3 解决方法
1)jdk1.7时:在try()中new的对象会在try或catch块执行完毕后自动执行close。但要求,能使用此方法的类必须实现Closeable或AutoCloseable的接口;
【如果是自定义的类,也必须实现Closeable或AutoCloseable的接口】
2)但是,如果后面还有代码块需要用到try中的对象时,就显得不方便了。
因此,JDK9进行了改进
例如:
import java.io.*;
/**
* try-with-resources
*/
public class Demo_IO_18 {
public static void main(String[] args) {
// 字符流的写出
try(FileWriter file = new FileWriter("E:\\study_2.txt");) {
file.write("我是爱摸鱼的TT!");// 往文件写入字符串
} catch (IOException e) {
e.printStackTrace();
}
// 字符流的读取
File file_1 = new File("E:\\study_2.txt");// 创建一个指定路径的文件对象
try(FileReader file1 = new FileReader(file_1)) {
char[] c = new char[(int)file_1.length()];// 创建一个与文件大小等长的字符数组
int len = file1.read();
System.out.println(new String(c));// 把字符数组转换为字符串
} catch (IOException e) {
// 捕获到输入输出异常
e.printStackTrace();
}
}
}
总之,这种写法省时省力便捷,往后会经常用到。
三、总结
一定要清楚 IO流(输入流 读取数据, 输出流 写出数据)
IO用于在设备检进行数据传输的操作
IO流分类:
- 字节流 InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream - 字符流 Reader FileReader BufferedReader
Writer FileWriter BufferedWriter