Java异常处理机制

Java异常

异常简介

什么是异常

编译期间的异常指:在写代码的时候编译器能识别出来的错误。

运行时的异常指:代码没有报错,但是运行时出现逻辑错误。在程序运行过程中,意外发生的情况,偏离我们程序本身的意图的表现,都可以理解为异常。
程序运行期间产生异常的后果:
轻:程序不正常运行,产生意想不到的后果
重:中断运行,造成用户数据丢失,系统资源无法正常释放甚至于:系统直接崩溃。

java中提供和非常强大的异常处理机制。

异常分类

Throwable 是所有异常的根类,有子类Error和子类Exception。

Error子类:程序中无法处理的错误,表示运行应用程序中较严重的问题。大多数都是JVM虚拟机出了问题。它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。

Exception子类:程序本身可以处理的异常,异常处理通常指针对这种类型异常的处理。异常分类为Unchecked Exception和Checked Exception。

Unchecked Exception非检查异常:编译器不要求强制处理的异常。RuntimeException和它的子类,包括NullPointerException空指针异常、ArrayIndexOoutOfBoundsException数组下标越界异常、ArithmeticException算数异常和ClassCastException类型转换异常。Checked Exception检查异常:除了RuntimeException之外的异常,包括IOExceptionIO异常和SQLExceptionSQL异常。这些异常要求程序在编码阶段就对异常进行处理,否则编译是无法正常的通过的。

异常处理简介

异常处理分类

在Java应用程序中,异常处理机制为:抛出异常,捕捉异常。

抛出异常

1、当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统。

2、异常对象包含了异常类型和异常出现时的程序状态等异常信息。

3、运行时系统负责寻找处置异常的代码并执行。

捕获异常

1、在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器。

2、运行时系统从发生异常的方法开始,依次回查调用栈中的方法,当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。

3、当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

4、对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。

5、总体来说,Java规定能够,对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。

6、简单地说,异常总是先被抛出,后被捕捉的。

异常处理:下一章

try…catch…finally实现异常处理

try-catch-finally简介

1、try块用于捕获异常,catch块用于处理try捕获到的异常,finally块无论是否发生异常代码总能执行。
2、try块后可以接零个或者多个catch块,如果没有catch块,则必须跟一个finally块进行使用。
3、catch和finally没有try的加入无法单独行动 。

使用try-catch结构处理异常

1、Exception e; e.printStackTrace();打印异常信息,包括异常类型描述和异常位置。
2、try抛出异常,catch捕获异常,finally无论怎么样都一定会执行的代码。

常见异常类型及原因分析

程序开发中不可避免会产生或者遇到异常,了解并合理的处理异常是一名优秀开发者的必备技能,下面结合Java常见异常的说明、出现条件以及示例演示进行总结。

ArithmeticException

1、异常说明:数字运算异常

2、出现条件:涉及到数学运算的地方可能出现失误,比如程序中出现了除以零这样的运算

3、示例演示:

public static void main(String[] args) {
    int one = 12;
    int two = 0;
    System.out.println(one/two);
}

运行结果:

NumberFormatException

1、异常说明:数字格式化异常

2、出现条件:涉及到类型转换时,比如不符合转换格式的字符串被转换成数字

3、示例演示:

   public static void main(String[] args) {
        String one = "abdc123";
//        Integer.parseInt();将字符串转换为对应的整数
        System.out.println(Integer.parseInt(one));
    }

运行结果:

ArrayIndexOutOfBoundsExecption

1、异常说明:数组下标越界异常

2、出现条件:涉及到使用超出数组下标范围的下标

3、示例演示:

public static void main(String[] args) {
    int[] array = {1,2,3,4};
    for (int i = 1; i <= 4; i++) {
        System.out.println(array[i]);
    }
}

运行结果:


NullPointerException

1、异常说明:空指针异常

2、出现条件:当使用了未经初始化的对象或者是不存在的对象时

3、示例演示:

 public static void main(String[] args) {
       String str = null;
//     字符串对象.length();可获取字符串对象的字符个数
       System.out.println(str.length());
    }

运行结果:

ClassCastException

1、异常说明:类型转换异常

2、出现条件:如进行向下转型,转换对象无法完成正常转换

3、示例演示:

public class Father {
}
public class Son extends Father{
}
public class Brother extends Father{
}
public class Test {
    public static void main(String[] args) {
        Father one =  new Son();
        Brother two = (Brother) one;
    }
}

运行结果:

InputMismatchException

1、异常说明:输入格式错误的异常

2、出现条件:接受数据与预期格式不符时

