目录
一 异常
1.1 异常分类图
异常对象都是派生于Throwable类
Error 类层次结构描述了Java运行时系统的内部错误和资源消耗用尽错误, 对这种错误,程序几乎无能为力。
RuntimeException 类与 Error 类及其子类的所有异常被称为非检查型异常, 除此之外的其他异常被称为检查型异常。
1.2 声明检查型异常
一个方法,必须声明所有可能抛出的检查型异常, 非检查型异常(RuntimeExcep)要避免出现(如: 数组越界, 除0异常, 空指针异常)。
如: 单个异常
public void load(String s) throws IOException{
...
}
多个异常:
public void load(String s) throws FileNotFoundException,IOException{
...
}
备注:
使用 throws 关键字, 在方法在方法首部声明这个方法可能会抛出异常
子类覆盖了父类的方法, 子类的方法的检查型异常不能比父类方法中声明的异常更通用。
不允许子类的 throws 出现超类未列出的异常类
1.3 抛出异常
示例:
public void load(String s) throws IOException {
if (new FileInputStream(s) == null) {
throw new IOException();
}
}
备注:
通过 throw 关键字, 可以将一个异常对象抛出。
1.4 自定义异常类:
实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我 们实际情况的异常。
示例:
public class Test {
private static String userName = "admin";
private static String password = "123456";
public static void main(String[] args) {
login("admin", "123456");
}
public static void login(String userName, String password) {
if (!Test.userName.equals(userName)) {
// TODO 处理用户名错误
}
if (!Test.password.equals(password)) {
// TODO 处理密码错误
}
System.out.println("登陆成功");
}
}
根据以上情况, 我们可以创建两个自定义异常:
class UserError extends Exception {
public UserError(String message) {
super("用户名错误");
}
}
class PasswordError extends Exception {
public PasswordError(String message) {
super("密码错误");
}
}
应用自定义异常:
public static void login(String userName, String password) throws UserError, PasswordError{
if (!Test.userName.equals(userName)) {
throw new UserError("用户名错误");
}
if (!Test.password.equals(password)) {
throw new PasswordError("密码错误");
}
System.out.println("登陆成功");
}
备注:
自定义异常通常会继承自 Exception 或其子类继承自 Exception 的异常默认是受查异常
1.5 捕获异常
一旦出现异常, 而没有在任何地方捕获这个异常, 程序就会终止。
try{
有可能出现异常的语句 ;
}catch (异常类型 异常对象) {
}
finally {
异常的出口
}
备注:
- try 代码块中放的是可能出现异常的代码.
- catch 代码块中放的是出现异常后的处理行为.
- finally 代码块中的代码用于处理善后工作, 会在最后执行.
- 其中 catch 和 fifinally 都可以根据情况选择加或者不加.
示例1 :
int[] arr = {1, 2, 3};
try {
System.out.println("before");
System.out.println(arr[100]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
// 打印出现异常的调用栈
e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
java.lang.ArrayIndexOutOfBoundsException: 100
at demo02.Test.main(Test.java:10)
after try catch
备注:
try 中出现异常 , 那么 try 代码块中的程序就不会继续执行 , 而是交给 catch 中的代码来执行。 catch 执行完毕会继续往下执行。
示例2 :
int[] arr = {1, 2, 3};
try {
System.out.println("before");
arr = null;
System.out.println(arr[100]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("after try catch");
// 执行结果
before
Exception in thread "main" java.lang.NullPointerException
at demo02.Test.main(Test.java:11)
备注:
catch 语句不能捕获到空指针异常, 因此程序中途终止。
示例3 :
int[] arr = {1, 2, 3};
try {
System.out.println("before");
arr = null;
System.out.println(arr[100]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("这是个数组下标越界异常");
e.printStackTrace();
} catch (NullPointerException e) {
System.out.println("这是个空指针异常");
e.printStackTrace();
}
备注:
可以同时捕获多个异常
示例4 :
int[] arr = {1, 2, 3};
try {
Scanner sc = new Scanner(System.in);
System.out.println("before");
arr = null;
System.out.println(arr[100]);
System.out.println("after");
} catch (Exception e) {
e.printStackTrace();
} finally {
sc,close();
System.out.println("end code");
}
// 执行结果
before
java.lang.NullPointerException
at demo02.Test.main(Test.java:12)
end code
备注:
无论是否存在异常, fifinally 中的代码一定都会执行到。
try (Scanner sc = new Scanner(System.in)) {
int num = sc.nextInt();
System.out.println("num = " + num);
} catch (Exception e) {
e.printStackTrace();
}
备注:
try括号中声明的资源对象,会自动关闭。更加实用(防止null指针异常)
错误示例1 :
public static int parseInt(String s){
try {
return Integer.parseInt(s);
}finally {
return 0;
}
}
备注:
调用parseInt("a"), Integer.parseInt(s)会抛出一个异常, 但finally子句的return语句会"吃掉"这个异常!
错误示例2 :
public class Demo {
public static void main(String[] args) {
parseInt(new LinkedList());
}
public static void parseInt(Deque s) {
long time = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
try {
s.pop();
} catch (Exception e) {
}
}
System.out.println(System.currentTimeMillis() - time); //输出 4625
time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
if (!s.isEmpty())
s.pop();
}
System.out.println(System.currentTimeMillis() - time); //输出 1
}
}
备注:
捕获异常的时间开销远远大于普通语句s.pop()语句。
总结:
通常, 最好的选择是什么也不做, 将异常传递给调用者, 由调用者来处理异常。
finally子句不要使用控制流语句(如: return,throw, break, continue)
二 断言
2.1 启用断言与禁用断言
默认情况下, 断言是禁用的
IDEA启用断言:
1) 点击运行, 选择编辑配置
2)选择add VM选项
3)输入 -ea
4)输入 -da 禁用断言
2.2 使用断言
示例1:
public class Demo {
public static void main(String[] args) {
int a = 5;
assert a != 5;
}
}
当 assert 结果为false 则会抛出 AssertionError 异常。
示例2:
public class Demo {
public static void main(String[] args) {
int a = 5;
assert a != 5:"a不能等于5";
}
}
"a不能等于5" 表达式会传入AssertError对象的构造器, 并转化成一个消息字符串。
备注:
相比异常, 断言机制可以在测试期间插入检查, 而在生存代码中会自动删除这些检查。
三 日志
暂略