异常
什么是异常
异常:即程序执行过程中不正常的行为;
java中不同类型的异常,都有与其对应的类来进行描述
异常的体系结构
- 异常体系的顶层类是Throwable,顶层类派生出2个子类,即Error(错误)、Exception(异常);
- 错误是指java虚拟机无法解决的严重问题,但凡发生,没有办法挽回;
- 异常发生后,程序员是可以通过代码进行处理的;
异常的分类
异常分为编译时异常和运行时异常2种;
编译时异常:也称为受查异常,是在程序编译期间出现的异常;
运行时异常:也称为非受查异常,是在程序执行期间发生的异常;
注:程序编译时出现的语法错误不属于异常,例如一些分号的缺失,括号不匹配等问题;
异常的处理方式
防御式
- 事前防御型:在操作进行之前进行检查;
这种处理方式将正确的执行流程和处理错误的流程放在一起,较为混乱,不太推荐;
- 事后认错型:先进行正常的操作,当程序出现了问题以后再去处理;
这种处理方式很好的区分了正确的流程和处理错误的流程,代码清晰明了,更为实用;
异常抛出
public static void func(int [] arr,int i) {
if(i<0||i>arr.length){
//使用throw来抛出一个异常
throw new ArrayIndexOutOfBoundsException("数组越界异常");
}
}
public static void main(String[] args) {
int [] arr={1,2,3,4,5};
int i=0;
func(arr,10);
}
- java中使用关键字throw来抛出一个异常,一旦throw语句执行,表示一定是出现了此异常;
- throw语句必须在方法内部;
- throw语句一次只能抛出一个异常;
- 异常一旦抛出,throw语句后面的代码不会执行;
异常声明
public static void func(int [] arr,int i) throws ArrayIndexOutOfBoundsException,NullPointerException{//使用throws来声明一个异常
if(i<0||i>arr.length){
//使用throw来抛出一个异常
throw new ArrayIndexOutOfBoundsException("数组越界异常");
//System.out.println("出现异常时,throw语句后面的代码不会执行");
}
}
public static void main(String[] args) {
int [] arr={1,2,3,4,5};
int i=0;
func(arr,2);
}
- 异常的声明需要使用关键字throws;
- 异常的声明是在方法参数列表的后面,不在方法内部;
- 可以一次声明多个异常,使用逗号隔开即可;
- 异常的声明只是提出了一个可能的异常情况,具体是否存在异常不由异常的声明决定;
如果方法内部抛出了多个异常,方法就必须声明多个异常,当抛出的异常之间是父类与子类的关系,则方法可以仅声明父类的异常;
当程序调用抛出异常的方法时,调用者需要对异常进行处理,或者使用throws继续声明;
抛出异常和声明异常都没有对异常真正处理,只是将异常告诉了方法的调用者,如果需要真正对方法进行处理,需要使用 try - catch 进行不活并处理;
异常的捕获处理
public class Test1 {
public static void func(int [] arr,int i) throws ArrayIndexOutOfBoundsException{//使用throws来声明一个异常
try{
if(i<0||i>arr.length){
//使用throw来抛出一个异常
throw new ArrayIndexOutOfBoundsException("数组越界异常");
//System.out.println("出现异常时,throw语句后面的代码不会执行");
}
}catch(ArrayIndexOutOfBoundsException e ){
e.printStackTrace();//使用该语句可以让我们看到异常的情况
System.out.println("");
}
}
public static void main(String[] args) {
int [] arr={1,2,3,4,5};
int i=0;
func(arr,6);
}
}
这里通过程序的退出码为0得知,程序是正常且成功执行完毕的,标红的异常是由于使用了printStackTrace( )方法;
使用try - catch 成功捕获并处理异常的条件:
异常的类型与 catch ( 异常 e)的异常必须一致或是前者是后者的基类;
异常是根据类型来捕获的;
public class Test1 {
public static void func(int [] arr,int i) {
try{
if(i<0||i>arr.length){
//使用throw来抛出一个异常
throw new ArrayIndexOutOfBoundsException("数组越界异常");
//try块内抛出异常位置之后的代码将不会被执行
}
}catch(ArrayIndexOutOfBoundsException e ){
e.printStackTrace();//使用该语句可以让我们看到异常的情况
System.out.println("处理异常");
//异常处理成功这里的后续代码都可以执行;
}
}
public static void main(String[] args) throws ArrayIndexOutOfBoundsException {
int [] arr={1,2,3,4,5};
int i=0;
func(arr,6);
}
}
多种异常,一次捕获,多次处理:
public static void main(String[] args) {
int [] arr={1,2,3,4,5};
int [] arr1=null;
try{
System.out.println(arr[5]);
System.out.println(arr1[2]);
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("数组越界异常");
e.printStackTrace();
}catch (NullPointerException e){
System.out.println("空指针异常");
e.printStackTrace();
}
}
这里try当中有多个异常,捕获的时候可以捕获多个异常,但是第一个异常之后,后面的代码不会再运行了,因此结果显示只捕获到了一个异常;
如果多个异常的处理方式相同,可以进行合并:
catch(ArrayIndexOutOfBoundsException | NullPointerException e){
}
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则会出错;
可以通过一个catch(Exception e)捕获所有的异常,即多个异常,一次捕获;但并不推荐,因为这种方式不利于后续针对特定的异常进行特定的处理,但可以使用catch(Exception e)来进行保底捕获,避免有遗漏的异常未成功捕获;
finally
异常的产生可能使一些语句不被执行到,但真正在程序的运行过程中,有些特定的代码不论在什么情况下都需要被执行,因此需要finally来解决这个问题;
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("这是个数组下标越界异常");
e.printStackTrace();
} finally {
System.out.println("这里的代码永远都会被执行");
}
System.out.println("没有异常抛出或者异常被成功处理,这里的代码就会执行");
}
自定义异常
自定义异常的流程:
- 自定义异常类,继承Exception或者RuntimeException;
- 在自定义的异常类中定义带一个string参数的构造方法;
- 在合适的位置声明和抛出异常;
public class LogIn {
private String userName = "hello";
private String password = "123456";
public static void main(String[] args) {
try{
LogIn login=new LogIn();
login.loginInfo("asd", "123456");
}catch (userNameException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (passwordException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
//自定义异常类,需要继承
class userNameException extends Exception{
public userNameException(String message) {
super(message);
}
}
class passwordException extends Exception{
public passwordException(String message) {
super(message);
}
}
//进行异常的声明和抛出
public void loginInfo(String userName, String password) throws userNameException,passwordException{
if (!this.userName.equals(userName)) {
throw new userNameException("用户名错误异常");
}
if (!this.password.equals(password)) {
throw new passwordException("密码错误异常");
}
System.out.println("登陆成功");
}
}
注:继承自 Exception 的异常默认是受查异常;
继承自 RuntimeException 的异常默认是非受查异常;
over !