前几天在微信订阅号"java技术"看了一下关于jdk7新特性方面的介绍,在这里总结分享一下.
1.自动资源管理(TWR)--try with resource
Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets等。这个新的语言特性允许try语句本身申请更多的资源,这些资源作用于try代码块,并自动关闭。
package yang.zhiran.TWR;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class test {
public static void main(String[] args) {
/*
* Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。
* TWR特性允许try语句本身申请更多的资源,这些资源作用于try代码块,并自动关闭。
*/
String path="C:\\test.txt";
try {
String contentOld=oldMethod(path);
System.out.println("old:"+contentOld);
String contentNew=newMethod(path);
System.out.println("new:"+contentNew);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String oldMethod(String path) throws IOException{
BufferedReader br=null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (Exception e) {
e.printStackTrace();
return "";
}finally{
br.close();
}
}
public static String newMethod(String path){
BufferedReader br=null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
package yang.zhiran.diamond;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
public class test {
public static void main(String[] args) {
/*
* 类型推断:diamond语法
*/
Map<String,List<String>> map=new HashMap<>();
List<String> list=new ArrayList<>();
list.add("xxx");
list.add("yyy");
map.put("yzr", list);
System.out.println(map);
}
}
package yang.zhiran.intvar;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
//在Java 7中可以使用下划线分隔长int以及long
int number=100_00_0;//等价于100000
System.out.println(number);
}
}
1.7之前switch只支持int和enum两种类型,现在也可以支持string
package yang.zhiran.switch7;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
switch ("YZR") {
case "YZR":
System.err.println("YZR");
break;
default:
System.err.println("default");
break;
}
}
}
在Java7之前的异常处理语法中,一个catch子句只能捕获一类异常。在要处理的异常种类很多时这种限制会很麻烦。每一种异常都需要添加一个catch子句,而且这些catch子句中的处理逻辑可能都是相同的,从而会造成代码重复。虽然可以在catch子句中通过这些异常的基类来捕获所有的异常,比如使用Exception作为捕获的类型,但是这要求对这些不同的异常所做的处理是相同的。另外也可能捕获到某些不应该被捕获的非受检查异常。而在某些情况下,代码重复是不可避免的。比如某个方法可能抛出4种不同的异常,其中有2种异常使用相同的处理方式,另外2种异常的处理方式也相同,但是不同于前面的2种异常。这势必会在catch子句中包含重复的代码。
对于这种情况,Java7改进了catch子句的语法,允许在其中指定多种异常,每个异常类型之间使用“|”来分隔。如例:
package yang.zhiran.catch7;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
Object obj=null;
try{
if(obj.toString().equals("") && 19/0 >0){
}
}catch (NullPointerException | ArithmeticException e) {
System.out.println("捕获 除数为0 和 空指针 异常");
//e.printStackTrace();
}
}
}
6.Path
在新的文件I/O中,Path是必须掌握的关键类之一,用Path对象来实现对文件或者文件夹的操作。Path通常代表文件系统中的位置,比如C:\workspace\java7developer(Windows文件系统中的目录)或/usr/bin/zip(*nix文件系统中zip程序的位置)。
注意:Path是一个抽象构造,你所创建和处理的Path可以不马上绑定到对应的物理位置上。
java.nio.file.Paths 包含了用于创建Path对象的静态方法
java.nio.file.Path 包含了大量用于操纵文件路径的方法
java.nio.file.FileSystems 用于访问文件系统的类
java.nio.file.FileSystem 代表了一种文件系统,例如Unix下的根目录为 / ,而Windows下则为C盘
一旦获得某个路径对应的Path对象,我们就能方便的获取文件系统中的各种信息,已及操作该path。
Path path = FileSystems.getDefault().getPath("C:\\test\\test.txt");
System.out.printf("文件名称: %s\n", path.getFileName());
System.out.printf("根目录: %s\n", path.getRoot());
for (int index = 0; index < path.getNameCount(); index++) {
System.out.printf("getName(%d): %s\n", index, path.getName(index));
}
System.out.printf("路径截取: %s\n", path.subpath(0, 2));
System.out.printf("路径截取: %s\n", path.subpath(0, 1));
System.out.printf("父目录: %s\n", path.getParent());
System.out.println("是否绝对路径:"+path.isAbsolute());
path = Paths.get("F", "test.txt");
System.out.printf("绝对路径地址: %s\n", path.toAbsolutePath());
System.out.println("是否绝对路径:"+path.isAbsolute());
//获取文件信息
path = Paths.get("C:\\test\\test.txt");
BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("Creation Time: " + attributes.creationTime());
System.out.println("Last Accessed Time: " + attributes.lastAccessTime());
System.out.println("Last Modified Time: " + attributes.lastModifiedTime());
System.out.println("File Key: " + attributes.fileKey());
System.out.println("isDirectory: " + attributes.isDirectory());
System.out.println("Other Type of File: " + attributes.isOther());
System.out.println("Regular File: " + attributes.isRegularFile());
System.out.println("Symbolic File: " + attributes.isSymbolicLink());
System.out.println("Size: " + attributes.size());
操作文件
通用,Java7也提供了简单的api可以让我们非常简单的复制、移动和删除文件以及路径。
//操作文件
Path directoryPath = Paths.get("C:/home");
//创建目录
Files.createDirectory(directoryPath);//不能存在多个为存在的文件夹,只能只有一个
System.out.println("Directory created successfully!");
Path filePath = Paths.get("C:/home/test.txt");
//创建文件
Files.createFile(filePath);
System.out.println("File created successfully!");
Path newFile = Paths.get("C:/home/newFile.txt");
Files.createFile(newFile);
System.out.println("File created successfully!");
Path copiedFile = Paths.get("C:/home/copiedFile.txt");
Files.copy(newFile, copiedFile, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File copied successfully!");
Files.createDirectory(Paths.get("C:/home/projects"));
Path sourceFile = Paths.get("C:/home/projects/note_bak1.txt");
boolean result = Files.deleteIfExists(sourceFile);
if (result) {
System.out.println("File deleted successfully!");
}
else{
Files.createFile(sourceFile);
}
sourceFile = Paths.get("C:/home/projects/note_bak1.txt");
Files.delete(sourceFile);
System.out.println("File deleted successfully!");
在调用createFile方法时,如果想要创建的文件已经存在,FileAlreadyExistsException会被抛出。createFile和createDirectory这个两个方法都是原子性的,即要不整个操作都能成功或者整个操作都失败。
复制一个文件同样非常简单,Files的copy方法就可以实现。在复制文件时,我们可以对复制操作加一个参数来指明具体如何进行复制。
java.lang.enum.StandardCopyOption这个枚举可以用作参数传递给copy方法。
ATOMIC_MOVE 原子性的复制
COPY_ATTRIBUTES 将源文件的文件属性信息复制到目标文件中
REPLACE_EXISTING 替换已存在的文件
删除文件可以使用Files的delete和deleteIfExists这两个方法。顾名思义,当文件不存在时deleteIfExists的删除结果为false。
目录遍历
//目录遍历
//得到该path下所有.txt文件的目录流接口
Path dir= Paths.get("C:\\");
DirectoryStream<Path> txtStream=Files.newDirectoryStream(dir,"*.txt");
System.out.println("===txt===");
for(Path item : txtStream){
System.out.println(item.toAbsolutePath()+":"+item.getFileName());
}
进行目录及目录树遍历(监听遍历)
package yang.zhiran.path;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class VisitorFile extends SimpleFileVisitor<Path>{
//访问目录及子目录中的每个path的方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file.toAbsolutePath()+":"+file.getFileName());
//表示继续遍历
return FileVisitResult.CONTINUE;
}
//访问某个path失败时调用的方法,默认抛出异常
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.out.println(file+";"+exc.getClass());
//表示继续遍历
return FileVisitResult.CONTINUE;
}
}
//进行目录及目录树遍历,打印出所有遍历到的文件
System.out.println("===dir tree===");
Files.walkFileTree(dir,new VisitorFile());
7.快速读写
Java 7可以直接用带缓冲区的读取器和写入器或输入输出流(为了和以前的Java I/O代码兼容)打开文件。
package yang.zhiran.newBuffered;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class test {
/*
* 文件读写
*/
public static void main(String[] args) throws IOException {
/*
Path path= Paths.get("C:/test.txt");
BufferedWriter writer = Files.newBufferedWriter(path,
Charset.forName("utf-8"));//StandardOpenOption.APPEND
writer.write("hello,java7");
writer.newLine();
writer.write("test");
System.out.println("ok");
writer.close();
List<String> lines= Files.readAllLines(path,Charset.forName("utf-8"));
System.out.println(lines);
*/
Path path = Paths.get("C:/test.txt");
try ( // 如果文件存在则直接打开,否则创建文件
BufferedWriter writer = Files.newBufferedWriter(path,
Charset.forName("utf-8"));
// 可以指定打开文件的模式,这里表示追加文件
// BufferedWriter writer=Files.newBufferedWriter(path,
// Charset.forName("utf-8"), StandardOpenOption.APPEND);
) {
writer.write("hello,java7");
writer.newLine();
writer.write("test");
System.out.println("ok");
}
List<String> lines = Files
.readAllLines(path, Charset.forName("utf-8"));
System.out.println(lines);
}
}
8.监听文件
要实现监听文件的变更然后马上做出响应,在Java7之前,除了不断的循环似乎没什么好办法,而且实现也很麻烦。Java7推出了一个WatchService来解决这个问题。
package yang.zhiran.watchService;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
final Path path = Paths.get("C:\\home");
System.out.println("begin");
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
//给path路径加上文件观察服务
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
// start an infinite loop
while (true) {
final WatchKey key = watchService.take();
for (WatchEvent<?> watchEvent : key.pollEvents()) {
final WatchEvent.Kind<?> kind = watchEvent.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
//创建事件
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("create");
}
//修改事件
if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("modify");
}
//删除事件
if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("delete");
}
// get the filename for the event
final WatchEvent<Path> watchEventPath = (WatchEvent<Path>) watchEvent;
final Path filename = watchEventPath.context();
// print it out
System.out.println(kind + " -> " + filename);
}
// reset the keyf
boolean valid = key.reset();
// exit loop if the key is not valid (if the directory was
// deleted, for
if (!valid) {
break;
}
}
} catch (IOException | InterruptedException ex) {
System.err.println(ex);
}
}
}
9 指定position读写文件
打开文件,定位到指定的位置,然后读或写文件内容。
在Javs7中,SeekableByteChannel接口提供了这个功能。
package yang.zhiran.fileChannel;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class test {
/**
* 定位position读写文件内容
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileChannel fileChannel=FileChannel.open(Paths.get("C:\\test.txt"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
fileChannel.write(ByteBuffer.wrap("hello,java".getBytes()));
fileChannel.position(0);//定位到文件开头
//fileChannel.write(ByteBuffer.wrap("seek".getBytes()));//替换
//fileChannel.write(ByteBuffer.wrap("seek".getBytes()),1);
fileChannel.write(ByteBuffer.wrap("seek".getBytes(),3,1));
//fileChannel.position(fileChannel.size());//定位到文件末尾
//fileChannel.write(ByteBuffer.wrap("end".getBytes()));
//将通道中的指定位置开始的内容传输到另一个通道中,这里传输到控制台
fileChannel.transferTo(0,fileChannel.size(), Channels.newChannel(System.out));
}
}
10.异步IO
Java7直接提供了用于异步操作io的各种通道。
新的异步功能的关键点,它们是Channel类的一些子集。包括:
AsynchronousFileChannel:针对文件;
AsynchronousSocketChannel :针对客户端的socket;
AsynchronousServerSocketChannel:针对服务器端的异步socket,用来接收到来的连接。
一些需要访问较大,耗时的操作,或是其它的类似实例,可以考虑应用此功能。
package yang.zhiran.asynchronous;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Future;
public class test {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//定义要打开的文件对应的path
Path path= Paths.get("C:\\test.txt");
//打开一个可以异步读写文件的通道
AsynchronousFileChannel channel=AsynchronousFileChannel.open(path);
//通道是基于ByteBuff读写的,所以需要声明一个bytebuff来存储要读写的数据
ByteBuffer bf=ByteBuffer.allocate(1024);//声明1024个字节的buff
//从0(文件开头)异步读取文件内容到bf,由于是异步操作,不管文件有没有读取完成,这句代码执行后立刻就会执行后面的代码,通过future可以知道结果
Future future=channel.read(bf,0);
System.out.println("文件读取中...");
//如果文件没有读完,可以继续干些别的事情
while(!future.isDone()){
System.out.println("我干别的了,你慢慢读");
}
System.out.println("文件读取完成");
bf.flip();
//打印bytebuff中的内容
System.out.println(Charset.forName("utf-8").decode(bf));
channel.close();
}
}
11.Fork/Join框架
Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架,使得应用能充分利用线程进行并行计算,并减少了线程间的竞争。
所谓Fork就是把一个大任务切分为若干子任务并行的执行,Join则是合并这些子任务的执行结果,最后得到这个大任务的结果。
比如计算1+2+。。+1000,可以分割成10个子任务,每个子任务分别对100个数进行求和,最终汇总这10个子任务的结果。
package yang.zhiran.forkJoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class test extends RecursiveTask<Integer>{
final int step=100;//表示每个任务最多只计算100个数字的和,比如从1加到200就分层1+100和101+200两个任务
private int from;//从哪里开始计算
private int to;//到哪里结束计算
public test(int from,int to){
this.from=from;
this.to=to;
}
//重写此方法,用于计算任务结果
@Override
protected Integer compute() {
if((to-from)<step){
//小于100个数,直接计算
return sum(from,to);
}
//拆分任务,一分为二
int middle=(from+to)/2;
test task1=new test(from,middle);
test task2=new test(middle+1,to);
//执行子任务(异步)
task1.fork();
task2.fork();
//等待子任务结果
int t1=task1.join();
int t2=task2.join();
return t1+t2;
}
private int sum(int from,int to){
System.out.println("from:"+from+",to:"+to);
int sum=0;
for(int i=from;i<=to;i++){
sum+=i;
}
return sum;
}
public static void main(String[] arg) throws Exception {
//fork/join需要ForkJoinPoll执行
ForkJoinPool pool=new ForkJoinPool();
System.out.println(pool.submit(new test(1,300)).get());
}
}
Fork/Join使用两个类来完成以上两件事情:
ForkJoinTask
我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制。
通常情况下我们不需要直接继承ForkJoinTask类,而只需要继承它的子类,Fork/Join框架提供了以下两个子类:
RecursiveAction:用于没有返回结果的任务。
RecursiveTask :用于有返回结果的任务。
ForkJoinPool
ForkJoinTask需要通过ForkJoinPool来执行,它是一个特殊的ExecutorService.任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。
package yang.zhiran.methodHandle;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandlerTest {
public static void main(String[] arg) throws Throwable{
//获取方法f1的methodType对象,表示此方法的返回值类型和参数类型
MethodType f1=MethodType.methodType(String.class,int.class);
MethodType f2=MethodType.methodType(void.class);//无参数,返回值是void的方法
MethodType f3=MethodType.methodType(void.class);
//通过MethodHandles.lookup()可以在一个类上根据方法名称和方法的methodType获取方法句柄
//查找普通方法
MethodHandle mf1=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f1",f1);
MethodHandle mf2=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f2",f2);
//查找静态方法
MethodHandle mf3=MethodHandles.lookup().findStatic(MethodHandlerTest.class,"f3",f3);
//通过方法句柄调用方法
MethodHandlerTest methodHandler=new MethodHandlerTest();
mf1.invoke(methodHandler,123);
mf2.invoke(methodHandler);
//使用invokeExact调用时,参数类型和返回值类型必须与方法签名的一致
String v=(String)mf1.invokeExact(methodHandler,1234);
//调用静态方法
mf3.invoke();
}
public String f1(int a){
System.out.println("f1"+a);
return a+"";
}
public void f2(){
System.out.println("f2");
}
public static void f3(){
System.out.println("f3");
}
}