异常
了解异常
常见的几种异常
除以0 (算数异常)
System.out.println(10/0);
//执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
数组越界异常
int[] array = {1,2,3,5,6};
System.out.println(array[10]);
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
空指针异常
public static void main(String[] args) {
int[] array = null;
System.out.println(array[1]);
}
// 执行结果
Exception in thread "main" java.lang.NullPointerException
防御式编程
LBYL :
在操作之前就做好充分检查。
EAFP
先操作,遇到问题再处理。
EAFP风格的代码:
try {
}catch{
}
Java中异常的核心思想就是 EAFP。
异常分类
运行时异常(非受查异常)
程序运行期间发生的错误,运行异常。
指程序已经编译通过得到了class文件,再由JVM执行过程中出现的错误。
public static void main(String[] args) {
int a = 10;
int b = 0;
System.out.println(a/b);// 出现算术异常
// java.lang.ArithmeticException: / by zero
System.out.println("lalal"); //此程序不会执行
}
a / b = 10 / 0,这个异常会交给JVM来处理,一旦交给JVM,那么程序就会异常终止。
编译时异常(受查异常)
程序编译期间异常。
异常的基本用法
▷捕获异常
try catch 的使用
1. try catch 基本语法
try {
可能会发生的异常;
}catch(异常类型 异常对象){
处理异常;
}finally {
异常的出口;
}
✪ try代码块 中放的是可能会出现异常的代码。
✪ catch代码块 中放的是出现异常后的处理行为。
✪ finally代码块中的代码用于处理善后工作,会在最后执行。
✪ catch 和 finally 都可以根据情况选择加或不加。
2. 使用try catch 处理异常
int a = 10;
int b = 0;
try {
System.out.println(a/b);// 出现异常
System.out.println("哈哈"); //此程序不会继续执行
}catch (ArithmeticException e ) {
e.printStackTrace();
System.out.println("捕获了空指针异常");
}
// 执行结果
java.lang.ArithmeticException: / by zero
at demo1.Test0.main(Test0.java:21)
捕获了空指针异常
❈ 一旦 try 中出现异常,那么 try 代码块中的程序不会继续执行,而是交给catch 中的代码来执行。catch 执行完毕会继续向下执行。
3. catch 只能处理对应种类的异常
try {
int a = 10;
int b = 10;
System.out.println(a/b);
System.out.println("哈哈");
String str = null;
System.out.println(str.length());// 代码会抛出空指针异常
}catch (ArithmeticException e ) {
e.printStackTrace();
System.out.println("捕获了算数异常");
}
// 执行结果
1
哈哈
Exception in thread "main" java.lang.NullPointerException
at demo1.TestDemo.main(TestDemo.java:44)
因为异常类型不匹配,所以 catch 语句不能捕获空指针异常。
4. catch可以有多个
try {
int a = 10;
int b = 10;
System.out.println(a/b);
String str = null;
System.out.println(str.length());
}catch (ArithmeticException e ) {
e.printStackTrace();
System.out.println("捕获了算数异常");
}catch (NullPointerException e){
e.printStackTrace();
System.out.println("空指针异常");
}
// 执行结果
1
空指针异常
java.lang.NullPointerException
at demo1.TestDemo.main(TestDemo.java:44)
如果多个异常的处理方式是相同的,也可以写成如下形式:
catch (ArithmeticException e | NullPointerException e) {
e.printStackTrace();
}
5. 也可以用一个 catch 捕获所有异常
try {
int a = 10;
int b = 10;
System.out.println(a/b);
String str = null;
System.out.println(str.length());
}catch (Exception e ) {
e.printStackTrace();
System.out.println("捕获了算数异常或空指针异常");
}
System.out.println("lalal");
// 执行结果
1
捕获了算数异常或空指针异常
lalal
java.lang.NullPointerException
Exception 类是所有异常类的父类,所有可以用这个类型表示所有异常。
catch 在进行类型匹配的时候,不光会匹配相同类型的异常对象,也会捕捉目标异常类型的子类对象。
ArithmeticException 和 NullPointerException 都是Exception 的子类,因此都会被捕捉。
♦ 写catch 时 (父类与子类一同出现时 )父类异常不要放到最前面 , 不然后面其他子异常捕获不到,会报错。如下图:
一般不太推荐使用Exception
Exception的使用 示例:
public void getCustomerInfo() {
try {
// do something that may cause an Exception
} catch (java.io.FileNotFoundException ex) {
System.out.print("FileNotFoundException!");
} catch (java.io.IOException ex) {
System.out.print("IOException!");
} catch (java.lang.Exception ex) {
System.out.print("Exception!");
}
}
有如下四种结果,想一想程序最终的运行结果是什么?
程序运行出现异常时,catch 只会捕获一处异常。
正确答案:A
6. 如果本方法中没有合适的处理异常的方式,就会沿着调用栈向上传递
public static void function(){
System.out.println(10/0);
}
public static void main(String[] args) {
function();
}
function 没有处理异常 就会给 main 处理异常,
main 函数没有处理异常,就只能交给 JVM 处理,
交给 JVM 处理,程序会异常终止。所以如下:
✍✍ 一定要自己处理异常:
public static void function(){
System.out.println(10/0);
}
public static void main(String[] args) {
try {
function();
}catch (ArithmeticException e){
e.printStackTrace();
}
System.out.println("结束");
}
// 执行结果
java.lang.ArithmeticException: / by zero
at demo1.TestDemo.function(TestDemo.java:29)
at demo1.TestDemo.main(TestDemo.java:33)
结束
7. 如果向上传递一直没有合适的方法处理异常,最终会交给 JVM 处理,程序就会异常终止。
public static void func2(){
System.out.println(10/0);
}
public static void main(String[] args) {
func2();
System.out.println("结束");
}
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
at demo1.TestDemo.func2(TestDemo.java:22)
at demo1.TestDemo.main(TestDemo.java:25)
程序没有执行 System.out.println(“结束”);
显然,程序终止异常了。
finally 语句
在异常处理中,完成释放资源,关闭数据库、关闭文件等工作。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
String str1 = scanner.nextLine();
System.out.println(str1);
}catch (NullPointerException e){
e.printStackTrace();
}finally {
scanner.close();
}
}
scanner 可以看作一种资源,输入之后再关闭。
▶▷上述代码进行优化
鼠标放在 try 上 ,Alt + Enter >> Replace
若使用 try( 某个资源 ),
后面就可不再使用 finally。
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
String str1 = scanner.nextLine();
System.out.println(str1);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
Scanner 对象在try( ) 中创建,就能保证在 try 执行完毕后自动调用 Scanner 的 close 方法。
finally 的注意事项
public static int func() {
try {
int a = 20;
return a;
}catch (NullPointerException e) {
System.out.println("空指针异常!");
}finally {
return 30; // 以后不要在finally 中写return
}
}
public static void main(String[] args) {
System.out.println(func());
}
//执行结果
30
注意:
执行 finally 一般在方法返回之前(try 或者 catch 中 如果有 return 会在这个return 之前执行 finally),但如果 finally 中也存在 return 语句 ,那么就会执行 finally 中的 return,不会执行 try 中的 return。
☝尽量不要在 finally 中写 return
▷处理异常流程
- 先执行 try 语句;
- 如果 try 中代码出现异常,则结束 try 中代码,查看 catch 中捕获异常是否匹配;
- 若找到匹配的异常类型,就执行 catch 中的代码;
- 若没有找到匹配的异常类型,就将异常向上传递到上层调用者;
- 无论是否找到匹配的异常类型,finally 中的代码都会被执行到(在该方法结束前执行);
- 如果上层调用者也没有处理异常,则继续向上传递;
- 直到 main 方法也没有合适的代码处理异常,就会交给 JVM 处理,此时程序就会异常终止。
▷抛出异常
- throw 手动抛出异常
public static void func0(int a,int b){
if (b == 0){
throw new ArithmeticException("除0异常");
}
System.out.println(a/b);
}
public static void main(String[] args) {
try {
func0(10, 0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
System.out.println("lalal");
}
抛出异常后,要再进行手动捕获异常。
- 声明异常 throws
public static void func0(int a,int b) throws ArithmeticException{
if (b == 0){
throw new ArithmeticException("除0异常");
}
System.out.println(a/b);
}
public static void main(String[] args) {
try {
func0(10, 0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
把可能抛出的异常标注在方法定义的位置上,提醒调用者要注意捕获这些异常。
▷自定义异常类
class MyException extends RuntimeException{
public MyException(String message){
super(message);
}
}
class UserError extends Exception{
public UserError(String message){
super(message);
}
}
注意 :
◈ 自定义异常通常会继承于 Exception 或者 RuntimeException;
◈ 继承于 Exception 的异常默认是受查异常;
◈ 继承于 RuntimeException 的异常默认是非受查异常。