异常(Exception)指的是:在程序的运行过程中,发生了不正常的现象,阻止了程序的运行。在没接触到Java关于异常处理的机制时,一般可能会尝试用 if-else 堵漏洞,但这样写会有诸多缺点:

  • 代码臃肿,业务代码和处理异常的代码混在一起
  • 可读性差
  • 需要花费大量的经历来维护这个漏洞
  • 很难堵住所有的漏洞

由于 if-else 处理异常缺点太多,所以Java专门出了一个异常处理机制:try-catch-finally

try-catch

public class Test {
    public static void main(String[] args) {
        try{
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第一个数:");
            int num1 = sc.nextInt();
            System.out.println("请录入第二个数:");
            int num2 = sc.nextInt();
            System.out.println("商:" + num1/num2);
        }catch(Exception ex){
            System.out.println("程序出现异常!");
        }
        System.out.println("感谢使用计算器");
    }
}

程序执行结果为:

请录入第一个数:
3
请录入第二个数:
0
程序出现异常!
感谢使用计算器

把可能出现异常的代码放入 try 代码块中,然后将异常封装为对象,被 catch 后面的 () 中的那个异常对象接收,接收以后执行 catch 后面的 {} 里面的代码,然后 try-catch 后面的代码该怎么执行就怎么执行。

注意事项如下:

  • try 中没有异常,catch 中代码不执行

  • try 中有异常,catch 进行捕获

    • 如果 catch 中异常类型和出现的异常类型匹配的话,对异常进行捕获,走 catch 中的代码,try-catch 后面的代码该怎么执行还是怎么执行
    • 如果 catch 中异常类型和出现的异常类型不匹配的话(比如 Exception 换成 ArithmeticException ),捕获失败,不走 catch 中的代码,程序相当于遇到异常了,程序中断,后续代码不执行
  • try 中如果出现异常,然后用 catch 捕获成功的话,那么 try 中后续的代码是不会执行的

catch中如何处理异常

catch 中处理异常的方式有多种:

  1. 什么都不写,什么都不做

    public class Test {
        public static void main(String[] args) {
            try{
                Scanner sc = new Scanner(System.in);
                System.out.println("请录入第一个数:");
                int num1 = sc.nextInt();
                System.out.println("请录入第二个数:");
                int num2 = sc.nextInt();
                System.out.println("商:"+num1/num2);
            }catch(Exception ex){
    
            }
            System.out.println("感谢使用计算器");
        }
    }
    

    程序执行结果为:

    请录入第一个数:
    3
    请录入第二个数:
    0
    感谢使用计算器
    
  2. 输出自定义异常信息

    public class Test {
        public static void main(String[] args) {
            try{
                Scanner sc = new Scanner(System.in);
                System.out.println("请录入第一个数:");
                int num1 = sc.nextInt();
                System.out.println("请录入第二个数:");
                int num2 = sc.nextInt();
                System.out.println("商:" + num1/num2);
            }catch(Exception ex){
                System.out.println("程序出现异常!");
            }
            System.out.println("感谢使用计算器");
        }
    }
    

    程序执行结果为:

    请录入第一个数:
    3
    请录入第二个数:
    0
    程序出现异常!
    感谢使用计算器
    
  3. 打印异常信息

    • 调用 toString() 方法,显示异常的类名(全限定路径)

      public class Test {
          public static void main(String[] args) {
              try{
                  Scanner sc = new Scanner(System.in);
                  System.out.println("请录入第一个数:");
                  int num1 = sc.nextInt();
                  System.out.println("请录入第二个数:");
                  int num2 = sc.nextInt();
                  System.out.println("商:"+num1/num2);
              }catch(Exception ex){
                  System.out.println(ex.toString());
              }
              System.out.println("感谢使用计算器");
          }
      }
      

      程序执行结果为:

      请录入第一个数:
      3
      请录入第二个数:
      0
      java.lang.ArithmeticException: / by zero
      感谢使用计算器
      
    • 显示异常描述信息对应的字符串,如果没有就显示 null

      public class Test {
          public static void main(String[] args) {
              try{
                  Scanner sc = new Scanner(System.in);
                  System.out.println("请录入第一个数:");
                  int num1 = sc.nextInt();
                  System.out.println("请录入第二个数:");
                  int num2 = sc.nextInt();
                  System.out.println("商:"+num1/num2);
              }catch(Exception ex){
                  System.out.println(ex.getMessage());
              }
              System.out.println("感谢使用计算器");
          }
      }
      

      程序执行结果为:

      请录入第一个数:
      3
      请录入第二个数:
      0
      / by zero
      感谢使用计算器
      
    • 显示异常的堆栈信息,将异常信息捕获以后,在控制台将异常的效果展示出来,方便查看异常,但是后面代码该执行还是执行

      public class Test {
          public static void main(String[] args) {
              try{
                  Scanner sc = new Scanner(System.in);
                  System.out.println("请录入第一个数:");
                  int num1 = sc.nextInt();
                  System.out.println("请录入第二个数:");
                  int num2 = sc.nextInt();
                  System.out.println("商:"+num1/num2);
              }catch(Exception ex){
                  ex.printStackTrace();
              }
              System.out.println("感谢使用计算器");
          }
      }
      

      程序执行结果为:

      请录入第一个数:
      3
      请录入第二个数:
      0
      java.lang.ArithmeticException: / by zero
      	at com.test.Test.main(Test.java:13)
      感谢使用计算器
      
    • 抛出异常

      public class Test {
          public static void main(String[] args) {
              try{
                  Scanner sc = new Scanner(System.in);
                  System.out.println("请录入第一个数:");
                  int num1 = sc.nextInt();
                  System.out.println("请录入第二个数:");
                  int num2 = sc.nextInt();
                  System.out.println("商:"+num1/num2);
              }catch(Exception ex){
                  throw ex;
              }
              System.out.println("感谢使用计算器");
          }
      }
      

      程序执行结果为:

      请录入第一个数:
      3
      请录入第二个数:
      0
      Exception in thread "main" java.lang.ArithmeticException: / by zero
      	at com.test.Test.main(Test.java:13)
      