3、示例演示:

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    System.out.println("请输入整数");
    int a = input.nextInt();
}

运行结果:

FileNotFoundException

1、异常说明:文件未找到异常

2、出现条件:操作文件内容时发现文件不存在

3、示例演示:

/**
 * java.io.FileInputStream:文件输入流,多用于文件读取。
 * 可在构造方法参数中设置读取的文件路径。
 * 暂定d:\file\a.txt文件不存在。
 * read():可用于读取文件中的数据
 * 由于FileInputStream()和read()执行时会产生检查异常,必须进行异常处理。
 * 特用try..catch做简单处理
 */
public static void main(String[] args) {
    try {
        java.io.FileInputStream f1 = new java.io.FileInputStream("d:\\file\\a.txt");
        System.out.println(f1.read());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

运行结果:

使用多重catch结构处理异常

1、一个try块跟的多个catch不允许出现同类型异常,当catch捕获第一个异常,后面的catch语句将不再被执行。

2、一般在最后一个catch块中添加一个Exception父类。Exception可以捕获所有类型的异常,放在最后一个,以免漏掉异常。

关于try-catch应用中常见问题

1、try…catch 里面定义的变量外部可以使用么?
不可以,try…catch代码块中定义的是局部变量,只能在其代码块中应用。

2、try…catch 结构中,catch(Exception e) 这个参数必须要是e吗?
这里对象名只是指代可捕获的上面try中的异常对象,并不是只能命名为e,只要符合Java命名规范,且不会造成范围内变量命名冲突即可。

3、既然 Exception 是父类,是不是平时工作的时候只用它就行了?
虽然Exception是异常的父类,但是更推荐大家结合多重catch有针对性的设置针对不同类型异常产生的处理方案。这样可以更好的增强程序的可扩展性和可维护性

4、为什么 ArithmeticException 不需要导入,而 InputMismatchException 需要手动导入呢?
ArithmeticException的完整名称是:java.lang.ArithmeticException ,由于java.lang包是默认直接加载的,所以不需要导入操作;而InputMismatchException的完整名称是:java.util.InputMismatchException,由于java.util包不是默认直接加载的,所以需要导入操作才能正常应用

5、异常输入的顺序与catch块定义的位置有关吗?
异常输出的顺序只与产生异常的位置有关,但是当应用多重catch进行异常捕获时,需要注意范围,即大范围(父类)后置。

终止finally执行的方法

一般情况下finally块的语句不管怎样都会被执行。但也存在特殊情况:System.exit(n)命令:当n为非0时,终止程序运行。此时,直接终止Java程序,不执行finally块的语句

return关键字在异常处理中的作用

1、return关键字的作用是,终止方法的执行,并将方法返回值带回。
2、如果try-catch-finally语句块中都有“return”关键字,那最终只能返回finally语句块中return返回的值。
3、所以,从逻辑角度出发,不建议将return关键字,放到finally语句块中。

异常处理中的返回操作

在Java程序中,我们可以通过try-catch-finally来捕获并处理各种异常。那么在异常处理中,返回操作是如何执行的呢?通常,我们会结合System.exit(参数);和return返回值来完成。下面,分别从语法和应用角度针对两种操作进行总结。

System.exit(参数);

语法:方法表示终止当前运行的Java虚拟机。其中,参数作为状态代码。按照惯例,非零状态码表示异常终止。

应用:从表层运行结果来看,无论参数为何,程序都会直接退出。不同的数值代表的信息是传递给底层。如下图所示,在catch中加入System.exit(1);

public static void main(String[] args) {
    try {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入整数");
        int a = input.nextInt();
    }catch (Exception e){
        System.exit(1);
        System.out.println("catch");
    }finally {
        System.out.println("finally");
    }
}

当输入abc时,如下图,程序直接结束,不会继续执行输出catch和finally

return返回值;

语法:如果方法返回值为void,则return后无需加返回值,直接以分号结束;通过return返回值,则可以结束当前方法执行将返回值带回调用处。return语句可以分别出现在try、catch以及finally块中,但是由于finally语句块一定要执行,所以当存在finally时,会先执行完finally中的代码再执行return。

应用:

1、如下,主方法返回值为void,因此return之后可以无返回值;并在try...catch...finally中都添加了return

public static void main(String[] args) {
    try {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入整数");
        int a = input.nextInt();
    }catch (Exception e){
        System.out.println("catch");
        return;
    }finally {
        System.out.println("finally");
        return;
    }
}

结合如下运行结果可以看出,当输入数据产生异常,分别执行完catch、finally后才会执行return跳出并结束执行。

2、如下,getResult方法返回值为int,分别在try...catch...finally中,通过return带回返回值**

public static void main(String[] args) {
    System.out.println(getResult());
}

public static int getResult(){
    int temp = 10;
    try{
        System.out.println("try");
        return temp+1;
    }catch (Exception e){
        System.out.println("catch");
        return temp+2;
    }finally {
        System.out.println("finally");
        return temp+3;
    }
}

结合下图可以看出,程序无异常,依次输出try,finally中的文字,并输出finally中返回的结果。

3、如下所示,getResult方法中去掉finally中的返回值

public static int getResult(){
    int temp = 10;
    try{
        System.out.println("try");
        return temp+1;
    }catch (Exception e){
        System.out.println("catch");
        return temp+2;
    }finally {
        System.out.println("finally");
    }
}

结合下图所示运行结果可以看出,程序无异常,依次输出try,finally中的文字,并输出try中的返回结果。

throw和throws

通过throws声明将要抛出的何种类型的异常,通过throw将产生的异常抛出

throws

  • 场景:当一个方法可能会出现异常,但没有能力处理这种异常。可以通过方法声明处用throws语句来抛出这种异常。谁调用该方法谁就要处理这种异常。
  • throws后可以跟单个或者多个异常类型,注意不是异常对象。多个异常对象使用逗号隔开。

throws使用规则

1、如果是不可查异常,即Error、RuntimeExecption或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。

2、如果一个方法中可能出现可查异常,要么用try-catch语句捕获,要么用throws子句中声明将它抛出,否则会导致编译错误。

3、当抛出了异常,则该方法的调用者必须处理或者重新抛出该异常。

4、当子类重写父类抛出异常的方法时,声明的异常必须是父类方法所声明异常的同类或子类。

public static void main(String[] args) {
   /*try {
      // TODO Auto-generated method stub
      int result = test();
      System.out.println("one和two的商是:" + result);
   } catch (ArithmeticException e) {
      System.out.println("除数不允许为零");
      e.printStackTrace();
   }catch(InputMismatchException e){
      System.out.println("请输入整数");
      e.printStackTrace();
   }*/
   try{
      int result = test();
      System.out.println("one和two的商是:" + result);
   }catch(ArithmeticException e){
      
   }catch(InputMismatchException e){
      
   }catch(Exception e){
      
   }
   int result2=test();
}

/**
	 * 测试接收数据相除结果的方法
	 * @return 两个接收数据的商
	 * @throws ArithmeticException
	 * @throws InputMismatchException
	 */
	public static int test() throws ArithmeticException,InputMismatchException{
		Scanner input = new Scanner(System.in);
		System.out.println("=====运算开始=====");
		System.out.print("请输入第一个整数:");
		int one = input.nextInt();
		System.out.print("请输入第二个整数:");
		int two = input.nextInt();
		System.out.println("=====运算结束=====");
		return one / two;
	}

throw

throw用来抛出一个异常,throw抛出的只能是可抛出类Throwable或者及其子类的实例对象。

throw抛出异常对象的处理方案:

  • 1、通过try..catch包含throw语句--自己抛自己处理
  • 2、通过throws在方法声明出抛出异常类型--谁调用谁处理--调用者可以自己处理,也可以继续上抛

  • throw抛出异常对象的处理方案:

  • 1、通过try..catch包含throw语句--自己抛自己处理

  • 2、通过throws在方法声明出抛出异常类型--谁调用谁处理--调用者可以自己处理,也可以继续上抛

  • 此时可以抛出与throw对象相同的类型或者其父类。

  • 描述酒店的入住规则:限定年龄,18岁以下,80岁以上的住客必须由亲友陪同

//方法一:通过try..catch包含throw语句--自己抛自己处理
public static void main(String[] args) {
        testAge();
    }
    
     public static void testAge() {
       try {
          System.out.println("请输入年龄:");
          Scanner input = new Scanner(System.in);
          int age = input.nextInt();
          if (age < 18 || age > 80) {
             throw new Exception("18岁以下,80岁以上的住客必须由亲友陪同");
          } else {
             System.out.println("欢迎入住本酒店");
          }
       } catch (Exception e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
       }
    }

方法二:通过throws在方法声明出抛出异常类型--谁调用谁处理--调用者可以自己处理,也可以继续上抛
 public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            testAge();
        } catch (Exception e) {
            System.out.println("18岁以下,80岁以上的住客必须由亲友陪同");
            e.printStackTrace();
        }
    }

    public static void testAge() throws Exception {
        System.out.println("请输入年龄:");
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        if (age < 18 || age > 80) {
            throw new Exception();
        } else {
            System.out.println("欢迎入住本酒店");
        }
    }

