指的是程序运行过程中,因为用户的误操作、代码的BUG、
等等一系列原因,引起的程序崩溃的现象,被称为异常
崩溃:程序无法正常继续执行,在这行代码抛错,抛错操作系统解决不了,也抛了出去最终软 件退出(因为解决不了问题,无法正常运行)
例:
- package com.openlab.day14;
-
- import java.util.Scanner;
-
- public class TestException01 {
-
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- System.out.print("请输入第一个数:");
- int num1 = input.nextInt();
- System.out.print("请输入第二个数:");
- int num2 = input.nextInt();
- int res = divide(num1, num2);
- System.out.println(String.format("%s ÷ %s = %s", num1, num2, res));
- input.close();
- }
-
- private static int divide(int num1, int num2) {
- int result = num1 / num2;
- return result;
- }
-
- }

解决掉异常的现象,让程序继续运行(不要崩溃退出)下去。
程序容错能力提升!!!!程序就会越稳定!!!
java进行异常处理,有两种解决方案:
|-- 抓捕异常【重点掌握】
|-- 抛出异常
异常分类:
1). 编译型异常:
在源码编译阶段,直接抛出异常,这种异常必须要处理。
不处理,则无法正常运行代码。
例:

异常原因:路径可能不存在
2). 运行时异常:
在代码运行时,有可能出现的异常,被称为运行时异常
一、异常 例就是运行时异常(除零异常)
1. 抓捕异常:
针对于可能出现异常的代码,进行抓捕
try {
// 可能发生异常的代码
} catch (Exception e) {
// 如果出现了一次,代码会立刻进入catch中
// 在这儿解决抓捕到的异常
} finally {
// 必须要执行的代码
}
try {
} catch(XxxException e) {
// 异常处理
} catch(XxxxException e) {
// 异常处理
} catch(Exception e) {
// 异常处理
} finally {
// 必须要执行的代码
}
如果使用抓捕异常,通过这种处理,程序即便是遇到了,也不崩溃!!!
- package com.openlab.day14;
-
- import java.io.File;
- import java.util.Scanner;
-
- public class TestException01 {
-
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- System.out.print("请输入第一个数:");
- int num1 = input.nextInt();
- System.out.print("请输入第二个数:");
- int num2 = input.nextInt();
- int res = divide(num1, num2);
- System.out.println(String.format("%s ÷ %s = %s", num1, num2, res));
- input.close();
- }
-
- private static int divide(int num1, int num2) {
- int result = 0;//局部变量必须初始化,无默认值。成员变量有默认值
- try {
- // 如果出现了错误,则立刻进入catch块
- result = num1 / num2;
- }catch(Exception e){
- //只有出现了异常,catch块中的代码才会执行
- System.out.println("出现了异常");
- }
- return result;
- }
-
- }
1)try catch运行流程:
出现异常的流程:try中出现异常代码之后的代码都不执行,立即跳入catch块,执行完之后再执行后继代码
不出现异常的流程:只有catch块中的代码不执行。try执行完之后,跳过catch块,直接执行后继代码
- package com.openlab.day14;
-
- import java.io.File;
- import java.util.Scanner;
-
- public class TestException2 {
-
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- System.out.print("请输入第一个数:");
- int num1 = input.nextInt();
- System.out.print("请输入第二个数:");
- int num2 = input.nextInt();
- int res = divide(num1, num2);
- System.out.println(String.format("%s ÷ %s = %s", num1, num2, res));
- input.close();
- }
-
- private static int divide(int num1, int num2) {
- int result = 0;
- System.out.println(1);
- try {
- // 如果出现了错误,则立刻进入catch块
- System.out.println(2);
- result = num1 / num2;
- System.out.println(3);
- }catch(Exception e){
- System.out.println(4);
- //只有出现了异常,catch块中的代码才会执行
- System.out.println("出现了异常");
- }
- System.out.println(5);
- return result;
- }
-
- }
不出现异常:

出现异常:

2)java中异常的继承关系:
Throwable 异常的顶级类
|-- Error 致命性的错误
|-- Exception 常见的异常的父类
|-- RuntimeException 运行时异常,只有运行时,才有可能出现异常
其他的Exception的子类都是编译型异常,必须处理

3)多个异常的处理方案:
最后其他异常可以用Exception e 来抓捕,运用了多态原理。所有异常都是Exception的子类,直接或者间接继承了Exception,根据多态原理,根据父类引用代指所有子类
try {
} catch(XxxException e) {
// 异常处理
} catch(XxxxException e) {
// 异常处理
} catch(Exception e) {
// 异常处理
}
- private static int divide(int num1, int num2, Scanner input) {
- int result = 0;
- try {
- result = num1 / num2;
- input.nextInt();
- }catch(ArithmeticException e){
- e.printStackTrace();
- System.out.println("除数不能为零!!!");
- }catch(InputMismatchException e){
- e.printStackTrace();
- System.out.println("请输入整数!!!");
- }catch(Exception e){//
- e.printStackTrace();
- System.out.println("其他异常");
- }
- return result;
- }
4). finally关键字
若都在try块中,关键代码在可能触发异常的代码之后,那么一旦触发异常,关键代码将不能被执行。因为异常,程序会直接跳到catch块中。所以java设计finally关键字。
try {
// 可能存在异常的代码
} catch(XxxException e) {
// 异常处理代码
} [finally {
}] // [ ] 表示可以有,可以没有