try-catch-finally

总结一下 try-catch 后面的代码不执行的情况:

  • throw 抛出异常
  • catch 中没有正常的进行异常捕获
  • try 中遇到 return(后面代码会报错)

为了使 try-catch 后面的代码无论如何都执行,引入了 finally 语句。

public class Test {
    public static void main(String[] args) {
        try{
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第一个数:");
            int num1 = sc.nextInt();
            System.out.println("请录入第二个数:");
            int num2 = sc.nextInt();
            System.out.println("商:"+num1/num2);
        }catch(Exception ex){
            throw ex;
        }finally {
            System.out.println("感谢使用计算器");
        }
    }
}

程序执行结果为:

请录入第一个数:
3
请录入第二个数:
0
感谢使用计算器
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.test.Test.main(Test.java:13)

注意事项如下:

  • returnfinally 的执行顺序是先执行 finally 最后执行 return

  • 一般来说,关闭数据库资源、关闭IO流资源、关闭socket资源这类代码会放到 finally

  • 有一句代码可以让finally中语句不执行

    System.exit(0);  // 终止当前的虚拟机执行
    

多重catch

try 中出现异常以后,可以将异常类型跟 catch 后面的类型依次比较,执行第一个与异常类型匹配的 catch 语句。

public class Test {
    public static void main(String[] args) {
        try {
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第一个数:");
            int num1 = sc.nextInt();
            System.out.println("请录入第二个数:");
            int num2 = sc.nextInt();
            System.out.println("商:" + num1 / num2);
        } catch(ArithmeticException ex){
            System.out.println("对不起,除数不可以为0");
        }catch(InputMismatchException ex){
            System.out.println("对不起,你录入的数据不是int类型的数据");
        }catch(Exception ex){
            System.out.println("对不起,你的程序出现异常");
        } finally {
            System.out.println("感谢使用计算器");
        }
    }
}

注意事项如下:

  • 一旦执行其中一条 catch 语句之后,后面的 catch 语句就会被忽略

  • 在安排 catch 语句的顺序的时候,一般会将特殊异常放在前面(并列),一般化的异常放在后面。即先写子类异常(如ArithmeticException),再写父类异常(如Exception)

  • 在JDK1.7以后,可以并列用 | 符号连接异常类型。例如 Integer 类中的 getInteger() 方法:

    public static Integer getInteger(String nm, Integer val) {
        String v = null;
        try {
            v = System.getProperty(nm);
        } catch (IllegalArgumentException | NullPointerException e) {
        }
        if (v != null) {
            try {
                return Integer.decode(v);
            } catch (NumberFormatException e) {
            }
        }
        return val;
    }