栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Java中异常的处理

Java 更新时间:发布时间: 百科书网 趣学号
什么是异常? Java异常体系 异常的用法 异常的处理流程 抛出异常 自定义异常

什么是异常?

我们在之前的学习中已经接触过异常,例如:

数组下标访问越界时:

public class Test2 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(array[5]);
    }
}
//结果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5

将0作为除数时:

public class Test2 {
    public static void main(String[] args) {
        int i = 0;
        int j = 1;
        System.out.println(j/i);
    }
}
//结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero

    上述结果都发生了异常,那么,到底什么才算异常呢?

    异常指的是在程序运行时出现错误并通知调用者的一种机制。

那么,有哪些错误属于异常呢?我们可以通过java异常体系来了解。

Java异常体系


    上图是Java异常的体系结构,Throwable类是Java中所有异常类的父类,它派生出了Exception类和Error类

    Error类指的是Java运行时的内部错误和资源耗尽错误,应用程序不抛出此类异常,这种内部错误一旦出现,就只能告知用户并终止程序,但这种内部错误很少出现。

    Exception类是程序员使用的异常类的父类,Exception类派生出了一个RuntimeException类,这里面抛出了我们常见的异常类,如NullPointerException,IndexOutOfBoundsException等。

    Java中所有继承于Error或RuntimeException类的异常称为非受查异常,非首查异常又叫运行时异常,其他异常称为受查异常,即图片中浅蓝色部分为非受查异常,其余异常均为受查异常。

    受查异常可以理解为编译时发生的异常,当出现受查异常时,编译器会划红线,此时无法编译通过。

    而非受查异常是在运行时才发生的异常,这种异常在程序编译时无法被检测出来,例如数组下标越界异常等。

异常的用法

    我们对异常有两种处理方式,一种是让JVM自动处理:

public class Test2 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(array[5]);
    }
}
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5

当JVM自动处理异常时,程序会直接结束,并不会执行后面的步骤,但有时我们希望程序出现异常后能正常执行,这时就需要我们使用try—catch语句来处理异常:

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int i = 100;
        System.out.println(func(array,i));
    }
    public static int func(int[] array,int i){
        try {	//try{ }中存放可能发生异常的语句
            System.out.println(array[i]);
            return array[i];
        }
        //catch用来捕获异常
        catch (ArrayIndexOutOfBoundsException e){	//( )内存放可能发生异常的类型
        	//固定用法,用于打印出现异常的数据栈
            e.printStackTrace();
            System.out.println("程序发生异常了");
            return -1;
        }
}
//结果:
java.lang.ArrayIndexOutOfBoundsException: 100
	at Test.Test1.func(Test1.java:20)
	at Test.Test1.main(Test1.java:16)
程序发生异常了
-1

    我们可以看出,当我们使用try—catch语句处理异常时,当程序发生异常后并没有直接结束,而是继续执行了后面的语句。

    当我们使用try—catch语句时,try{ }中存放可能发生异常的语句,catch后面的( )内存放可能产生异常的类型,例如上述代码中,try语句中可能会产生数组下标越界异常,catch后的( )存放数组下标异常类,catch方法内部存放发生异常时要执行的操作,e.printStackTrace()用于打印出现的异常。

但是,catch只能捕获对应种类的异常,当我们将上述代码中catch( )内的数组下表异常更改为空指针异常时:

public class Test2 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(array[5]);
    }
}
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5

    当JVM自动处理异常时,程序会直接结束,并不会执行后面的步骤,但有时我们希望程序出现异常后能正常执行,这时就需要我们使用try—catch语句来处理异常:

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int i = 100;
        System.out.println(func(array,i));
    }
    public static int func(int[] array,int i){
        try {	//try{ }中存放可能发生异常的语句
            System.out.println(array[i]);
            return array[i];
        }
        //catch用来捕获异常
        catch (NullPointerException e){	//将这里更改为空指针异常
            e.printStackTrace();
            System.out.println("程序发生异常了");
            return -1;
        }
}
//结果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
	at Test.Test1.func(Test1.java:20)
	at Test.Test1.main(Test1.java:16)

    这时可以发现,catch并没有捕获到对应的异常,这次异常是由编译器捕获的,这是因为catch( )中的异常类型与try{ }中产生的异常类型不匹配。