throw和throws的区别

Java语言通过异常处理机制来解决运行期间产生的异常。良好的异常处理设计,可以降低错误的程序代码或系统错误所造成的不可预期的损失,增强程序的灵活性、可读性和健壮性。

当在设计中,我们希望有针对性的产生、抛出异常,由上级调用者视情况处理时,就需要使用throw、throws了。

throw

语法:throw 异常对象;或throw new 异常类型(参数列表);

说明:

1、一般在代码块的内部,当程序出现某种逻辑错误时,由程序员主动抛出某种特定类型的异常。

2、语句定义在方法体内,只能抛出一个异常对象。

3、抛出的异常可以在方法内,自行通过try...catch...finally进行处理,也可以借由throws通知方法调用者,应用时再进行处理。

4、通过throw抛出的异常是一定会产生的。

5、如throw抛出的是CheckedExecption对象,且并未进行任何处理,会编译报错;如抛出的是UncheckedExecption对象,则默认不会产生错误提醒。当然,更推荐进行处理操作,以避免后续不必要的失误。

6、方法中,throw和return都会触发方法中断操作,因此在未加入判断的情况下,不可能同时出现。

throws

语法:throws 异常类型列表

说明:

1、表示通知方法调用者,使用该方法时,可能会发生哪些异常,需要进行相关处理。

