jdk内置的一些工具类
java中一些工具类
一. java.util.Objects类
1. 介绍
该类自jdk1.7引入, 此类包含用于对对象进行操作或在操作前检查某些条件的static实用程序方法.
例如: hashcode计算, 对象比较, 判空, toString, 索引范围检查等等.
相比于我们常见的使用表达式, 静态方法可以更好的配合函数式编程.
2. 方法

Objects类中的方法都相当简单, openjdk17中总计代码行数未破500行(包括注释), 这些方法在空安全和索引检查方面非常实用, 特别是结合Stream使用时.
例如:
// 如果a为空, 会抛出空指针
if(a.equals(b)) {
// ...
}
// 可以替换成下面的代码
if(Objects.equals(a, b)) {
// ...
}
// 可能导致NPE
int hashCode = o.hashCode();
// 空安全的hashCode
int hashCode = Objects.hashCode(o);
// 空安全的toString, 如果o为null, 会变成"null"字符串
String s = Objects.toString(o);
// 空安全的toString, 如果o为null, 会返回传入的字符串, 此处为空字符串.
String s = Objects.toString(o, "");
// 排序比较
String a = "Abc";
String b = "abD";
int compared = Objects.compare(a, b, String.CASE_INSENSITIVE_ORDER);
// 空断言, 为空直接报错
Objects.requireNonNull(obj);
// 可修改为空的报错消息
Objects.requireNonNull(obj, "不能为空!");
// 为空则调用supplier获取错误消息, 常用于错误消息较长需要拼接等情况.
Objects.requireNonNull(obj, () -> "不能为空!");
// 为空则使用给定的默认值, 如果默认也为空则报错
var nonNullObj = Objects.requireNonNullElse(obj, defaultObj);
// 为空则调用supplier获取默认值
var nonNullObj = Objects.requireNonNullElse(obj, () -> { .... return defaultObj; });
// 便于函数式编程的空检查
boolean isNull = Objects.isNull(obj);
boolean nonNull = Objects.nonNull(obj);
list.stream().map(...).filter(Objects::nonNull).collect(....);
// 索引范围检查
Objects.checkIndex(index, length);
Objects.checkFromIndexSize(fromIndex, size, length);
Objects.checkFromToIndex(fromIndex, toIndex, length);
二. java.util.Collections类
1. 介绍
集合操作的工具类, Collection, Map, Enumeration 等接口类型的工具类.
2. 方法

内部方法主要有:
-
empty开头的用于创建各种空集合的方法. jdk9以后可以用各种接口类型的空参of静态方法创建. -
checked开头的返回动态类型安全的集合视图的方法. -
synchronized开头的获取同步集合视图的方法. -
unmodifiable开头的获取不可变集合视图的方法, jdk10以后可以用List.copyOf. -
singleton开头创建单个元素的集合的方法, jdk9以后可以用List.of,Set.of,Map.of等替换.
其它方法也都是顾名思义.
- 二分查找:
binarySearch - 交换:
swap - 排序:
sort - 随机打乱:
shuffle - 旋转(末尾元素移到最前, 前面元素后移):
rotate - 频率计数(指定元素出现多少次):
frequency - 无交集判断:
disjoint - 以一个元素的多次重复创建列表:
nCopies - 子列表查找:
indexOfSubList,lastIndexOfSubList - 替换:
replaceAll,copy,fill - 添加:
addAll - 反转:
reverse - 获取反转的
Comparator:reverseOrder
使用需要注意的细节:
-
singleton,empty,unmodifiable,nCopies方法所创建的集合视图是不可变的. -
copy其实是替换目标列表中的元素为源列表中的元素, 当目标列表的size小于源列表会索引越界.var dest = new ArrayList<String>(); // IndexOutOfBoundsException: Source does not fit in dest Collections.copy(dest, List.of("abc")); -
fill同样是替换, 将列表中的元素全部替换为指定元素.var dest = new ArrayList<String>(); Collections.fill(dest, "haha"); System.out.println(dest); // 输出[] dest.add("abc"); Collections.fill(dest, "haha"); // 输出[haha]
三. java.nio.file.Files类
1. 介绍
java.nio.file.Files类是java1.7引入的一个用于简化一些常用IO操作的工具类.
Files 类中的方法没有使用 File 作为参数, 而是使用 Path 类作为参数. Path 是文件系统路径的抽象.
任何与文件相关的操作, 首先都可以考虑 Files 类中是否已经存在.
2. 方法

