17 – 18. Java学习 – File类、IO流
文章目录
1. File类
1.1. File类的构造函数
File常用的构造函数如下:
- public File(String pathname) 根据文件的路径(绝对路径 相对路径)构建文件对象
- File(String parent, String child) 根据父路径和子路径构建文件对象
- File(File parent, String child) 根据父File对象和子路径构建文件对象
@Test
public void test01() {
// 根据文件路径构建文件对象
File file1 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\HHH.jpg");
System.out.println("文件是否存在: " + file1.exists()); // 文件是否存在: true
// 根据父路径和子路径构建文件对象
File file2 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote", "HHH.jpg");
System.out.println("文件是否存在: " + file2.exists()); // 文件是否存在: true
// 根据父文件对象和子路径构建一个File对象
File partent = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote");
File file3 = new File(partent, "HHH.jpg");
System.out.println("文件是否存在: " + file3.exists()); // 文件是否存在: true
}
文件的分隔符的问题:
在不同操作系统下面,文件的分隔符是不一样的。
在windows操作系统下面,文件的路径分割符是 \\。但是在linux操作系统下面,文件路径的分割符是 /。
为了动态描述文件的分隔符,我们可以使用File.separator。
@Test
public void test02() {
// 关于文件的分隔符的问题
File file = new File("F:" + File.separator + "DownloadLX" + File.separator + "A_software" +
File.separator + "Note" + File.separator + "JavaNote" + File.separator + "HHH.jpg");
System.out.println(file); // F:\DownloadLX\A_software\Note\JavaNote\HHH.jpg
System.out.println("文件是否存在: " + file.exists()); // 文件是否存在: true
}
1.2. File类的常用方法
1.2.1. 创建和删除方法
@Test
public void test03() {
// 创建文件的方法
File file1 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\A.java");
try {
System.out.println(file1.createNewFile()); // true
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("文件是否创建: " + file1.exists()); // 文件是否创建: true
// 创建单级文件目录
File file2 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\B");
System.out.println(file2.mkdir()); // true
System.out.println("文件夹是否创建: " + file2.exists()); // 文件夹是否创建: true
// 创建多级文件夹
File file3 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\B\\C\\D");
System.out.println(file3.mkdirs()); // true
System.out.println("多级文件夹是否创建: " + file3.exists()); // 多级文件夹是否创建: true
// 重命名文件
// 如果路径一样,renameTo方法就是重命名
System.out.println("重命名是否成功: " +
file1.renameTo(new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\ZZ.java"))); // 重命名是否成功: true
// 如果路径不一样,renameTo方法就是剪切并重命名
File file4 = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\ZZ.java");
System.out.println("剪切并重命名是否成功: " +
file4.renameTo(new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\B\\ZZ.java"))); // 剪切并重命名是否成功: true
// 删除文件或者文件夹
// 删除文件
if (file1.exists()) {
System.out.println("是否删除成功: " + file1.delete());
} else {
System.out.println(file1.getName() + "文件夹不存在,删除失败");
} // A.java文件夹不存在,删除失败
if (file4.exists()) {
System.out.println("是否删除成功: " + file4.delete());
} else {
System.out.println(file4.getName() + "文件夹不存在,删除失败");
} // 是否删除成功: true
// 删除文件夹,只能删除空文件夹
System.out.println("是否删除成功: " + file3.delete()); // 是否删除成功: true
}
1.2.2. 判断方法
@Test
public void test04(){
File file = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\HHH.jpg");
// 判断文件是否已经存在
System.out.println(file.exists()); // true
// 判断是否是文件
System.out.println(file.isFile()); // true
// 判断是否是文件夹
System.out.println(file.isDirectory()); // false
// 判断当前文件是否是隐藏文件
System.out.println(file.isHidden()); // false
// 判断文件是否只读
System.out.println(file.canRead()); // true
// 判断文件是否可写
System.out.println(file.canWrite()); // true
// 判断当前文件的路径是否是绝对路径
System.out.println(file.isAbsolute()); // true
}
1.2.3. 获取方法
@Test
public void test05(){
File file = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\HHH.jpg");
// 获取文件名称
System.out.println(file.getName()); // HHH.jpg
// 获取文件最后修改时间 -- 类型为Long的毫秒值
System.out.println(file.lastModified()); // 1658495989730
Date date = new Date(file.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date)); // 2022-07-22 21:19:49
// 获取当前文件的父级目录
System.out.println(file.getParent()); // F:\DownloadLX\A_software\Note\JavaNote
// 获取当前文件的父级路径的文件对象
System.out.println(file.getParentFile().getName()); // JavaNote
// 获取当前文件的绝对路径
System.out.println(file.getAbsolutePath()); // F:\DownloadLX\A_software\Note\JavaNote\HHH.jpg
// 获取文件内容长度
System.out.println(file.length()); // 45860
}
1.2.4. 遍历方法
@Test
public void test06(){
// 返回本机电脑盘符
for (File file : File.listRoots()) {
System.out.println(file);
}
/*C:\
D:\
E:\
F:\
G:\
H:\*/
// 获取指定目录下面文件内容
File file = new File("F:\\DownloadLX\\A_software\\Note\\JavaNote\\B");
String[] list = file.list();
for(String fileName : list){
System.out.println(fileName);
}
/*C
ZZ.java*/
// 获取指定目录下面文件内容,使用File对象进行描述
for (File listFile : file.listFiles()) {
System.out.println(listFile);
}
/*F:\DownloadLX\A_software\Note\JavaNote\B\C
F:\DownloadLX\A_software\Note\JavaNote\B\ZZ.java*/
}
1.3. File案例
1.3.1. 根据指定目录,获取当前目录下面的.java文件
方法1:
@Test
public void test07(){
File file = new File("F:\\DownloadLX\\A_software");
for (String s : file.list()) {
if(s.endsWith(".java")){
System.out.println(s);
}
}
}
结果:
方法2:
@Test
public void test08(){
File file = new File("F:\\DownloadLX\\A_software");
String[] list = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
});
for (String s:list){
System.out.println(s);
}
}
结果:
方法3:
@Test
public void test09() {
File file = new File("F:\\DownloadLX\\A_software");
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java");
}
});
for (File f : files) {
System.out.println(f.getName());
}
}
结果:
1.3.2. 将指定目录下面的文件归类输出
@Test
public void test10(){
File file = new File("F:\\DownloadLX\\A_software");
File[] files = file.listFiles();
for (File f:files){
if(f.isDirectory()){
System.out.println("文件夹是: "+f.getName());
}
}
for (File f:files){
if(f.getName().endsWith(".java")){
System.out.println(".java文件是: "+f.getName());
}
}
}
结果:
1.3.3. 使用过滤器,将指定目录下面的所有文件夹输出
@Test
public void test11() {
File file = new File("F:\\DownloadLX\\A_software");
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File file1 = new File(dir, name);
return file1.isDirectory();
}
});
for (File f : files) {
System.out.println("文件夹有: " + f.getName());
}
}
结果:
2. IO流
2.1. 字节流
2.1.1. 字节流输入
字节输入流的体系结构:
- 抽象类InputStream是所有输入流的基类(父类)
- FileInputStream 字节输入流,继承了InputStream
例子: 读取一个txt文档里面的内容
使用字节输入流读取文件的步骤:
构建文件对象
构建字节输入流对象
执行read操作,读取文件
关闭流通道,释放资源
方法1:一个字节一个字节读取
@Test
public void test01() throws Exception {
File file = new File("F:\\DownloadLX\\A_software\\Note\\Hello.txt");
FileInputStream fileInputStream = new FileInputStream(file);
int data = 0;
while ((data = fileInputStream.read()) != -1) {
System.out.print((char) data); // HelloWorld
}
fileInputStream.close();
}
-
data是什么?
- 读到的是每一个文字的10进制的ascii编码值,我们可以通过char强转,得到对应的字符
-
为什么循环的条件是不能等于-1?
- 读到了文件的末尾处,这个read的返回值就为-1,说明文件内容读完了
-
为什么最后要关闭资源? 为什么要inputStream.close()?
- 止内存泄漏(Memory leak)
-
内存泄漏和内存溢出的区别?
-
内存泄漏:每次程序执行完,所占用的内存不能被释放,刚开始没有什么问题,但是如果越来越多的内存得不到释放,就会导致有效内存越来越少,最终内存不够用,导致内存溢出。
- 内存溢出(out of Memory):内存空间不够用
-
方法2:byte数组一次读取多个字节
@Test
public void test02() throws Exception {
FileInputStream fileInputStream = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\Hello.txt");
byte[] bytes = new byte[1024];
int length = 0;
while ((length = fileInputStream.read(bytes)) != -1) {
/**
* 根据字节数组构建字符串对象
* 参数1: 字节数组
* 参数2: 根据字节数组的起始索引位置开始构建字符串
* 参数3: 从索引值位置开始,根据多少个字节构建字符串。
*/
String s = new String(bytes, 0, length);
System.out.println(s); // HelloWorld
}
fileInputStream.close();
}
- length = fileInputStream.read(bytes):可以一次性读取1024个字节,其中length保存的是实际读取的字节的个数。
- 因为有可能在最后一次读取的时候,读取的字节数不满1024个,于是需要length保存的实际读取的字节个数。
2.1.2. 字节输出流
字节输出流体系结构:
- 抽象类OutputStream是所有字节输出流的基类
- FileOutputStream字节输出流的子类,一般用它进行文件的写操作
例子:将字符串写入文件
@Test
public void test03() throws Exception {
File file = new File("F:\\DownloadLX\\A_software\\Note\\Hello.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
String content = "Wow,it's so perfect";
fileOutputStream.write(content.getBytes());
fileOutputStream.close();
}
结果:
注意:
以上这种方式写入文件,会将原文件的内容覆盖。若是不希望原内容被覆盖,只需要在FileOutputStream的构造函数中传入参数true即可
例子:不覆盖原内容
@Test
public void test04() throws Exception {
File file = new File("F:\\DownloadLX\\A_software\\Note\\Hello.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
String content = ". I really like it";
fileOutputStream.write(content.getBytes());
fileOutputStream.close();
}
结果:
2.1.3. 字节输入输出流案例
案例:用字节输入输出流实现图片的拷贝
@Test
public void test05() throws Exception {
// 构建输入流
FileInputStream input = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\HHH.jpg");
// 构建输出流
FileOutputStream output = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\ZZZ.jpg");
// 读取
byte[] bytes = new byte[1024];
int length = 0;
while ((length = input.read(bytes)) != -1) {
// 写入
output.write(bytes, 0, length);
}
// 关闭输入输出流,谁后开启,谁先关闭
output.close();
input.close();
}
2.1.4. 缓冲字节输入流
缓冲字节输入流的继承关系如下图所示:
不管是缓冲字节输入流还是缓冲字节输出流都是通过减少文件读写次数(减少IO次数)来提高文件的读写效率。在缓冲流内部维护了一个byte类型的数组,数组的长度达到了8192。
我们使用缓冲输出流进行写操作的时候,我们首先会把内容写到缓冲区里面去,然后等缓冲区写满了之后,再调用flush方法进行写入操作。
一般在文件内容比较多的场景下使用缓冲流。
例子:对大文件进行读写操作
@Test
public void test06() throws Exception {
// 开始的时间
long start = System.currentTimeMillis();
BufferedInputStream bif = new BufferedInputStream(new FileInputStream(
new File("F:\\DownloadLX\\A_software\\Note\\sql1.log")));
int length = 0;
byte[] bytes = new byte[1024 * 8];
while ((length = bif.read(bytes)) != -1) {
String str = new String(bytes, 0, length);
System.out.println(str);
}
// 结束时间
long end = System.currentTimeMillis();
System.out.println("花费的时间是:" + (end - start));
bif.close();
}
2.1.5. 缓冲字节输出流
缓冲字节输出流的继承关系如下图所示:
例子:使用缓冲字节输出流进行文件的写操作
@Test
public void test07() throws Exception {
BufferedOutputStream bufferOutput = new BufferedOutputStream(
new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\WWW.txt"));
String content = "hello,vem";
bufferOutput.write(content.getBytes());
bufferOutput.flush(); // 可以不写,该方法默认执行
bufferOutput.close();
}
2.1.6. 缓冲字节输入输出流案例
案例: 使用缓冲字节输入流和缓冲字节输出流进行大文件的拷贝
@Test
public void test08() throws Exception {
// 设置输入流
FileInputStream fileInputStream = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\sql1.log");
BufferedInputStream input = new BufferedInputStream(fileInputStream);
// 设置输出流
FileOutputStream fileOutputStream = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\sql2.log");
BufferedOutputStream output = new BufferedOutputStream(fileOutputStream);
// 读取
byte[] bytes = new byte[1024 * 8];
int length = 0;
while ((length = input.read(bytes)) != -1) {
// 写入
output.write(bytes, 0, length);
}
// 关闭通道
output.close(); // 自带flush
input.close();
}
2.2. 字符流
为了解决字节流中文输入输出乱码的问题,引入了字符流。
2.2.1. 字符输入流
字符输入流的体系结构:
- 抽象类Reader是字符流的基类
- FileReader是抽象类Reader的间接子类
例子:使用字符流进行文件的读操作
字符流使用步骤:
根据文件构建一个字符输入流对象
根据字符输入流对象,调用read方法进行读操作
关闭资源
方法1:一个字符一个字符的读
@Test
public void test09() throws Exception {
FileReader fileReader = new FileReader("F:\\DownloadLX\\A_software\\Note\\file.txt");
int data = 0;
while ((data = fileReader.read()) != -1) {
System.out.print((char) data);
}
fileReader.close();
}
方法2:借助char[]数组
@Test
public void test10() throws Exception {
FileReader fileReader = new FileReader("F:\\DownloadLX\\A_software\\Note\\file.txt");
char[] chars = new char[1024];
int length = 0;
while ((length = fileReader.read(chars)) != -1) {
System.out.println(new String(chars, 0, length));
}
fileReader.close();
}
2.2.2 字符输出流
字符流的体系结构:
- 抽象类Writer是字符输出流的基类
- FileWriter是具体的实现类,间接的继承了Writer
例子:将字符串写入到文件中
@Test
public void test11() throws Exception {
FileWriter fileWriter = new FileWriter("F:\\DownloadLX\\A_software\\Note\\file.txt", true);
String content = "今天天气真好";
fileWriter.write(content);
fileWriter.close();
}
注意:如果使用字符输出流实现图片的拷贝,会出现拷贝图片与原图片大小不一致,拷贝图片打不开的情况。
因为计算机中的所有信息都是以二进制形式进行的存储。在读取时,字符流会自动对这些二进制按照码表进行编码处理,但是图片本来就是二进制文件,不需要进行编码。
并且编码时,有一些恰巧在码表中有对应,可以进行处理。但并不是所有的二进制都可以在码表中找到对应的值,这时信息就会丢失。
2.2.3. 缓冲字符输入流
缓冲字符输入流的体系结构:
- Reader 字符输入流的基类
- BufferedReader 缓冲字符输入流 直接继承了Reader
例子:对大文件进行读操作
@Test
public void test12() throws Exception {
FileReader reader = new FileReader("F:\\DownloadLX\\A_software\\Note\\news.txt");
//构建一个缓冲字符输入流对象
BufferedReader br = new BufferedReader(reader);
//保存读取一行的内容
String content = null;
while ((content = br.readLine()) != null) {
System.out.println(content);
}
//关闭资源
br.close();
reader.close();
}
2.2.4. 缓冲字符输出流
缓冲罐字符输出流的体系结构:
- Writer 字符输出流的基类
- BufferedWriter 缓冲字符输出流 是Writer的子类
例子:将文本内容使用缓冲字符输出流写入到文件中
@Test
public void test13() throws Exception{
//构建缓冲字符输出流对象
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("F:\\DownloadLX\\A_software\\Note\\news.txt"), true));
//准备写入的内容
String message = "今天天气有点阴";
writer.newLine(); //写出内容之前,进行换行操作
//执行写操作
writer.write(message);
//关闭资源
writer.close();
}
2.2.5. 缓冲字符输入输出流案例
案例:对文本内容进行排序输出
文本内容:
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。今天天气真好
@Test
public void test14() throws Exception {
// 构建字符流读取
FileReader fileReader = new FileReader("F:\\DownloadLX\\A_software\\Note\\file.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
// 构建字符流输出
FileWriter fileWriter = new FileWriter("F:\\DownloadLX\\A_software\\Note\\new_file.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
// 利用TreeMap自动排序特性
TreeMap<String, String> tree = new TreeMap<>();
// 读取
String content = null;
while ((content = bufferedReader.readLine()) != null) {
// split()方法的参数是一个正则表达式,而在正则表达式中,某些字符如点.存在特殊含义,需要使用\\转义
String[] split = content.split("\\.");
tree.put(split[0], split[1]);
}
// 写入
for (int i = 1; i <= 9; i++) {
bufferedWriter.write(i + "." + tree.get(String.valueOf(i)));
bufferedWriter.newLine();
}
// 关闭
bufferedWriter.close();
bufferedReader.close();
}
结果:
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。今天天气真好
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
9.今当远离,临表涕零,不知所言。
2.3. Properties资源的读取
Properties 类是 Java 中用于处理配置文件的工具类,它继承自 Hashtable 类,实现了 Map 接口。
- 主要用于读取和写入属性文件,以键值对的形式存储数据
- 配置文件通常以 .properties 为扩展名,其中每一行表示一个属性或配置项
优势:
- 配置文件管理: 读取和保存应用程序的配置信息。例如数据库连接信息、用户设置等
- 国际化: 加载不同语言的资源文件,方便国际化应用程序
- 持久化: 可以将配置信息持久化到文件,方便下次程序启动时重新加载
例子:将properties文件中的内容进行读操作
方式1:
@Test
public void test15() throws Exception {
Properties properties = new Properties();
//load方法 将字节输入流加载到Properties对象中
properties.load(new FileInputStream(new File("D:\\doitedu\\javaseday15-io\\src\\main\\resources\\user.properties")));
// 获取Properites对象中的内容
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement().toString();
String value = properties.getProperty(key);
System.out.println(key + " " + value);
}
}
方式2:基于类加载器进行加载(重要)
@Test
public void test16() throws Exception {
Properties properties = new Properties();
// 基于类加载器进行文件的读操作(从类路径下加载)
InputStream in = IOTest.class.getClassLoader().getResourceAsStream("user.properties");
properties.load(in);
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement().toString();
String value = properties.getProperty(key);
System.out.println(key + " " + value);
}
}
2.4. 序列化
序列化:将Java对象转换成字节输出流(ObjectOutputStream),将对象持久化的保存在文件中,就是序列化。
反序列化:将字节输入流(ObjectInputStream)恢复为java对象的过程。
对象进行序列化和反序列化的前提是:对象所属的类必须实现Serializable接口
序列化和反序列化对应的类的体系结构:
- InputStream 字节输入流的基类
- ObjectInputStream字节输入流是InputStream 的直接子类,用于读取操作。
- OutputStream 字节输出流的基类
- ObjectOutputStream字节输出流是OutputStream的直接子类,用于写出操作。
2.4.1. 序列化和反序列化案例
例子:将User对象进行序列化和反序列化
@Data // 自动生成get set方法,自动生成toString方法
@NoArgsConstructor // 创建无参构造函数
@AllArgsConstructor // 创建带所有参数的构造函数
public class User implements Serializable {
private String name;
private String password;
}
class UserTest {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("AAA");
user.setPassword("admin");
// 构建输出流对象
FileOutputStream fileOutput = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fileOutput);
oos.writeObject(user);
// 关闭资源
oos.close();
fileOutput.close();
// 构建输入流对象
FileInputStream fileInput = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectInputStream ois = new ObjectInputStream(fileInput);
User user1 = (User) ois.readObject();
System.out.println(user1);
// 关闭资源
ois.close();
fileInput.close();
}
}
例子:将List集合进行序列化和反序列化
public class UserTest {
@Test
public void test01() throws Exception {
User aaa = new User("AAA", "123");
User bbb = new User("BBB", "234");
User ccc = new User("CCC", "345");
List<User> users = new ArrayList<>(Arrays.asList(aaa, bbb, ccc));
// 写入
FileOutputStream out = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(users);
// 关闭资源
oos.close();
out.close();
}
@Test
public void test02() throws Exception {
// 输出
FileInputStream in = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectInputStream ois = new ObjectInputStream(in);
ArrayList<User> arrayList = (ArrayList<User>) ois.readObject();
System.out.println(arrayList);
// 关闭资源
ois.close();
in.close();
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class User implements Serializable {
private String name;
private String password;
}
2.5. CommonsIO
这个库是Apache Commons项目的一部分,目的是为Java开发者提供一系列通用的IO操作工具。Java标准库中的IO操作有时候显得有点复杂和笨重。Apache Commons IO就是为了解决这个问题而诞生的。
Apache Commons IO库主要提供以下几个方面的功能:
- 文件操作:简化文件的读取和写入
- 流操作:提供了更简单的方法来处理Java的输入输出流
- 文件监控:能够监控文件系统的变化,比如文件的创建、修改和删除
Apache Commons IO库优势:
- 简化代码:使用Apache Commons IO可以使得代码更加简洁,提高代码的可读性和可维护性
- 功能强大:提供了很多Java标准库中没有的便利功能
- 社区支持:作为Apache项目的一部分,它拥有强大的社区支持和持续的更新
2.5.1. 文件读写操作简化
// commons io读取
@Test
public void test01() throws Exception {
File file = new File("F:\\DownloadLX\\A_software\\Note\\io\\out.txt");
String s = FileUtils.readFileToString(file, "UTF-8");
System.out.println(s);
}
// commons io写入
@Test
public void test02() throws Exception {
File file = new File("F:\\DownloadLX\\A_software\\Note\\io\\out.txt");
String massage = "斩尽春风未肯归";
FileUtils.writeStringToFile(file, massage, "UTF-8", true);
}
总结:
使用Apache Commons IO和不使用时的代码量存在较大差异。在传统的Java IO操作中,即使是简单的文件读写,也需要处理流的开启和关闭,还要处理异常。而使用Apache Commons IO,这些都被内部处理了,大大减少了代码量,提高了代码的可读性和可维护性。
2.5.1. 文件复制操作
// 文件复制
@Test
public void test03() throws Exception {
FileInputStream input = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\io\\HHH.jpg");
FileOutputStream output = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\io\\ZZZ.jpg");
// 文件
FileInputStream in = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\io\\out.txt");
FileOutputStream out = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\io\\output.txt");
// 文件
FileReader re = new FileReader("F:\\DownloadLX\\A_software\\Note\\io\\out.txt");
FileWriter wr = new FileWriter("F:\\DownloadLX\\A_software\\Note\\io\\write.txt");
// 实现拷贝
IOUtils.copy(input, output);
IOUtils.copy(in, out);
IOUtils.copy(re, wr);
// 关闭资源
IOUtils.closeQuietly(re);
IOUtils.closeQuietly(wr);
}
CommonsIO的复制方法可以传不同的参数,如下图
比较常用的是:copy(InputStream input,OutputStream output),copy(Reader input,Writer output)。一个传字节流,一个传字符流。
其中copy(InputStream input,OutputStream output)既可以传字节流数据,也可以传字符流数据,但是copy(Reader input,Writer output)只能传字符流数据,传字节流数据会产生乱码。
2.5.3 对文件系统进行监控
在日常的软件开发和系统管理中,监控文件系统的需求相当普遍。
比如,当一个配置文件被修改时,程序可能需要重新加载这个文件。或在一个文件夹中新增了文件,系统需要自动进行一些处理。
// 监控器
public static void main(String[] args) throws Exception {
// 监控的文件夹路径
String directoryPath = "F:\\DownloadLX\\A_software\\Note\\io";
// 创建一个文件观察器,用于监控指定的目录
FileAlterationObserver observer = new FileAlterationObserver(
directoryPath,
FileFilterUtils.and(
FileFilterUtils.fileFileFilter(), // 只监控文件
FileFilterUtils.suffixFileFilter(".txt") // 只监控.txt文件
)
);
// 创建一个监听器,用于响应文件变化事件
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println("文件被创建: " + file.getName());
}
@Override
public void onFileDelete(File file) {
System.out.println("文件被删除: " + file.getName());
}
});
// 创建文件变化监控器,并添加观察器
FileAlterationMonitor monitor = new FileAlterationMonitor(2500); // 检查间隔为5秒
monitor.addObserver(observer);
// 启动监控器
monitor.start();
System.out.println("文件监控启动,正在监控: " + directoryPath);
}
2.5.4. 文件过滤器和文件比较器
如果此时,有一个包含数千个文件的目录,需要找出其中的所有JPEG图片文件,或者找出最后修改时间在特定日期之后的文件。这时候手动一个个查看显然是不现实的,这时候就需要用到文件过滤器。
例子:假设想要列出一个文件夹中所有的.txt文件。
// 文件过滤器
@Test
public void test05() {
File file = new File("F:\\DownloadLX\\A_software\\Note\\io");
SuffixFileFilter txtSuffixFileFilter = new SuffixFileFilter(".txt");
// 使用文件过滤器获取所有.txt文件
Collection<File> files = FileUtils.listFiles(file, txtSuffixFileFilter, null);
for (File f : files) {
System.out.println("找到.txt文件: " + f.getName());
}
}
2.5.5. 对大文件读取
// 大文件读取
@Test
public void test06() throws Exception {
File largeFile = new File("F:\\DownloadLX\\A_software\\Note\\io\\sql1.log");
LineIterator iterator = FileUtils.lineIterator(largeFile);
while (iterator.hasNext()) {
String line = iterator.nextLine();
System.out.println(line);
}
}
补充
1. lombok
lombok可以帮助我们自动构造get set方法,重写toString方法,自动生成无参和带所有参数的构造函数。
使用方法:
- 引入依赖
- 在类上面使用对应的注解
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
@Data // 自动生成get set方法,自动生成toString方法
@NoArgsConstructor // 创建无参构造函数
@AllArgsConstructor // 创建带所有参数的构造函数
public class User implements Serializable {
private String name;
private String password;
}
class UserTest {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("AAA");
user.setPassword("admin");
// 构建输出流对象
FileOutputStream fileOutput = new FileOutputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fileOutput);
oos.writeObject(user);
// 关闭资源
oos.close();
fileOutput.close();
// 构建输入流对象
FileInputStream fileInput = new FileInputStream("F:\\DownloadLX\\A_software\\Note\\user.txt");
ObjectInputStream ois = new ObjectInputStream(fileInput);
User user1 = (User) ois.readObject();
System.out.println(user1);
// 关闭资源
ois.close();
fileInput.close();
}
}