Java学习——文件管理与I/O流
文章目录
一、文件管理
Java语言使用File类对文件和目录进行操作,查找文件时需要实现FilenameFilter或FileFilter接口。另外,读写文件内容可以通过FileInputStream、 FileOutputStream、FileReader和FileWriter类实现, 它们属于I/O流。这些类和接口全部来源于java.io包。
(1). File类
API对这个类的解释:
文件和目录路径名的抽象表示。
用户界面和操作系统使用依赖于系统的路径名字符串来命名文件和目录。 此类提供了一个抽象的,与系统无关的分层路径名视图。 抽象路径名有两个组成部分:
可选系统有关的前缀字符串,如磁盘驱动器符, "/“对于UNIX根目录下,或者”\\"的Microsoft Windows UNC路径,一个零个或多个字符串名称的序列。
抽象路径名中的第一个名称可以是目录名称,对于Microsoft Windows UNC路径名,可以是主机名。 抽象路径名中的每个后续名称表示目录; 姓氏可以表示目录或文件。 空抽象路径名没有前缀和空名称序列。
将路径名字符串转换为抽象路径名或从抽象路径名转换本质上取决于系统。 将抽象路径名转换为路径名字符串时,每个名称将通过默认分隔符的单个副本与下一个名称分隔开。 默认的名称分隔符由系统属性file.separator定义,并在此类的公共静态字段separator和separatorChar中可用。 当路径名字符串转换为抽象路径名时,其中的名称可以由默认的名称 - 分隔符或基础系统支持的任何其他名称 - 分隔符分隔。
绝对路径、相对路径与抽象路径:
路径名,无论是抽象的还是字符串形式,可以是绝对路径名也可以是相对路径名。 绝对路径名是完整的,因为不需要其他信息来定位它表示的文件。 相反,相对路径名必须根据从其他路径名获取的信息来解释。 默认情况下, java.io程序包中的类始终解析当前用户目录的相对路径名。 此目录由系统属性user.dir命名,通常是调用Java虚拟机的目录。
抽象路径名的父级可以通过调用此类的getParent()方法获得,并且包含路径名的前缀以及路径名的名称序列中的每个名称(除了最后一个)。 每个目录的绝对路径名是任何File对象的祖先,其绝对抽象路径名以目录的绝对路径名开头。 例如,抽象路径名"/usr"表示的目录是路径名"/usr/local/bin"表示的目录的祖先。
抽象路径,通俗来说:
操作系统使用与系统相关的路径名字符串来命名文件和目录。此类呈现分层路径名的一个抽象的、与系统无关的视图。
抽象路径名有两个组件:
一个可选的与系统有关的前缀字符串,比如盘符,“/” 表示 UNIX 中的根目录,“\” 表示 Microsoft Windows UNC 路径名,以及零个或更多字符串名称的序列。
除了最后一个,抽象路径名中的每个名称代表一个目录;最后一个名称既可以代表目录,也可以代表文件。空的抽象路径名没有前缀和名称序列。
我们一般创建File对象,直接在类的构造方法里赋值给文件路径
构造方法:
File(File parent, String child) //从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname) //通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child) //从父路径名字符串和子路径名字符串创建新的 File实例。
File(URI uri) //通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
还有诸多方法:
获得文件名
String getName( ) //获得文件的名称,不包括路径。
String getPath( ) //获得文件的路径。
String getAbsolutePath( ) //获得文件的绝对路径。
String getParent( ) //获得文件的上一级目录名。
文件属性测试
boolean exists( ) //测试当前File对象所表示的文件是否存在。
boolean canWrite( ) //测试当前文件是否可写。
boolean canRead( ) //测试当前文件是否可读。
boolean isFile( ) //测试当前文件是否是文件。
boolean isDirectory( ) //测试当前文件是否是目录。
文件操作
long lastModified( ) //获得文件最近一次修改的时间。
long length( ) //获得文件的长度,以字节为单位。
boolean delete( ) //删除当前文件。成功返回 true,否则返回false。
boolean renameTo(File dest) //将重新命名当 前File对象所表示的文件。成功返回 true,否则返回false。
目录操作
boolean mkdir( ) //创建当前File对象指定的目录。
String[] list() //返回当前目录下的文件和目录,返回值是字符串数组。
String[] list(FilenameFilter filter) //返回当前目录下满足指定过滤器的文件和目录,参数是实现FilenameFilter接口对象,返回值是字符串数组。
File[] listFiles() //返回当前目录下的文件和目录,返回值是File数组。
File[] listFiles(FilenameFilter filter) //返回当前 目录下满足指定过滤器的文件和目录,参数是实现FilenameFilter接口对象,返回值是 File数组。
File[] listFiles(FileFilter filter) //返回当前目录 下满足指定过滤器的文件和目录,参数是实 现FileFilter接口对象,返回值是File数组。
示例:
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File e = new File("D:\\Kugou");
File[] files = e.listFiles();
listFiles(files);
}
/**
* 文件夹遍历
* @param files
*/
public static void listFiles(File[] files) {
if (files!=null && files.length>0) {
//对该文档下的所有内容进行遍历
for (File file : files) {
//当这个文件是文件时,
if (file.isFile()) {
//查找mp3格式的文件
if (file.getName().endsWith(".mp3")) {
//输出这个文件及所在路径
System.out.println("找到一个.mp3文件:" + file.getAbsolutePath());
if (file.length() > 10*1024*1024) { //当文件大小超过 10M,我们执行删除操作
file.delete();
}
}
}else {
//如果找到的时文件夹,对这个文件夹进行遍历(递归)
File[] files2 = file.listFiles();
listFiles(files2);
}
}
}
}
}
D:\Kugou\Music路径下的内容:
D:\Kugou\Music\KugouMusic路径下的内容:
输出结果:
文件夹:
可以看到,对定义的整个文件夹下进行了遍历,并对10M以上的.mp3格式文件进行了删除操作。
(2). 文件过滤
对目录操作有两个过滤器接口:FilenameFilter 和 FileFilter 。它们都只有一个抽象方法accept。
FilenameFilter接口中的accept方法如下:
boolean accept(File dir, String name) //测试指定 dir目录中是否包含文件名为name的文件。
FileFilter接口中的accept方法如下:
boolean accept(File pathname) //测试指定路径名 是否应该包含在某个路径名列表中。
注意 路径中会用到路径分隔符,路径分隔符 在不同平台上是有区别的,UNIX、Linux和 macOS中使用正斜杠“/”,而Windows下使用反 斜杠“\”。Java是支持两种写法,但是反斜 杠“\”属于特殊字符,前面需要加转义符。例如 C:\Users\a.java在程序代码中应该使用 C:\Users\a.java表示,或表示为C:/Users/a.java 也可以。
示例:
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
public class Demo1 {
//java.i0 .*
public static void main(String[] args) throws IOException {
File e = new File("D:\\KuGou");
listFiles(e);
}
public static void listFiles(File file) {
//1.创建一个过滤器并描述规则
FileFilter filter = new MusicFileFilter();
//2.通过文件获取子文件夹
File[] files = file.listFiles(filter);
//开始遍历,遍历前先做条件判断,否做可能会出现空指针异常
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isDirectory()) {
listFiles(f);
} else {
System.out.println("找到一个.mp3格式的文件:" + f);
}
}
}
}
//常见一个过滤器类继承FileFilter
static class MusicFileFilter implements FileFilter {
//重写继承类里的抽象方法accept
@Override
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3") || pathname.isDirectory()) //过滤规则:当文件后缀是.mp3或者是文件夹时,可以accept掉
return true;
return false;
}
}
}
结果:
上面的示例可以进行优化,将MusicFileFilter 类中的规则写入过滤器创建的过程(使用匿名内部类):
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
public class Demo1 {
//java.i0 .*
public static void main(String[] args) throws IOException {
File e = new File("D:\\KuGou");
listFiles(e);
}
public static void listFiles(File file) {
//1.创建一个过滤器并描述规则
//使用匿名内部类来代替单独创建的过滤器类MusicFilter
FileFilter filter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3") || pathname.isDirectory()) { //过滤规则:当文件后缀是.mp3或者是文件夹时,可以accept掉
return true;
}
return false;
}
};
//2.通过文件获取子文件夹
File[] files = file.listFiles(filter);
//开始遍历,遍历前先做条件判断,否做可能会出现空指针异常
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isDirectory()) {
listFiles(f);
} else {
System.out.println("找到一个.mp3格式的文件:" + f);
}
}
}
}
/* //常见一个过滤器类继承FileFilter
static class MusicFileFilter implements FileFilter {
//重写继承类里的抽象方法accept
@Override
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3") || pathname.isDirectory()) //过滤规则:当文件后缀是.mp3或者是文件夹时,可以accept掉
return true;
return false;
}
}*/
}
我们注释掉了过滤器类MusicFileFilter ,结果还是和原来一样。
这时我们在对代码进行优化:第二步中的2.通过文件获取子文件夹中的filter时使用的是,第一步中1.创建一个过滤器并描述规则中新建的filter。这时我们将等号两边连起来:
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
public class Demo1 {
//java.i0 .*
public static void main(String[] args) throws IOException {
File e = new File("D:\\KuGou");
listFiles(e);
}
public static void listFiles(File file) {
//1.创建一个过滤器并描述规则
//使用匿名内部类来代替单独创建的过滤器类MusicFilter
/* FileFilter filter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3") || pathname.isDirectory()) { //过滤规则:当文件后缀是.mp3或者是文件夹时,可以accept掉
return true;
}
return false;
}
};*/
//2.通过文件获取子文件夹
File[] files = file.listFiles(new FileFilter() { //用第一步中的过滤器规则代替。
@Override
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3") || pathname.isDirectory()) { //过滤规则:当文件后缀是.mp3或者是文件夹时,可以accept掉
return true;
}
return false;
}
});
//开始遍历,遍历前先做条件判断,否做可能会出现空指针异常
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isDirectory()) {
listFiles(f);
} else {
System.out.println("找到一个.mp3格式的文件:" + f);
}
}
}
}
}
结果依然:
二、I/O流(简)
概述:
Java将数据的输入输出(I/O)操作当作“流”来处理,“流”是一组有序的数据序列。
“流”分为两种形 式:输入流和输出流,从数据源中读取数据是输入流,将数据写入到目的地是输出流。
提示: 以CPU为中心,从外部设备读取数据到内存,进而再读入到CPU,这是输入(Input, 缩写I)过程;将内存中的数据写入到外部设 备,这是输出(Output,缩写O)过程。所以输入输出简称为I/O。
以字节为单位的流称为字节流,以字符为单位的流称为字符流。
Java SE提供4个顶级抽象类,
两个字节流抽象类:InputStream和OutputStream;
两个字符流抽象类:Reader和Writer。
(1). 字节输入流
字节输入流根类是InputStream
主要的字节输入流:
字节输入流类继承层次
(2). 字节输出流
字节输出流根类是OutputStream。
主要的字节输出流:
字节输出流类继承层次:
(3). 字符输入流
字符输入流根类是Reader,这类流以16位的 Unicode编码表示的字符为基本处理单位。
主要的字符输出流:
字符输出流类继承层次:
(4). 字符输出流
字符输出流根类是Writer,这类流以16位的 Unicode编码表示的字符为基本处理单位。
主要的字符输出流:
字符输出流类继承层次:
(2). try-with-resources
示例:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo15 {
public static void main(String[] args) throws FileNotFoundException {
//try-with-resources
/*try {
FileReader fr = new FileReader("c://book.txt");
int c = fr.read();
System.out.println((char)c);
fr.close();
} catch (IOException e) {
e.printStackTrace();
}*/
//jdk9
FileReader fr = new FileReader("c://book.txt");
PrintWriter pw = new PrintWriter("c://book.txt");
try(fr;pw){
int c = fr.read();
System.out.println((char)c);
}catch (IOException e) {
e.printStackTrace();
}
}
}