2.1. 复制
将输入复制到输出目标, 当输出目标是 Path 时, 可以指定 CopyOption.
CopyOption 一般实现是 StandardCopyOption 枚举类.
copy(InputStream, Path, CopyOption...)copy(Path, OutputStream)copy(Path, Path, CopyOption...)
2.2. 创建
以 create 开头的方法, 可以创建文件夹, 文件, 临时文件夹, 临时文件, 链接.
// 创建文件夹
Files.createDirectory(dirPath);
// 创建文件
Files.createFile(filePath);
// 创建链接
Files.createLink(linkPath);
// 创建符号链接
Files.createSymbolicLink(symbolicPath);
// 在系统的临时文件夹创建临时文件夹
Files.createTempFile(tempFilePrefix, tempFileSufix);
// 在指定文件夹下创建临时文件
Path dirPath = ...
Files.createTempFile(dirPath, tempFilePrefix, tempFileSufix);
// 在系统的临时目录创建临时文件夹
Files.createTempDirectory(tempDirPrefix);
// 在指定文件夹下创建临时文件夹
Files.createTempDirectory(dirPath, tempDirPrefix);
2.3. 删除
以delete开头的方法, 可以删除文件和空文件夹, 但是不能删除有文件的文件夹.
如果要递归地删除文件夹和文件, 需要配合walkFileTree:
// 删除
Files.delete(filePath);
// 递归删除
Files.walkFileTree(
Path.of("./dirToRemove"),
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
2.4. 判断文件是否存在
exists, notExists, 字面理解即可.
2.4. 查找文件
find 递归地查找文件, 可以指定深度和查找条件, 可以通过FileVisitOption选择关闭跟随符号引用.
需要注意流的关闭.
try (Stream<Path> pathStream =
Files.find(
Path.of("./dir"),
Integer.MAX_VALUE,
(path, attrs) -> !Files.isDirectory(path) && path.endsWith(".java"))) {
pathStream.forEach(System.out::println);
}
2.5. 获取, 检查文件的属性或状态
get 和 is 开头的各种方法.
size 可以获取文件的大小.
2.6. 直接将一个字符文件按行读入
lines 方法, 返回 Straem<String>, 比较简单的按行读入的方法.
Stream<String> lines = Files.lines(path);
lines.map(..).forEach(...);
2.7. 返回两个文件第一个不匹配的字节的位置
mismatch.
对于文件 f, mismatch(f,f) 始终返回 -1.
对于文件 f和g, mismatch(f,g) 和 mismatch(g,f) 相同.
2.8. 移动(重命名)文件
move.
示例:
// 重命名
Path source = ...
Files.move(source, source.resolveSibling("newname"));
// 移动并替换已存在文件
Path source = ...
Path newdir = ...
Files.move(source, newdir.resolve(source.getFileName()), REPLACE_EXISTING);
2.9. 新建输入和输出
new 开头的各种方法. 可以创建 BufferedReader, BufferedWriter, InputStream, OutputStream, ByteChannel.
newDirectoryStream用于创建可以遍历文件夹的 DirectoryStream.
示例:
// 遍历特定后缀的文件
List<Path> listSourceFiles(Path dir) throws IOException {
List<Path> result = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
for (Path entry: stream) {
result.add(entry);
}
} catch (DirectoryIteratorException ex) {
// I/O error encountered during the iteration, the cause is an IOException
throw ex.getCause();
}
return result;
}
// 遍历自定义条件的文件
Path dir = ...
try (var stream =
Files.newDirectoryStream(dir, path -> Files.size(path) > 1000L)) {
}
2.10. 探测文件类型
probeContentType. 返回的是MIME字符串, 例如: image/jpg, text/plain 等等, 无法探测到结果时返回null.
依赖平台特定实现.
2.11. 读取文件内容, 属性, 链接
read.
// 读取所有字节
byte[] bytes = Files.readAllBytes(Path.of("./file.txt"));
// 读取所有行
List<String> lines = Files.readAllLines(Path.of("./file.txt"));
// 指定编码
List<String> lines = Files.readAllLines(Path.of("./file.txt"), StandardCharsets.UTF_8);
// 读取文件属性
Path path = ...
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
Map<String,Object> attrMap = Files.readAttributes(path, "*");
// 以字符串读入
String content = Files.readString(path);
// 指定编码
String content = Files.readString(path, StandardCharsets.UTF_8);
// 读取符号链接
Path path = Files.readSymbolicLink(path);
2.12. 设置文件属性, 修改时间, 所有者, 权限等.
set.
Path path = ...
// 设置dos的hidden属性
Files.setAttribute(path, "dos:hidden", true);
// 设置最后修改时间
FileTime now = FileTime.fromMillis(System.currentTimeMillis());
Files.setLastModifiedTime(path, now);
// 设置owner
UserPrincipalLookupService lookupService =
provider(path).getUserPrincipalLookupService();
UserPrincipal joe = lookupService.lookupPrincipalByName("joe");
Files.setOwner(path, joe);
// 设置Posix的文件权限 (static import PosixFilePermission.*)
Files.setPosixFilePermissions(path, EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ));
2.13. 遍历文件树
walk.
walk 方法与 walkFileTree 不同, walk 直接返回 Stream<Path>, 而 walkFileTree 通过回调设置如何处理文件.
walk 方法返回的Stream<Path> 建议配合 try-with-resource 或类似机制确保流关闭, 如果流不关闭对目录的访问也不会关闭.
// 遍历输出所有文件的文件名
try (Stream<Path> paths = Files.walk(Path.of("./dir"))) {
paths.filter(Files::isRegularFile)
.map(Path::getFileName)
.map(Path::toString)
.forEach(System.out::println);
}
// 遍历深度两层
try (Stream<Path> paths = Files.walk(Path.of("./dir"), 2)) {
paths.filter(Files::isRegularFile)
.map(Path::getFileName)
.map(Path::toString)
.forEach(System.out::println);
}
// 递归删除文件和文件夹
Path start = ...
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException e)
throws IOException
{
if (e == null) {
Files.delete(dir);
return FileVisitResult.CONTINUE;
} else {
// directory iteration failed
throw e;
}
}
});
// 递归复制文件夹和文件
final Path source = ...
final Path target = ...
Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
Path targetdir = target.resolve(source.relativize(dir));
try {
Files.copy(dir, targetdir);
} catch (FileAlreadyExistsException e) {
if (!Files.isDirectory(targetdir))
throw e;
}
return CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.copy(file, target.resolve(source.relativize(file)));
return CONTINUE;
}
});
2.14. 写文件
write. 可以通过参数的 OpenOption 设置写文件的选项, 一般实现是StandardOpenOption
Path path = Path.of("./file.txt");
// 写字节
Files.write(path, bytes);
// 写字符串
Files.writeString(path, "hello");
// 写多个字符串
Files.write(path, List.of("hello", " ", "world"));
// Append模式
Files.writeString(path, StandardOpenOption.APPEND);
// 设置编码
Files.writeString(path, "你好!", StandardCharsets.UTF_8);
四. java.util.Comparator类
Comparator 类本身较少直接使用, 一般配合集合进行排序时用到, 或者在某些sorted集合中使用.
Comparator 本身提供了一些非常有用的函数式编程的组合方法, 可以非常方便的创建需要的 Comparator.
静态方法 comparing 可以从一个 Function 创建Comparator.
其本质是通过 Function 从要比较的对象上获取数据, 然后再进行比较.
thenComparing 可以组合Comparator, 实现多重排序.