我们可以同时进行多次catch():

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int i = 100;
        System.out.println(func(array,i));
    }
    public static int func(int[] array,int i){
        try {
            System.out.println(array[i]);
            return array[i];
        }
        catch (NullPointerException e){
            e.printStackTrace();
            System.out.println("程序发生空指针异常");
            return -1;
        }
        catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("程序发生数组下标越界异常");
            return -1;
        }
    }
}
//结果:
java.lang.ArrayIndexOutOfBoundsException: 100
	at Test.Test1.func(Test1.java:20)
	at Test.Test1.main(Test1.java:16)
程序发生数组下标越界异常
-1

    可以看到,这次catch( )成功捕获了异常,第一次catch( )没有捕获到异常后执行第二次catch( ),直到所有catch( )全部完成。

     如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递:

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int i = 100;
        try {
            System.out.println(func(array, i));
        }
        catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("程序发生数组下标越界异常");
        }
    }
    public static int func(int[] array,int i){
            System.out.println(array[i]);
            return array[i];
    }
}
//结果:
java.lang.ArrayIndexOutOfBoundsException: 100
	at Test.Test1.func(Test1.java:25)
	at Test.Test1.main(Test1.java:17)
程序发生数组下标越界异常

     我们通常用finally来进行最后的工作, 如释放资源等:

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int i = 100;
            System.out.println(func(array, i));
    }
    public static int func(int[] array,int i){
        try {
            System.out.println(array[i]);
            return array[i];
        }
        catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println("程序发生数组下标越界异常");
            return -1;
        }
        finally {
            System.out.println("finally");
        }
    }
}

在上述例子中,如果没有finally,catch结束后会直接return -1,但有了finally后,catch会先执行finally,再返回。

    值得注意的是,我们不应该将返回值放在finally中,如果finally中有返回值,那么就一定会返回finally中的return,而不会返回try和catch中的返回值。原因就是在执行try和catch的返回值之前一定会执行finally,但finally中存在返回值,那么就会直接返回finally中的return。

异常的处理流程
  1. 程序先执行 try 中的代码
  2. 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
  3. 如果找到匹配的异常类型, 就会执行 catch 中的代码
  4. 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  5. 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  6. 如果上层调用者也没有对应的catch, 就继续向上传递.
  7. 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

抛出异常

    除了Java内置的类会抛出异常之外,我们也可以通过throw关键字手动抛出某个异常:

public class Test {
    public static void main(String[] args) {
        int i = 0;
        int j = 1;
        if(i==0){
            throw new IndexOutOfBoundsException("你的除数是0");
        }
    }
}
//结果:
Exception in thread "main" java.lang.IndexOutOfBoundsException: 你的除数是0
	at Test.Test.main(Test.java:15)

    在这个代码中, 我们可以根据实际情况来抛出需要的异常. 在构造异常对象同时可以指定一些描述性信息.

自定义异常

Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.

例如, 我们实现一个用户登陆功能:

class UserNameError extends Exception{
    public UserNameError(String message) {
        super(message);
    }
}
class PasswdError extends Exception{
    public PasswdError(String message) {
        super(message);
    }
}
public class Test2 {
    static String userName = "1999";
    static String passwd = "2000";
    public static void main(String[] args) {
        Test2 test2 = new Test2();
        try {
            test2.login("123","2000");
        }
        catch (UserNameError userNameError){
            userNameError.printStackTrace();
        }
        catch (PasswdError passwdError){
            passwdError.printStackTrace();
        }
    }
    public void login(String username,String passwd)throws UserNameError,PasswdError{
        if(!username.equals(Test2.passwd)){
            throw new UserNameError("用户名错误");
        }
        if(!passwd.equals(Test2.passwd)){
            throw new PasswdError("密码错误");
        }
        System.out.println("登录成功!");
    }
}

自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常


The end

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/601790.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号