Java SE 7 新增特性
作者:Grey
二进制表示
在
Java SE 7
中,基本类型(byte
、short
、int
和long
)也可以用二进制表示。只需要在数字上添加前缀0b
或0B
。
public class BinaryTest {
public static void main(String[] args) {
// 0b是二进制的开头
// 所以num表示7
int num = 0b111;
System.out.println(num);
// 二进制0110转换成十进制是6
num = 0B0110;
System.out.println(num);
}
}
下划线
在
Java SE 7
及以后的版本中,任何数量的下划线字符_
都可以出现在数字开头和结尾之间的任何地方。这一特性使你能够在数字中分隔数字组,这可以提高代码的可读性。例如,如果你的代码中包含有许多数字,你可以使用下划线字符将数字分成三组,类似于你使用逗号或空格等标点符号作为分隔符的方式。
public class UnderscoresTest {
public static void main(String[] args) {
long creditCardNumber = 1234_5678_9012_3456L;
System.out.println(creditCardNumber);
long socialSecurityNumber = 999_99_9999L;
System.out.println(socialSecurityNumber);
float pi = 3.14_15F;
System.out.println(pi);
long hexBytes = 0xFF_EC_DE_5E;
System.out.println(hexBytes);
long hexWords = 0xCAFE_BABE;
System.out.println(hexWords);
long maxLong = 0x7fff_ffff_ffff_ffffL;
System.out.println(maxLong);
byte nybbles = 0b0010_0101;
System.out.println(nybbles);
long bytes = 0b11010010_01101001_10010100_10010010;
System.out.println(bytes);
}
}
switch中可以使用字符串
public class SwitchTest {
public static void main(String[] args) {
System.out.println("Monday");
}
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
String typeOfDay;
switch (dayOfWeekArg) {
case "Monday":
typeOfDay = "Start of work week";
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
typeOfDay = "Midweek";
break;
case "Friday":
typeOfDay = "End of work week";
break;
case "Saturday":
case "Sunday":
typeOfDay = "Weekend";
break;
default:
throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
}
return typeOfDay;
}
}
类型推断
例如,jdk1.7
之前:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
在jdk1.7
中,可以使用<>
,省去了具体类型
Map<String, List<String>> myMap = new HashMap<>();
注意,<>
是必需的,如果没有<>
,例如:
Map<String, List<String>> myMap = new HashMap();
会报一个unchecked conversion warning
的警告信息。
Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods
Try-With-Resource
jdk1.7
之前的写法
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
jdk1.7
之后的写法
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
此外,还可以try
后面的括号里面还可以接多个表达式,例如:
public static void writeToFileZipFileContents(String zipFileName, String outputFileName)
throws java.io.IOException {
java.nio.charset.Charset charset = java.nio.charset.Charset.forName("US-ASCII");
java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName);
// Open zip file and create output file with try-with-resources statement
try (
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
// Enumerate each entry
for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements(); ) {
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName = ((java.util.zip.ZipEntry) entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
}
注意,这里的类需要实现java.lang.AutoCloseable
或者java.io.Closeable
接口。
文件操作相关API增强
Java 7 提供了 Path 接口用来表示路径的抽象,然后提供了一系列对于路径的操作方法
private static void pathTest1(final String pathString) {
Path path = Paths.get(pathString);
System.out.println("完整路径:" + path.toString());
Path pathParent = path.getParent();
System.out.println("父级路径:" + pathParent.toString());
Path pathRoot = path.getRoot();
System.out.println("根目录:" + pathRoot.toString());
int pathNameCount = path.getNameCount();
System.out.println("目录深度:" + pathNameCount);
Path pathIndex3 = path.getName(2);
System.out.println("第三级目录:" + pathIndex3);
Path subPath = path.subpath(1, 3);
System.out.println("第1级目录到第三级目录(包左不包右):" + subPath.toString());
// resolveSibling 从当前目录父目录开始拼接目录
// FIXME 使用你本地存在的路径
Path pathResolveSibling = path.resolveSibling("src\\main");
System.out.println("父目录开始拼接参数:" + pathResolveSibling.toString());
// resolve 把当前路径当作父路径,参数作为子目录或者文件
// FIXME 使用你本地存在的路径
Path pathResolve = Paths.get("\\java\\snippet").resolve("SimpleSortMethod.java");
System.out.println("当前目录拼接后的目录:" + pathResolve.toString());
// 参数路径相对于主体路径的相对路径
// FIXME 使用你本地存在的路径
Path path1 = Paths.get("\\java\\snippet");
Path path2 = Paths.get("\\java\\snippet\\SimpleSortMethod.java");
Path path3 = path1.relativize(path2);
System.out.println("相对路径:" + path3.toString());
}
private static void pathOp() throws IOException {
// 如果文件不存在,则创建一个文件
Path path = Paths.get("test.txt");
Path pathBackup = Paths.get("test_bak.txt");
Path pathLink = Paths.get("test.txt.link");
Path pathDir = Paths.get("dir");
// 已存在则删除
Files.deleteIfExists(path);
Files.deleteIfExists(pathBackup);
Files.deleteIfExists(pathLink);
Files.deleteIfExists(pathDir);
// 创建文件写入内容
Path file = Files.createFile(path);
Files.write(path, "ABC".getBytes());
Files.write(path, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
Files.write(path, "EFG".getBytes(), StandardOpenOption.APPEND);
System.out.println("创建文件:" + file.toString());
// 创建文件链接
pathLink = Files.createLink(pathLink, path);
System.out.println("创建文件:" + pathLink.toString());
// 创建目录
Path directory = Files.createDirectory(pathDir);
System.out.println("创建目录:" + directory.toString());
// 文件复制
Files.copy(path, pathBackup);
System.out.println("复制文件: " + path + " --> " + pathBackup);
// 读取文件
List<String> lines = Files.readAllLines(pathBackup, Charset.defaultCharset());
for (String line : lines) {
System.out.println("文件读取:" + line);
}
}
private static void fileInfo() throws IOException {
// FIXME 使用你本地存在的文件
Path path = Paths.get("D:\\git\\algorithm\\README.md");
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes();
FileTime creationTime = basicFileAttributes.creationTime();
FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime();
FileTime lastAccessTime = basicFileAttributes.lastAccessTime();
System.out.println("创建时间:" + creationTime);
System.out.println("上次修改时间:" + lastModifiedTime);
System.out.println("上次访问时间:" + lastAccessTime);
boolean directory = basicFileAttributes.isDirectory();
boolean regularFile = basicFileAttributes.isRegularFile();
boolean symbolicLink = basicFileAttributes.isSymbolicLink();
System.out.println("是否目录:" + directory);
System.out.println("是否普通文件:" + regularFile);
System.out.println("是否符号链接:" + symbolicLink);
long size = basicFileAttributes.size();
System.out.println("文件大小:" + size);
// Linux或者Mac使用下面PosixFileAttributeView
// PosixFileAttributeView linuxFileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
// UserPrincipal owner = linuxFileAttributeView.getOwner();
// System.out.println("文件归属用户:" + owner.getName());
// Windows使用DosFileAttributeView
DosFileAttributeView windowsFileAttributeView = Files.getFileAttributeView(path, DosFileAttributeView.class);
DosFileAttributes dosFileAttributes = windowsFileAttributeView.readAttributes();
}
在Java 7
之前遍历文件目录和文件,可以使用File
类的listFiles
方法。
public static void listFile() {
// FIXME 改成你自己本地的可访问路径
String pathString = "D:\\git\\algorithm\\src\\main\\java\\snippet";
File file = new File(pathString);
File[] listFiles = file.listFiles();
for (File tempFile : listFiles) {
System.out.println("file list: " + tempFile.getAbsolutePath());
}
}
Java 7
引入了DirectoryStream
文件列表流。它可以进行渐进式的文件遍历,每次读取一定数量,降低遍历时的性能开销,但是DirectoryStream
遍历时只会遍历它的直接目录和文件,不会递归的遍历子目录。
public static void listFile2() throws IOException {
// FIXME 改成你自己本地的可访问路径
String pathString = "D:\\git\\algorithm\\src\\main\\java\\snippet";
// Path 直接遍历方式,不会遍历子目录
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(pathString))) {
for (Path pathTemp : directoryStream) {
System.out.println("DirectoryStream: " + pathTemp);
}
}
Java 8
中对Files
类进行了增强,引入了Lambda
表达式,增加了walk
方法
public static void fileTravel() throws Exception {
// FIXME 修改成你自己本地的路径
final String pathString = "D:\\git\\algorithm\\src\\main\\java\\snippet";
// 遍历所有目录和子目录
Stream<Path> pathStream = Files.walk(Paths.get(pathString));
pathStream.forEach(pathTemp -> {
System.out.println("Stream: " + pathTemp.toString());
});
// 遍历所有目录和子目录 - 筛选 java 文件
pathStream = Files.walk(Paths.get(pathString));
pathStream
.filter(pathTemp -> pathTemp.toString().endsWith(".java"))
.forEach(pathTemp -> {
System.out.println("Stream filter java: " + pathTemp.toString());
});
}
也就是可以动态的监测指定目录的文件或者内容的变化,被监视的对象要实现Watchable
接口,然后通过register
方法注册到监视服务WatchService
接口的实现,同时指定要监视的事件类型。
以下是代码(监听D:\\test
目录的各种改动,测试时请换成你本地存在的文件夹)
// 监听文件/文件夹的变化
private static void fileWatch() throws Exception {
WatchService watchService = FileSystems.getDefault().newWatchService();
// FIXME 换成你本地目录
Path path = Paths.get("D:\\test");
path.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey watchKey = watchService.take();
// 获取事件类型
for (WatchEvent<?> pollEvent : watchKey.pollEvents()) {
// 具体的事件上下文信息
Path tempPath = (Path) pollEvent.context();
WatchEvent.Kind<?> kind = pollEvent.kind();
if (kind.name().equals(StandardWatchEventKinds.ENTRY_CREATE.name())) {
System.out.println("创建了一个文件:" + tempPath.toString());
}
if (kind.name().equals(StandardWatchEventKinds.ENTRY_DELETE.name())) {
System.out.println("删除了一个文件:" + tempPath.toString());
}
if (kind.name().equals(StandardWatchEventKinds.ENTRY_MODIFY.name())) {
System.out.println("修改了一个文件:" + tempPath.toString());
}
}
// 事件处理完毕后要进行 reset 才能继续监听事件
watchKey.reset();
// 取消监视
// watchKey.cancel();
}
}
捕获多个异常类型
如果一段代码有多个异常,原先的代码可能会是这样
catch (IOException ex) {
logger.log(ex);
throw ex;
catch (SQLException ex) {
logger.log(ex);
throw ex;
}
在jdk1.7
中,可以简写成这样
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
此外,还可以指定抛出异常类型,而不需要每次都抛出一个大而全的Exception
,例如
public void rethrowException(String exceptionName) throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
} else {
throw new SecondException();
}
} catch (Exception e) {
throw e;
}
}
我们可以指定抛出的异常类型
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
参考资料
Java 7 Files,Paths,Path 文件操作介绍