示例:
// 指定按照长度排序 (基础类型使用对应的comparing和thenComparing方法)
List<String> strings = ....
strings.sort(Comparator.comparingInt(String::length));
// 忽略大小写排序
strings.sort(String.CASE_INSENSITIVE_ORDER);
// 按长度排序并忽略大小写
strings.sort(Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));
// 按长度排序并忽略大小写(另一种方式, 但会有装箱拆箱)
strings.sort(Comparator.comparing(String::length, String.CASE_INSENSITIVE_ORDER));
// 反序排序
strings.sort(Comparator.reverseOrder());
// 忽略大小写反序排序
strings.sort(String.CASE_INSENSITIVE_ORDER.reversed());
// 按年龄再按身高排序 (对于基础类型的比较, 有提供相应的方法, 可以减少泛型带来的装箱拆箱)
List<User> users = ...
users.sort(Comparator.comparingInt(User::age).thenComparingInt(User::height));
// 以email地址排序, null优先
List<User> users = ...
users.sort(Comparator.nullsFirst(Comparator.comparing(User::email)));
值得注意的细节:
- 如果用于比较的字段有可能为空, 必须使用
nullsFirst或nullsLast对null进行处理. 否则可能出现空指针. - 如果用于比较的字段是基础类型, 优先使用对应的
comparing和thenComparing方法, 避免拆箱装箱.
五. 其它
java.util.Base64
提供Base64的编解码.
var encoded = Base64.getEncoder().encodeToString("hello".getBytes());
System.out.println(encoded);
System.out.println(new String(Base64.getDecoder().decode(encoded)));
java.uitl.HexFormat
jdk17及以上可用.
提供格式化16进制字符串的方法.
示例:
var bytes = new byte[]{ (byte) 0xAB, (byte) 0xCD, (byte) 0xEF };
System.out.println(HexFormat.of().formatHex(bytes)); // abcdef
System.out.println(HexFormat.of().withUpperCase().formatHex(bytes)); // ABCDEF
System.out.println(HexFormat.ofDelimiter(":").withUpperCase().formatHex(bytes)); // AB:CD:EF
java.util.StringJoiner
将序列以分隔符拼接起来, 可以设置前后缀.
示例:
StringJoiner sj = new StringJoiner(":", "[", "]");
sj.add("George").add("Sally").add("Fred");
String desiredString = sj.toString();
Collectors.joining 实际上就是使用了该类实现.
对于集合和数组, String类提供了不含前后缀的拼接方法: String.join.

浙公网安备 33010602011771号