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,异常处理对象1System.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````javapublic class Person {private String name = ""; //而不是写成 private String name = null;}
返回空字符串
""、空数组而不是nullpublic 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个级别,从高到低:SEVEREWARNINGINFOCONFIGFINEFINERFINEST
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. ```
