前情提要
2024-3-1的面试中,被面试官问到了基本没使用过的io,本人对于处理io仅限于文件上传用过。。着实回答不上来,故通过各种平台学习了io方面的知识,形成此文。
后由于io部分的比较难点的知识如:io涉及的设计模式、io的源码分析、nio的使用对当时的我过于困难,故推迟至今出文。
本文先分享对于Java文件类的使用。
在Java中,I/O流用于处理各种数据源和目标,包括文件、网络连接、内存缓冲区等。
我们一般是从用IO操作文件来步入学习,因此了解IO之前,首先要了解文件的使用。
File类的简单讲解与使用
在Java中,File用于表示文件或目录路径的抽象。这代表着无论指定路径下是否存在该目录或文件,都能去创建此指定路径下的File对象。
指定路径可以通过绝对路径或相对路径来实现。
- 绝对路径是包含盘符的全路径,例如maven的setting文件的默认路径
C:\Users\arrayofsky\.m2。 - 相对路径是相较于本项目的工作路径,这个本项目的工作路径是一个比较暧昧的词汇,可以通过
System.getProperty("user.dir")获取。
由于不同操作系统的路径分隔符不一样,建议通过java.io.File.separator获取路径分隔符。
如何创建File
从构造器来看,创建File的方式还是挺多的,这里只挑常用且基础的来讲。
File(String pathname, int prefixLength)
File(String child, File parent)
File(String pathname)
File(String parent, String child)
File(File parent, String child)
File(URI uri)
- 绝对路径示例:
B:\\workspace\\a.txt默认windows操作系统/parent/parent2/a.txt这也算绝对路径
- 相对路径示例:
arrayofsky-redis/a.txt路径为用户工作路径+相对路径 - 直接填文件名 默认为相对路径 即 用户工作路径+文件名
其他的都大同小异,无非是通过字符串拼接等方式来实现,不做赘述。
演示如下:
String property = System.getProperty("user.dir");
File file1 = new File("parent1/parent2/test1.txt");
File file2 = new File("/parent1/parent2/test2.txt");
File file3 = new File("B:/parent1/paren2/test3.txt");
System.out.println(property);
System.out.println(file1.getAbsolutePath());
System.out.println(file2.getAbsolutePath());
System.out.println(file3.getAbsolutePath());
输出结果:
B:\workspace\java\arrayofsky-jvm
B:\workspace\java\arrayofskyjvm\parent1\parent2\test1.txt
B:\parent1\parent2\test2.txt
B:\parent1\paren2\test3.txt
尤其需要注意test2
获取文件信息
可以通过调用File对象的成员方法,来执行以下操作
- 获取文件名 getName()
- 获取文件绝对路径 getAbsolutePath()
- 获取文件父目录 getParent()
- 返回文件大小(字节) length()
- 判断文件是否存在 exists()
- 判断是否是一个文件 isFile()
- 判断是否是一个目录 isDirectory()
api比较简单,且见名知意,不做过多解释,可自行测试验证
操作文件
可以通过调用File对象的成员方法,来执行以下操作
- 创建一级目录 mkdir
- 创建多级目录 mkdirs
- 创建文件 createNewFile
- 删除文件或目录 delete
- 重命名文件 renameTo
- 遍历文件 listFiles
其中,对创建多级目录和遍历文件做以下测试及讲解:
遍历文件:
File[] files = file.listFiles();
for(File file1 : files){
//执行进一步操作
}
创建多级目录:可以把不存在的多级路径一起创建出来
File file = new File("B:\\test1\\test2\\test3\\test4");
try {
boolean mkdirs = file.mkdirs();
System.out.println(mkdirs);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
小结
这样对于File类的基本使用是没有问题了。不过提到File类,就不得不提Java7推出的Files、Path、Paths类了,下面也做简单的讲解,不感兴趣可跳过,不影响后文。
Java7新特性—Files,Path,Paths的用法(可跳过)
Java7中文件IO发生了很大的变化,专门引入了很多新的类:
java.nio.file.DirectoryStream;
java.nio.file.FileSystem;
java.nio.file.FileSystems;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.nio.file.attribute.FileAttribute;
java.nio.file.attribute.PosixFilePermission;
java.nio.file.attribute.PosixFilePermissions;
在java中文件或是目录习惯用java.io.File对象来表示,但是File类有很多缺陷
- 它的很多方法不能抛出异常
- 它的delete方法经常莫名其妙的失败等,旧的File类经常是程序失败的根源。
因此在Java7中有了更好的替代:java.nio.file.Path及java.nio.file.Files。
- Path接口的名字非常恰当,就是表示路径的。
- API中讲Path对象可以是一个文件,一个目录,或是一个符号链接,也可以是一个根目录。
- 用法很简单。创建Path并不会创建物理文件或是目录,path实例经常引用并不存在的物理对象,要真正创建文件或是目录,需要用到Files类。
- Files类是一个非常强大的类,它提供了处理文件和目录以及读取文件和写入文件的静态方法。
- 可以用它创建和删除路径、复制文件、检查路径是否存在等。
- 此外,Files还拥有创建流对象的方法。
Paths
Paths类仅由静态方法组成,通过转换路径字符串返回Path或URI。
//将路径字符串或连接到路径字符串的字符串序列转换为 Path,可以get("c:/abc");或者get("c:","abc")
//这里用了可变形参的表示方式
static Path get(String first, String... more)
//将给定的URI转换为Path对象
static Path get(URI uri)
Path
Path就是取代File的,用于来表示文件路径和文件。
可以有多种方法来构造一个Path对象来表示一个文件路径,或者一个文件:
该接口的实现是不可变且安全的,可供多个并行线程使用。
- 创建Path
//和File一样,并没有实际创建路径,而是一个指向d:/users/日记5.txt路径的引用
Path path=FileSystems.getDefault().getPath("d:/users/日记5.txt");
//Paths类提供了这个快捷方法,直接通过它的静态get方法创建path
Path path=Paths.get("d:/users/日记5.txt");
Path path= = new File("d:/users/日记5.txt").toPath();
Path接口没什么判断方法,其实更多的判断和操作都在Files工具类里面
boolean isAbsolute()
//告诉这条路是否是绝对的
boolean endsWith(Path other)
//测试此路径是否以给定的路径结束
boolean endsWith(String other)
//测试此路径是否以给定字符串结束,如"c:/a/banana/cat"可以以"/banana/cat"结尾,但不能以"t"结尾
boolean startsWith(Path other)
//测试此路径是否以给定的路径开始。
boolean startsWith(String other)
//测试此路径是否以给定字符串开始,跟上面一样规律
Path getFileName()
//将此路径表示的文件或目录的名称返回为 Path对象,文件名或文件夹名,不含路径
Path getName(int index)
//返回此路径的名称元素作为 Path对象。目录中最靠近root的为0,最远的为(count-1),count由下面的方法获得
int getNameCount()
//返回路径中的名称元素的数量。0则只有root
Path getParent()
//返回 父路径,如果此路径没有父返回null,如/a/b/c返回/a/b,配合下面的方法消除"."或".."
Path normalize()
//返回一个路径,该路径是冗余名称元素的消除。如消除掉"."、".."
Path getRoot()
//返回此路径的根组分作为 Path对象,或 null如果该路径不具有根组件。如返回"c:/"
Path relativize(Path other)
//构造此路径和给定路径之间的相对路径。有点难理解,p1-"Topic.txt",p2-"Demo.txt",p3-"/Java/JavaFX/Topic.txt",p4-"/Java/2011";;那么p1和p2的结果是"../Demo.txt";;p2和p1的结果是"../Topic.txt";;p3和p4的结果是"../../2011";;p4和p3的结果是"../JavaFX/Topic.txt"
Path resolve(String other)
//将给定的路径字符串转换为 Path。如"c:/a/b"和字符串"c.txt"的结果是"c:/a/b/c.txt";更像是拼接
Path resolveSibling(String other)
//将给定的路径字符串转换为 Path。如"c:/a/b.txt"和字符串"c.txt"的结果是"c:/a/c.txt";更像是替换
Path subpath(int beginIndex, int endIndex)
//返回一个相对的 Path ,它是该路径的名称元素的子序列,如"d:/a/b/c.txt"参数为(1,3)返回一个"b/c.txt"
Path toAbsolutePath()
//返回表示此路径的绝对路径的 Path对象。包括盘符和文件名或文件夹名
Iterator<Path> iterator()
//返回此路径的名称元素的迭代器。"c:/a/b/c.txt"的迭代器可以next出以下"a""b""c.txt"
File toFile()
//返回表示此路径的File对象
Files
Files类只包含对文件,目录或其他类型文件进行操作的静态方法。主要和Path接口的对象进行配合使用
1.判断方法
static boolean exists(Path path, LinkOption... options)
//测试文件是否存在。
static boolean notExists(Path path, LinkOption... options)
//测试此路径所在的文件是否不存在。
static boolean isDirectory(Path path, LinkOption... options)
//测试文件是否是目录。
static boolean isExecutable(Path path)
//测试文件是否可执行。
static boolean isHidden(Path path)
//告知文件是否被 隐藏 。
static boolean isReadable(Path path)
//测试文件是否可读。
static boolean isRegularFile(Path path, LinkOption... options)
//测试文件是否是具有不透明内容的常规文件。说实话,我也不太懂常规文件指的是啥
static boolean isSameFile(Path path, Path path2)
//测试两个路径是否找到相同的文件。
static boolean isSymbolicLink(Path path)
//测试文件是否是符号链接。//
static boolean isWritable(Path path)
//测试文件是否可写。
2.删除方法
static boolean deleteIfExists(Path path)
//删除文件(如果存在)。
static void delete(Path path)
//删除文件。
3.复制方法
static long copy(InputStream in, Path target, CopyOption... options)
//将输入流中的所有字节复制到文件。
//关于CopyOption则是一个被继承的接口主要有枚举类StandardCopyOption和LinkOption
// 1.StandardCopyOption
// REPLACE_EXISTING(也就是替换覆盖)
// COPY_ATTRIBUTES(将源文件的文件属性信息复制到目标文件中)
// ATOMIC_MOVE(原子性的复制)都是字面意思
// 2.LinkOption
// NOFOLLOW_LINKS
static long copy(Path source, OutputStream out)
//将文件中的所有字节复制到输出流。
static Path copy(Path source, Path target, CopyOption... options)
//将文件复制到目标文件。
4.移动和重命名方法
static Path move(Path source, Path target, CopyOption... options)
//将文件移动或重命名为目标文件。
5.创建文件和文件夹方法
static Path createDirectories(Path dir, FileAttribute<?>... attrs)
//首先创建所有不存在的父目录来创建目录。
static Path createDirectory(Path dir, FileAttribute<?>... attrs)
//创建一个新的目录。
static Path createFile(Path path, FileAttribute<?>... attrs)
//创建一个新的和空的文件,如果该文件已存在失败。
6.文件属性方法
static <V extends FileAttributeView> V getFileAttributeView(Path path, 类<V> type, LinkOption... options)
//返回给定类型的文件属性视图。指定六个视图其中一种,上面一开始有点到。拿到的xxxAttributeView会有一个跟下面一样名字的readAttributes方法来得到一个xxxAttributes真正的获取操作就全是在这个xxxAttributes类的对象里get啦
static <A extends BasicFileAttributes> A readAttributes(Path path, 类<A> type, LinkOption... options)
//读取文件的属性作为批量操作。指定一个xxxAttributes,得到一个实例,通过里面的方法得到时间等基本属性
static Object getAttribute(Path path, String attribute, LinkOption... options)
//读取文件属性的值。这个 String attributes 参数的语法固定是以 view-name:comma-separated-attributes 的形式;view-name指定视图名如basic,posix,acl等,不写默认为basic;有写默认要加":";可以用"basic:*"或"*"读取所有,又或者用"basic:size,lastModifiedTime"读取大小和修改时间。具体还有那些属性可以看具体指定的类,比如basic视图就看BasicFileAttributes这个接口都有哪些方法,可以读取哪些文件属性。同理,下面的 String attributes 一样是这个理
static Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)
//读取一组文件属性作为批量操作。
static Path setAttribute(Path path, String attribute, Object value, LinkOption... options)
//设置文件属性的值。
/* 下面这些也是获取属性的方法,不过还没研究到是怎么用的 */
static FileTime getLastModifiedTime(Path path, LinkOption... options)
//返回文件的上次修改时间。
static UserPrincipal getOwner(Path path, LinkOption... options)
//返回文件的所有者。
static Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options)
//返回文件的POSIX文件权限。
static Path setLastModifiedTime(Path path, FileTime time)
//更新文件上次修改的时间属性。
static Path setOwner(Path path, UserPrincipal owner)
//更新文件所有者。
static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms)
//设置文件的POSIX权限。
static long size(Path path)
//返回文件的大小(以字节为单位)。
7.读取、编辑文件内容方法
static BufferedReader newBufferedReader(Path path)
//打开一个文件进行阅读,返回一个 BufferedReader以高效的方式从文件读取文本。
static BufferedReader newBufferedReader(Path path, Charset cs)
//打开一个文件进行阅读,返回一个 BufferedReader ,可以用来以有效的方式从文件读取文本。
static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options)
//打开或创建一个写入文件,返回一个 BufferedWriter ,可以用来以有效的方式将文本写入文件。
static BufferedWriter newBufferedWriter(Path path, OpenOption... options)
//打开或创建一个写入文件,返回一个 BufferedWriter以高效的方式写入文件。
static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
//打开或创建文件,返回可访问的字节通道以访问该文件。
static SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
//打开或创建文件,返回可访问的字节通道以访问该文件。
static InputStream newInputStream(Path path, OpenOption... options)
//打开一个文件,返回输入流以从文件中读取。
static OutputStream newOutputStream(Path path, OpenOption... options)
//打开或创建文件,返回可用于向文件写入字节的输出流。
static byte[] readAllBytes(Path path)
//读取文件中的所有字节。
static List<String> readAllLines(Path path)
//从文件中读取所有行。
static List<String> readAllLines(Path path, Charset cs)
//从文件中读取所有行。
static Path write(Path path, byte[] bytes, OpenOption... options)
//将字节写入文件。
static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options)
//将文本行写入文件。
static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
//将文本行写入文件。
8.遍历文件列表方法
static DirectoryStream<Path> newDirectoryStream(Path dir)
//打开一个目录,返回一个DirectoryStream以遍历目录中的所有条目。最好用 try-with-resources 构造,可以自动关闭资源。返回的 DirectoryStream<Path> 其实可以直接使用 Iterator或者for循环 遍历每一个 dir 下面的文件或目录
static DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
//上面方法的重载,通过实现参数二(有一个 boolean accept(Path p) 方法来判断文件是否符合需要)来达到过滤的目的。如accept方法中写"return (Files.size(p) > 8192L);"来匹配大于8k的文件
static DirectoryStream<Path> newDirectoryStream(Path dir, String glob)
//上面方法的重载,可以通过参数二作为过滤匹配出对应的文件。如 newDirectoryStream(dir, "*.java") 用于遍历目录里所有java后缀的文件
static Stream<Path> walk(Path start, FileVisitOption... options)
//深度优先遍历。返回一个 Stream ,它通过 Path根据给定的起始文件的文件树懒惰地填充 Path 。
static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options)
//深度优先遍历。返回一个 Stream ,它是通过走根据给定的起始文件的文件树懒惰地填充 Path 。
static Stream<Path> list(Path dir)
//返回一个懒惰的填充 Stream ,其元素是 Stream中的条目。返回的 Stream 里封装了一个 DirectoryStream 用于遍历。
总结
反正都是一些api级别的东西,大家只需要知道需要用File的地方建议用Path来代替。