异常概述
异常指在程序的运行过程中所发生的不正常事件。如所需文件找不到、网络连接不通或连接中断、数组下标越界等。异常会中断正在运行的程序。
在Java的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。
所谓异常处理,就是指程序在出现问题时依然可以正确的执行完。
下图为被0除时程序运行发生异常:
如程序实现用户控制台输入被除数和除数,计算结果并输出商。
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入被除数:");
double num1 = sc.nextDouble();
System.out.print("请输入除数:");
double num2 = sc.nextDouble();
System.out.println(num1+"除以"+num2+"的结果为:"+num1/num2);
}
}
当输入正确时:
当输入错误时:
从运行结果可以看出,一旦出现异常程序将会立即结束。
使用条件语句来对异常情况进行处理:
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入被除数:");
double num1 = 0.0;
if(sc.hasNextDouble()){//如果输入的是double类型
num1 = sc.nextDouble();
}else {
System.out.println("程序退出!");
System.exit(1);//结束程序
}
System.out.print("请输入除数:");
double num2 = 0.0;
if(sc.hasNextDouble()){//如果输入的是double类型
num2 = sc.nextDouble();
if (0==num2){
System.out.println("输入的除数不能为0,程序退出!");
System.exit(1);//结束程序
}
}else {
System.out.println("程序退出!");
System.exit(1);//结束程序
}
System.out.println(num1+"除以"+num2+"的结果为:"+num1/num2);
}
}
使用条件语句进行异常处理缺点:
-
代码臃肿,加入了大量的异常情况判断和处理代码。
-
程序员把相当多的时间放在了异常处理的代码上,影响开发效率。
-
很难穷举所有的异常情况,程序任旧不健壮。
-
异常处理代码和业务代码交织在一起,影响代码的可读性,加大日后程序维护难度。
所以Java提供了异常处理机制。
异常处理机制
Java是采用面向对象的方式来处理异常的。处理过程:
-
抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
-
捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
Java异常类层次
JDK中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception。Java异常类的层次结构如下图所示。
异常 | 说明 |
---|---|
Exception | 异常层次结构的根类 |
ArithmeticException | 算术错误异常,如以0作为除数 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问null对象成员 |
ClassNotFoundException | 不能加载所需的类 |
InputMismatchException | 欲得到的数据类型与实际输入的类型不匹配 |
IllegalArgumentException | 方法接受到非法参数 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把“abc”转换成数字 |
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作“引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。
异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。通常,Java的异常(包括Exception和Error)分为可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。
Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
-
运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、**IndexOutOfBoundsException(下标越界异常)**等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
-
非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
Java异常处理:Java采用异常处理机制,将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁,并易于维护。
-
Java提供的是异常处理的抓抛模型。
-
Java程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
-
如果一个方法内抛出异常,该异常会被抛到调用方法中。如果异常没有在调用方法中处理,它继续被抛给这个调用方法的调用者。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。
-
如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。
-
程序员通常只能处理Exception,而对Error无能为力。
try-catch-finally
异常处理是通过try-catch-finally语句实现的。
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
[ finally{
...... //无条件执行的语句
} ]
-
try,捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
-
catch (Exceptiontype e),在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
-
捕获异常的有关信息:与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
-
getMessage():用来得到有关异常事件的信息。
-
printStackTrace():用来跟踪异常事件发生时执行堆栈的内容。
-
-
finally捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。不论在try、catch代码块中是否发生了异常事件,finally块中的语句都会被执行。finally语句是可选的。
throws
声明抛出异常是Java中处理异常的第二种方式。
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显式地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
在方法声明中用 throws 子句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
public void readFile(String file) throws FileNotFoundException {
//......
// 读文件的操作可能产生FileNotFoundException类型的异常
FileInputStream fis = new FileInputStream(file);
//......
}
重写方法声明抛出异常的原则:重写方法不能抛出比被重写方法范围更大的异常类型。
public class A {
public void methodA() throws IOException {
//......
}
}
public class B1 extends A {
public void methodA() throws FileNotFoundException {
//......
}
}
public class B2 extends A {
public void methodA() throws Exception { //error
//......
}
}
throw
在Java语言中,可以使用throw关键字来自行抛出异常。在方法内部通过throw抛出异常。
public class Person {
private String name = "";
private int age = 0;
private String sex = "男";
public void setSex(String sex) throws Exception{
if(sex.equals("男")||sex.equals("女")){
this.sex = sex;
}else {
throw new Exception("性别必须为男或者女!");
}
}
public void print(){
System.out.println("性别是:"+this.sex);
}
//测试代码
public static void main(String[] args) {
Person p = new Person();
try {
p.setSex("Male");
p.print();
}catch (Exception e){
e.printStackTrace();
}
}
}
throw和throws的区别
1、作用不同:throw用于程序员自行产生并抛出异常,throws用于声明该方法内抛出了异常。
2、使用的位置不同:throw位于方法体内部,可以作为单独语句使用;throws必须跟在方法参数列表的后面,不能单独使用。
3、内容不同:throw抛出一个异常对象,只能是一个;throws后面跟异常类,可以跟多个。
try-with-resource自动关闭Closable接口的资源
Java中,JVM的垃圾回收机制可以对内部资源实现自动回收,给开发者带来了极大的便利。但是JVM对外部资源(调用了底层操作系统的资源)的引用却无法自动回收,例如数据库连接,网络连接以及输入输出IO流等。这些连接就需要我们手动去关闭,不然会导致外部资源泄露,连接池溢出以及文件被异常占用等。
JDK7之后,新增了”try-with-reasource”。它可以自动关闭实现了AutoClosable接口的类,实现类需要实现close()方法。”try-with-resources声明”,将try-catch-finally简化为try-catch,这其实是一种语法糖,在编译时仍然会进行转化为try-catch-finally语句。
public class Test {
public static void main(String[] args) {
try (FileReader reader = new FileReader("d:/a.txt")) {
char c = (char) reader.read();
char c2 = (char) reader.read();
System.out.println("" + c + c2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
自定义异常
用户自定义异常类MyException,用于描述数据取值范围错误信息。用户自己的异常类必须继承现有的异常类。
步骤:
1、定义异常类,并继承Exception或者RuntimeException。
2、编写异常类的构造方法,并继承父类的实现。
class MyException extends Exception {
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
受检与非受检异常
受检异常:Exception(编译器异常)
定义方法时必须声明所有可能会抛出的exception;在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;exception是从java.lang.Exception类衍生出来的。例如:IOException,SQLException就属于Exception。
非受检异常:RuntimeException(运行时异常)
在定义方法时不需要声明会抛出runtime exception;在调用这个方法时不需要捕获这个runtime exception;runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。例如:NullPointException,IndexOutOfBoundsException就属于runtime exception。
评论区