异常处理是JAVA的一种编程概念,用于JAVA程序执行中出现的异常或错误情况。
一. 程序运行出问题的类型
Throwable 异常体系的顶层,其分为 Exception 和 Error 两个子类。
Error 即错误,是JAVA虚拟机无法处理的严重问题。
Exception 即异常,异常产生后,程序员可以通过代码处理,使程序继续运行。
二. 异常的分类
1. 编译时异常
像这样程序还没运行,在编写代码时就报错,就叫编译时异常,也称为受检查异常。
当然,编写代码时的语法错误,是错误!不是异常!
2. 运行时异常
这些在编译时没问题,在程序执行期间发生的异常,就叫运行时异常,也称作非受检查异常。
(1)算数异常
System.out.println(10 / 0);
(2)数组越界异常
int[] arr = {1, 2, 3};
System.out.println(arr[4]);
(3)空指针异常
int[] arr = null;
System.out.println(arr.length);
三. 异常处理
如果方法内出现异常,就会沿着调用栈向上传递,如果向上传递过程中都没有合适的方式处理异常,就会交给JVM处理,程序就会异常终止。
为了避免出现程序运行终止,JAVA提供了以下程序媛可以通过代码处理异常的方式。
1. 异常的抛出 throw
在编写代码时,发现程序出现异常,可以通过throw将异常的信息抛出告知调用者。
语法格式:
throw new XXXXException ("这里书写你异常的信息及产生原因");
使用例子:
int a = 0; if(a == 0) { throw new ArithmeticException ("a=0是异常"); }
throw抛出运行时异常时是不需要处理的 ,只是告知调用者。
但是如果时编译时异常,我们则需要处理,而最简单的处理方式是通过throws处理
2. 异常的声明 throws
语法格式:(一般放在方法声明的地方)
修饰符 返回值类型 方法名 (参数列表) throws 异常类型1,异常类型2...{ }
使用例子:
同样的代码,但是我们将异常类型改为编译时异常
int a = 0; if(a == 0){ throw new CloneNotSupportedException ("异常为 a = 0"); }
加上throws CloneNotSupportedException后,错误消失
调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用 throws 抛出
我们以如下代码举例
public static void main(String[] args) {
test();
}
public static void test() throws CloneNotSupportedException{
int a = 0;
if(a == 0){
throw new CloneNotSupportedException("异常为 a = 0");
}
}
我们将Test问题向上抛出给Main方法处理,但是Main没有继续处理,故报错。
解决方法有两种:
① 对异常报错的方法再使用throws声明一下
throws 对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。
如果真正要对异常进行处理,就需要try-catch。
3. 异常的捕获并处理 try-catch
② 使用try-catch捕获并处理该异常
语法格式:
try{
//将可能出现异常的代码放在这里
} catch ( 异常类型1 e ){
//如果try中的代码抛出异常了,且try的异常类型与catch括号中的异常类型一致,或者catch括号中的异常是try异常类型的父类,就会被catch捕捉到,运行这个大括号里的代码
//在catch里对异常进行正常处理,处理完成后,就会跳出try-catch结构,继续执行后续代码
} catch ( 异常类型2 e ){
//多重catch保险,总有一个try异常能找到属于自己的catch~
//异常处理ing
} finally {
//后文会讲
//此处代码一定会被运行到
}// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
使用例子:
public static void main(String[] args){ try { int a = 10 / 0; }catch (ArithmeticException e){ System.out.println("捕获到了异常"); } System.out.println("后续的代码"); }
try异常类型与catch一致,异常成功捕获,由程序员处理,退出代码为0
注意事项
(1)try异常类型与catch异常类型不匹配
try {
int a = 10 / 0;
}catch (NullPointerException e){
System.out.println("捕获到了异常");
}
System.out.println("后续的代码");
}
导致结果:异常捕获失败,也就不会被处理,继续往外抛,直到JVM收到中断程序,退出代码为1
(2)异常一旦抛出,其后的代码就不会执行
public static void main(String[] args){
try {
int a = 10 / 0; //此处出现异常
System.out.println("66666666"); //之后的代码便不会被执行
}catch (ArithmeticException e){
System.out.println("捕获到了异常");
}
System.out.println("后续的代码");
}
运行结果:
(3)多个 catch 来捕获,但是只能抛出一个异常
try 中可能会抛出多个不同的异常对象,则必须用多个 catch 来捕获,但是只能抛出一个异常
public static void main(String[] args){
try {
int a = 10 / 0;
}catch (NullPointerException e){
System.out.println("捕获到了异常111");
}catch (ArithmeticException e){
System.out.println("捕获到了异常222");
}
System.out.println("后续的代码");
}
运行结果:
(4)子类异常在前catch,父类异常在后catch
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则则会报错
public static void main(String[] args){
try {
int a = 10 / 0;
}catch (Exception e){
System.out.println("捕获到了异常111");
}catch (NullPointerException e){
System.out.println("捕获到了异常222");
}
System.out.println("后续的代码");
}
Exception 是所有异常的父类,所以报错
4. 异常的扫尾处理 finally
在程序正常或者异常退出时,必须要对资源进行回收,而 finally 中的代码一定会执行的,一般在 finally 中进行一些资源清理的扫尾工作
语法格式:
try {
// 可能会发生异常的代码
} catch ( 异常类型 e ){
// 对捕获到的异常进行处理
} finally {
// 此处的语句无论是否发生异常,都会被执行到
}
// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
public static void main(String[] args) {
try {
int a = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("捕获到了异常");
} finally {
System.out.println("finally代码");
}
System.out.println("后续的代码");
}
运行结果:
四. 自定义异常类
假设我们有一个用户登录账号密码的项目业务,处理用户名错误和密码错误的时候可能你会这样写代码
public class Login{
private String UserName = "jack256";
private String Password = "123456";
public void loginInfo (String username,String password){
if(!this.UserName.equals(username)){
System.out.println("用户名错误");
}
if(!this.Password.equals(password)){
System.out.println("密码错误");
}
System.out.println("登录成功");
}
}
public void main(String[] args) {
Login login = new Login();
login.loginInfo("jack256","123456");
}
但是专业一点,处理用户名错误和密码错误我们也可以抛出两种异常,我们可以基于已有的异常类(如 Exception 或者 RunTimeException)进行扩展 ( 继承 ), 自定义创建和我们业务相关的异常类。
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常
语法格式: (自定义异常类,继承自Exception 或者 RunTimeException)
class 自定义异常名称 extends 已有异常类 { }
具体实现:
//UserNameException类
class UserNameException extends RuntimeException { public UserNameException() { } public UserNameException(String message) { super(message); } }
//PasswordException 类
class PasswordException extends Exception { public PasswordException() { } public PasswordException(String message) { super(message); } }
//LogIn类
public class LogIn { private String userName = "admin"; private String password = "123456"; public void loginInfo(String userName, String password) throws UserNameException,PasswordException{ if (!this.userName.equals(userName)) { //System.out.println("名字错误"); throw new UserNameException("用户名错误"); } if (!this.password.equals(password)) { //System.out.println("密码错误"); throw new PasswordException("密码错误"); } System.out.println("登陆成功"); } public static void main(String[] args) { LogIn logIn = new LogIn(); try{ logIn.loginInfo("admin1", "123456"); } catch (PasswordException e) { e.printStackTrace(); System.out.println("PasswordException"); }catch (UserNameException e) { e.printStackTrace(); System.out.println("UserNameException"); } } }
运行结果:
希望对你有帮助