2、语句设置在方法参数列表胡,throws后可以跟着多个异常类型名,表示抛出的异常之间使用逗号隔开。

3、表现一种产生异常的可能性,但不一定会发生。

自定义异常

异常类的使用案例

创建

//创建一个自定义异常类
public class HotelAgeException extends Exception {
    public HotelAgeException(){
        super("18岁以下,80岁以上的住客必须由亲友陪同");
    }
}

使用

public static void main(String[] args) {
    try {
        testAge();
    } catch (HotelAgeException e) {
        System.out.println(e.getMessage());
        System.out.println("酒店前台工作人员不允许办理入住登记");
    }catch(Exception e){
        e.printStackTrace();
    }
}

public static void testAge() throws HotelAgeException {
    System.out.println("请输入年龄:");
    Scanner input = new Scanner(System.in);
    int age = input.nextInt();
    if (age < 18 || age > 80) {
        throw new HotelAgeException();
    } else {
        System.out.println("欢迎入住本酒店");
    }
}

运行结果

异常类的常见问题

1、自定义异常属于检查异常还是非检查异常?
这要看在定义自定义异常类时所继承的父类,如果父类属于检查异常,则自定义异常也就是检查异常,反之亦然。

2、通过throw new Exception(描述信息);也能输出自己定义的错误信息,跟自定义异常类是一样的么?
不一样的,通过throw new Exception(描述信息);完成的是实例化Exception类型对象,并针对其异常描述信息进行赋值的操作,这种操作比较适合进行临时或者应用频率不高的异常处理情况;

而通过自定义异常类,完成的是通过继承自某种已存在异常类型,创建一个独特的,结合业务产生的类型,并设置其异常描述信息,这种操作更加适合该异常将在项目中相对频繁出现并应用的场景。

3、getMessag( )、toString()和printStackTrace( ) 在异常处理中的区别是什么?
e.toString():获得异常类型和描述信息,当直接输出对象e时,默认调用e.toString()方法。
e.getMessage() : 获得异常描述信息。
e.printStackTrace():打印出异常产生的堆栈信息,包括种类、描述信息、出错位置等。

4、自定义异常只能抛出后(throw)后才能卸载catch里吗?
是的,自定义机场需要先经过throw抛出,才能被catch捕获,是无法自动被程序捕获并处理的。

异常链

异常链使用案例

public static void main(String[] args) {
      try {
         testThree();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public static void testOne() throws HotelAgeException {
      throw new HotelAgeException();
   }

   public static void testTwo() throws Exception {
      try {
         testOne();
      } catch (HotelAgeException e) {
         throw new Exception("我是新产生的异常1",e);
      }
   }

   public static void testThree() throws Exception {
      try {
         testTwo();
      } catch (Exception e) {
          throw new Exception("我是新产生的异常2",e);
      }
   }

运行结果:

总结

异常

异常处理

throw & throws

自定义异常

异常链

posted @ 2022-11-15 18:49  吃星星的大黄鸭  阅读(871)  评论(0编辑  收藏  举报