1). 把什么样的代码写在finally中?
回收垃圾
关闭IO流
关闭数据库连接
……
核心必须要执行代码
2)finally块
上断代码的关注点不是catch块,只是想执行try后执行finally
在开发过程中,希望有的代码必须执行,但是不能保证一定执行,可能有异常等跳过代码,所以有这样一种结构。利用finally关键字的作用。
try {
} finally {
// 将必须要执行的代码写在finally中!!!
}
3). 注意:在开发过程中请注意,return关键字和finally关键字同时出现的情况,认真分析
仔细考虑
finally 是在jvm层次执行的,不是在原码层次执行的。代码遇到return,,尤其是函数中,遇到return就会立即返回到调用的位置,在返回前,发现后面有finally,会将返回的状态保存在那里,之后开始执行finally,执行完返回。
- package com.openlab.day14;
-
- import java.io.IOException;
-
- public class TestException4 {
-
- public static void main(String[] args) throws IOException {
- int res = say();
- System.out.println(res);
- }
-
- public static int say() {
- try {
- int a = 10;
- return a;
- } catch (Exception e) {
- } finally {
- System.out.println("这儿还会执行吗?");
- }
- return 0;
- }
-
- }

面试题:下面代码的运行结果?
- package com.openlab.day14;
-
- import java.io.IOException;
-
- public class TestException5 {
-
- public static void main(String[] args) throws IOException {
-
- int res = say();
- System.out.println(res);
- }
-
- public static int say() {
- int a = 10;
- int b = 20;
- try {
- b += a++;// b = 30 a = 11
- return b;
- } catch (Exception e) {
-
- } finally {
- a += 10;// a = 21
- b += 20;// b = 50
- }
- return a;
- }
-
- }

finally在return之后执行,但是return的趋势已经形成,一旦return的趋势形成,会将要返回的值临时存储下来,等待后面执行finally之后再将它返回。虽然b = 50,a = 21,但是不影响将30return
将异常交给调用者处理
编程型异常,如果我们不想处理,可以直接抛给调用者,由调用者完成处理,调用者也不处理,会继续上抛,直到抛给main函数,main函数抛给jvm,jvm尝试处理,发现解决不了,就挂了


由此看出,抛出异常并不是永久解决问题的方案,我们抛出异常原因:
异常不一定会触发,尤其是编译型异常出现,懒得处理(如刚刚的文件例子,有路径异常就出现不了,出现不了就没有问题)
例:
- package com.openlab.day14;
-
- import java.io.File;
- import java.io.IOException;
-
- public class TestException4 {
-
- public static void main(String[] args) throws IOException {
- String path = "c:/a.txt";
- creatFile02(path);
- }
-
- private static void creatFile02(String path) throws IOException {
- File file = new File(path);
- file.createNewFile();
- }
-
- private static void creatFile(String path) {
- try {
- File file = new File(path);
- file.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- System.out.println("创建失败");
- }
- }
-
-
- }
接触到的都是属于java官方提供的异常类,
有时候自己设计异常类
自定义异常类:定义一个普通的类,让该类成为成为Exception的子类或Exception类的孙子类
异常抛出:
可以通过创建异常(系统或者自定义),来给调用者一个确切的错误信息
调用者就可以通过抛出的信息做成对应的判断。
在自定义异常时,若继承Exception,做的是编译型异常,在编译阶段必须得处理,不建议使用编译型异常。建议使用运行时异常,简化写法
编译型异常例:
- package com.openlab.day14;
-
- public class MyException extends Exception{
-
- private static final long serialVersionUID = -5723348118187732441L;
-
- public MyException() {
- super();
-
- }
-
- public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
-
- }
-
- public MyException(String message, Throwable cause) {
- super(message, cause);
-
- }
-
- public MyException(String message) {
- super(message);
-
- }
-
- public MyException(Throwable cause) {
- super(cause);
-
- }
-
- }
- package com.openlab.day14;
-
- public class Test02 {
-
- public static void main(String[] args) {
-
- }
-
- //登录功能
- //需要写 throws MyException 。抛出的是编译型异常,往出去抛还得在当前声明,所以不建议使用
- public void login(String username, String password) throws MyException {
- if(username.equals("admin") && password.equals("123456")) {
- System.out.println("登录成功");
- }else {
- //throws是放在方法上的,用来抛出不处理的异常,处理异常
- //throw是人为在底层向调用者抛出异常对象的,制造异常
- throw new MyException("对不起,用户名或者密码错误,请重新登录");
- }
- }
-
- }
throws和throw区别:
throws是放在方法上的,用来抛出不处理的异常,处理异常
throw是人为在底层向调用者抛出异常对象的,制造异常
运行时异常例:
- package com.openlab.day14;
-
- /*
- * 在自定义异常时,建议使用运行时异常
- */
- public class MyException extends RuntimeException{
-
- private static final long serialVersionUID = -5723348118187732441L;
-
- public MyException() {
- super();
-
- }
-
- public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
-
- }
-
- public MyException(String message, Throwable cause) {
- super(message, cause);
-
- }
-
- public MyException(String message) {
- super(message);
-
- }
-
- public MyException(Throwable cause) {
- super(cause);
-
- }
-
- }
- package com.openlab.day14;
-
- public class Test02 {
-
- public static void main(String[] args) {
- try {
- new Test02().login("admin", "admin");
- }catch(MyException e) {
- //e.printStackTrace();
- System.out.println(e.getMessage());
- }
- }
-
- //登录功能
- //编译型需要写 throws MyException 。抛出的是编译型异常,往出去抛还得在当前声明,所以不建议使用
- public void login(String username, String password) {
- if(username.equals("admin") && password.equals("123456")) {
- System.out.println("登录成功");
- }else {
- //throws是放在方法上的,用来抛出不处理的异常,处理异常
- //throw是人为在底层向调用者抛出异常对象的,制造异常
- //自定义异常,可以向调用者传递需要的信息
- throw new MyException("对不起,用户名或者密码错误,请重新登录");
- }
- }
-
- }
