Java的异常
- Java中,异常是一种
class
,因此它本身带有类型信息 Error
表示严重的错误,程序对此一般无能为力,不需要捕获的异常OutOfMemoryError
:内存耗尽NoClassDefFoundError
:无法加载某个ClassStackOverflowError
:栈溢出
Exception
表示运行时的错误,可以被捕获并处理- 例子:
NumberFormatException
:数值类型的格式错误FileNotFoundException
:未找到文件SocketException
:读取网络失败NullPointerException
:对某个null的对象调用方法或字段IndexOutOfBoundsException
:数组索引越界
RuntimeException
以及它的子类,是否被捕获并处理视情况而定- 非
RuntimeException
(包括IOException
、ReflectiveOperationException
等等),必须被捕获并处理
- 例子:
- 捕获异常使用
try...catch
语句,把可能发生异常的语句放在try { … }中,然后使用catch捕获对应的Exception
及其子类 - 所有异常都可以调用
printStackTrace()
(e.printStackTrace();
)方法打印异常栈
捕获异常 try … catch … finally
- 可以使用多个
catch
语句,分别捕获对应的Exception
及其子类,但是只有一个catch
语句被执行(自上到下匹配,一旦匹配成功则执行,不再继续往下匹配) - 因此,
catch
的顺序非常重要:子类必须写在前面,否则捕获到父类就不会再匹配到子类了 finally
语句块保证有无错误都会执行public static void main(String[] args) {
try {
//正常语句体
} catch (UnsupportedEncodingException e) { //异常类1,异常处理对象1
System.out.println("Bad encoding"); //处理异常类1,异常处理对象1的语句
} catch (IOException | NumberFormatException e) { //用 | 合并相同处理的异常
System.out.println("Bad input");
//或者使用 e.printStackTrace(); //所有异常都可以调用printStackTrace()方法打印异常栈
} finally {
System.out.println("END"); //最后执行的语句,有无错误都会执行
}
}
用
|
合并相同处理的异常
抛出异常 throw
抛出异常分两步
- 创建某个
Exception
的实例 - 用
throw
语句抛出void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
- 创建某个
子过程使用
throw
抛出异常,调用该子过程的上层调用方法使用try ... catch ... finally
捕获异常并处理- 调用
printStackTrace()
(e.printStackTrace();
)可以打印异常的传播栈
自定义异常
先自定义一个
BaseException
作为“根异常”,BaseException
通常建议从RuntimeException
派生自定义的
BaseException
应该提供多个构造方法public class BaseException extends RuntimeException {
public BaseException() {
super();
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(String message) {
super(message);
}
public BaseException(Throwable cause) {
super(cause);
}
}
其他业务类型的异常就可以从
BaseException
派生 ```java public class UserNotFoundException extends BaseException { }
public class LoginFailedException extends BaseException { }
…
<a name="9ePJS"></a>
# NPE 空指针异常 NullPointerException
- `NullPointerException`是`RuntimeException`异常的子类
- `NullPointerException`是代码逻辑错误,应尽早发现并修改代码,而不能用`catch`捕获来隐藏这种编码错误
- `NullPointerException`的避免:
- 使用空字符串`""`而不是默认的`null`
```java
public class Person {
private String name = ""; //而不是写成 private String name = null;
}
返回空字符串
""
、空数组而不是null
public String[] readLinesFromFile(String file) { if (getFileSize(file) == 0) { // 返回空数组而不是null: return new String[0]; } ... }
如果调用方一定要根据
null
判断,比如返回null
表示文件不存在,那么考虑返回Optional<T>
,这样调用方必须通过Optional.isPresent()
判断是否有结果public Optional<String> readFromFile(String file) { if (!fileExist(file)) { return Optional.empty(); } ... }
- 给JVM添加一个
-XX:+ShowCodeDetailsInExceptionMessages
参数可以输出异常的定位java -XX:+ShowCodeDetailsInExceptionMessages Main.java
断言 assert
public class Main {
public static void main(String[] args) {
int x = -1;
assert x > 0; //断言失败时抛出AssertionError,并退出程序
System.out.println(x);
}
}
- 断言失败时会抛出
AssertionError
,导致程序结束退出。因此,断言不能用于可恢复的程序错误,只应该用于开发和测试阶段 - 对于可恢复的程序错误,不应该使用断言,而应抛出异常并在上层捕获
- JVM默认关闭断言指令,使用
-enableassertions
或-ea
参数启用断言java -ea Main.java
日志
- 日志:在调试程序时,不再需要反复增删
System.out.println()
-
JDK Logging
JDK
Logging
包含7个级别,从高到低:SEVERE
WARNING
INFO
CONFIG
FINE
FINER
FINEST
Logging
默认级别是INFO
,因此INFO
级别以下的日志,不会被打印出来 ```java import java.io.UnsupportedEncodingException; import java.util.logging.Logger;
public class Main { public static void main(String[] args) { Logger logger = Logger.getLogger(Main.class.getName()); logger.info(“Start process…”); try { “”.getBytes(“invalidCharsetName”); } catch (UnsupportedEncodingException e) { // TODO: 使用logger.severe()打印异常 logger.severe(e.toString()); } logger.info(“Process end.”); } }
输出:
Oct 26, 2020 11:43:41 AM Main main INFO: Start process… Oct 26, 2020 11:43:41 AM Main main SEVERE: java.io.UnsupportedEncodingException: invalidCharsetName Oct 26, 2020 11:43:41 AM Main main INFO: Process end. ```