switch 支持 String 做参数
switch
语句支持了对字符串String
的判定,这对于开发来说是非常便利的改进。
public class SwitchTest {
public static void switchTest(String arg) {
switch (arg) {
case "Lisa":
System.err.println("我是Lisa!");
break;
case "Mike":
System.err.println("我是Mike!");
break;
default:
System.err.println("我是你的唯一!");
break;
}
}
public static void main(String[] args) {
switchTest("Lisa");//我是Lisa!
switchTest("Mike");//我是Mike!
switchTest("");//我是你的唯一!
}
}
二进制整型字面值
在旧版的 Java 中,字面值只支持十进制、八进制、十六进制3种类型,在Java7中又多了一种二进制,它的前缀是0B
,配合需要位运算的场景特别合适,尤其是跟下划线组合使用:
int i = 0B1010_1100_0010_1100_0000_1111_0001_1011;
数字常量 - 新形式写法
这个特性的引入对我们阅读数字有很好的帮助。可以使用下划线去划分,这样更加便于阅读。这个下划线不是说一定要 3 位数字划分一次,多少位都可以,可以根据个人阅读习惯,编译的时候JVM会将下划线去掉。
long amount = 123000555999666L;
//JDK7特性写法
long amount1 = 123_000_555_999_666L;
注意:只能将下划线置于数字之间。
以下地方不能放置下划线:
- 数字的开头或结尾;浮点数中靠近小数点的位置;
- F 或 L 后缀之前期望放置一串数字的地方。
try-with-resources
在Java编程过程中,如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,需要手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。
传统做法:
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(new File("test"));
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
JDK1.7之后:
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
try-with-resource
并不是 JVM 虚拟机的新增功能,只是 JDK 实现了一个语法糖,实际上就是自动调用资源的close()
函数,当你将上面代码反编译后会发现,其实对JVM虚拟机而言,它看到的依然是之前的写法。
那什么是try-with-resource
呢?简而言之,当一个外部资源的句柄对象(比如FileInputStream
对象)实现了AutoCloseable 或者 Closeable接口,将外部资源的句柄对象的创建放在try
关键字后面的括号中,当这个try-catch
代码块执行完毕后,Java会确保外部资源的close
方法被调用。代码是不是瞬间简洁许多!
注意:资源关闭会按声明时的相反顺序被执行!
在传统做法中,调用close
方法时若出现异常可以直接在 finally
块中捕获并作出相应处理,但是try-with-resource
做了简化,在自动调用相应的close
方法时发生异常后如何处理?
- 如果对外部资源的处理未发生异常,但对外部资源的关闭发生了异常的时候,关闭异常将被抛出。
- 如果对外部资源的处理和对外部资源的关闭均发生了异常,关闭异常将被抑制,处理异常将被抛出。但关闭异常没有丢失,而是存放在处理异常的被抑制的异常列表中。你可以通过被
try
代码块抛出的异常的Throwable.getSuppressed
方法找回被压抑的异常。
public class Connection implements AutoCloseable {
public void sendData() throws Exception {
throw new Exception("send data");
}
public void close() throws Exception {
throw new Exception("close");
}
}
// 测试
@Test
public void test() {
try (Connection conn = new Connection()) {
conn.sendData();
} catch (Exception e) {
e.printStackTrace();
}
}
// 控制台异常输出结果
java.lang.Exception: send data
at Connection.sendData(Connection.java:4)
at TryWithResource.test(TryWithResource.java:8)
.......
Suppressed: java.lang.Exception: close
at Connection.close(Connection.java:8)
at TryWithResource.test(TryWithResource.java:9)
... 23 more
上面的代码不难看出,close
方法被执行了。但是由于sendData
方法也抛出了异常,而close
方法也抛出了异常。信息中多了一个Suppressed的提示,附带上close
的异常。这个异常其实由两个异常组成, 执行close
方法的异常是被Suppressed【抑制】
的异常。
// 获取被抑制异常
Throwable[] th = e.getSuppressed();
System.out.println(th[0].getMessage());
th[0].printStackTrace();
// 控制台异常输出结果
close
java.lang.Exception: close
at Connection.close(Connection.java:8)
at TryWithResource.test(TryWithResource.java:9)
..........
改进的异常处理
- Multi catch:开发者现在能够在一个catch代码块中捕获多个异常类型。
- Final Rethrow:它可以让开发者捕获一个异常类型及其子类型,并且无需向方法声明中增加抛出子句,就能重新将其抛出。
Multi catch
Multi catch 开发者现在能够在一个 catch 代码块中捕获多个异常类型。原来我们捕获多个异常的时候常用的写法是:
} catch (FirstException ex) {
logger.error(ex);
throw ex;
} catch (SecondException ex) {
logger.error(ex);
throw ex;
}
这种写法除了冗长外没有什么优点。可能我们想到的一个解决办法是找出这两个异常类型的共同父类型,只对其进行捕获。但是这种方法通常会捕获一些你并不需要的异常。Muti-catch就解决了此类问题,借助于这个新增的功能,我们可以使用以下代码:
} catch (FirstException | SecondException ex) {
logger.error(ex);
throw ex;
}
Final Rethrow
Final Rethrow 【不常用可略过】可以让开发者捕获一个异常类型及其子类型,并且无需向方法声明中增加抛出子句,就能重新将其抛出。
假如开发过程中希望在捕获所有异常后,进行必要的几个操作后,然后再将其抛出。就代码编写而言并不是一件难事,但是我们必须在方法声明中增加一个抛出子句,来管理自己代码发出的新异常。
class SubException1 extends Exception {}
class SubException2 extends Exception {}
public void testThrow() throws Exception {
try{
throw new SubException1();
}catch(Exception e){
try{
throw e; //1
}catch(SubException2 e2){ //JDK6 可编译通过,JDK7 下无法通过编译
}
}
}
在 JDK7 下报错为:
Unreachable catch block for App.SubException2. This exception is never thrown from the try statement body
JDK7 编译器在 1 处能推断出抛出的异常类型是 SubException1, 底下的 catch(SubException2 e2) 就别白费心思啦。
public void doSomething() /*throws Exception*/{
try {
doSomethingElse();
} catch (Exception e) {
//JDK6 下报 Unhandled exception type Exception 错误,必须声明抛出 Exception
throw e;
}
}
public void doSomethingElse(){
throw new RuntimeException();
}
在 JDK6 下 doSomething()
方法必须声明 throws Exception
抛出 Exception
类型的异常才成。而 JDK7 编译器在 doSomethingElse()
推断出 catch(Exception e)
就是一个 RuntimeException
非检测异常类型,所以 doSomething()
方法中可以省去 throws Exception
。
下面的代码对 JDK7 来说可以通过,糊弄不了它的,也无须为 doSomething()
方法声明 throws Exception
:
public void doSomething() {
try {
doSomethingElse();
} catch (Exception e) {
throw e;
}
}
public void doSomethingElse(){
doAnotherThing();
}
public void doAnotherThing(){
throw new RuntimeException();
}
但 JDK7 看到如下的代码同样会傻眼:
public void doSomething() /*JDK7 下也必须加上 throws Exception*/{
try {
doSomethingElse();
} catch (Exception e) {
throw e;
}
}
public void doSomethingElse() throws Exception{
throw new RuntimeException();
}
在 JDK7 下为 doSomething()
加上 throws Exception
声明,只看到 doSomethingElse()
方法的 throws Exception
声明就认定它抛出的是 Exception
类型,而跳过的 throw new RuntimeException()
内容的具体推断。
因此当我们在为 catch(Exception e) { throw e; }
后要不要为所在方法加上 throws
声明时,可以查查 try
代码块中调用的方法有没有声明抛出需检测的异常。
当然,有现代化的 IDE 根本不担心这个,按错误提示来办事,通常只需一个简单的快捷键就帮你做好了,但任何时候理解万岁。
创建泛型实例时自动类型推断
在以前的版本中使用泛型类型,需要在声明并赋值的时候,两侧都加上泛型类型。例如:
Map<String, String> myMap = new HashMap<String, String>();
不过在Java SE 7中,这种方式得以改进,现在你可以使用如下语句进行声明并赋值:
Map<String, String> myMap = new HashMap<>(); //注意后面的"<>"
在这条语句中,编译器会根据变量声明时的泛型类型自动推断出实例化HashMap
时的泛型类型。再次提醒一定要注意new HashMap
后面的<>
,只有加上这个<>
才表示是自动类型推断,否则就是非泛型类型的HashMap
,并且在使用编译器编译源代码时会给出一个警告提示。
注意:Java SE 7在创建泛型实例时的类型推断是有限制的,你只能在联系上下文可以明确确定参数化类型的时候使用泛型推断。
下面的例子无法正确编译:
List<String> list = new ArrayList<>();
list.add("A");
// 由于addAll期望获得Collection<? extends String>类型的参数,因此下面的语句无法通过
list.addAll(new ArrayList<>());
与上面的例子相比,下面的这个例子可以通过编译:
List<String> list = new ArrayList<>();
list.add("A");
List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);
改进使用带泛型可变参数的方法时的编译器警告和错误提示
【暂时还不理解,所以上面的链接是外链接】
参考资料: