一 .异常的基本概念和作用
所谓异常就是Java在运行过程中出现的错误。如用户输入了错误的数据文件无法打开等操作。由于异常事件的发生会导致严重的错误,一个好的程序应该考虑到这些错误情况,并有相应的处理手段,这就是程序的“健壮性”。
使用异常处理机制的三个优势。
- 在使用传统语言编程,我们只能通过函数的返回值知道错误信息,因此需要些很多的 if-else 之类的判断语句,而且这些判断语句通常是嵌套的,导致程序可读性降低,代码也难于维护。
采用异常处理机制后,写程序时可以认为不会发生异常,一直按照正常的程序处理流程写下去,知道最后在捕获异常并进行相应的处理手段就可以了 - 由于函数只有一个返回值,所以难以区分到底是正常值还是错误信息的代码。
采用异常处理机制后则不会发生这种情况。一旦有错误发生,被调用的方法会抛出异常。不用去判断到底是那一步错了。 - 在传统语言中,错误代码需要调用链上的函数一层一层返回。例如有这样的调用链:A->B->C->D,如果D发生错误,将返回一个错误代码,如果C和B不处理这个错误,就必须将这个错误返回给上一级,。如果函数编写者忘了编写这一项,函数A就不会得到错误信息。
采用异常处理机制后,在D中抛出的异常就会存放在异常栈中,如果C和B不处理,仍然会传递给A,极端情况下即使A不处理它,系统也会处理它。
二 .异常的继承体系图如下
Java中有两种不同的异常处理方式:
- 一种是自己利用Java异常处理机制将该问题处理,然后继续执行。
- 另一种是自己没有针对的处理方式,交给调用main的jvm来处理(jvm有一个默认的异常处理机制,将该异常处理,并将该异常的名称,信息,以及异常出现的位置打印在控制台上同时将程序停止执行)。
异常
三 .异常处理方式
- try…catch…finally
- throws
try…catch处理异常的基本格式
try {
可能出现问题的代码 ;
}catch(异常名1 变量名){
针对问题的处理 ;
}catch(异常名2 变量名){
针对问题的处理 ;
}
finally{
释放资源;
}
变形格式
try {
可能出现问题的代码 ;
}catch(异常名 变量名){
针对问题的处理 ;
}
在这里需要注意的时try…catch的方式可以处理多个异常,在捕获异常的时候能明确的异常尽量就明确,另外在捕获异常时平级关系的异常那个在前无所谓,但是如果出现了继承关系,那么父类必须在后面。
public class MyTest1 {
public static void main(String[] args) {
int a = 10;
int b = 0;
int[]c = {1,2};
c = null;
try{
System.out.println(c[3]);
System.out.println(a / b);
}catch (ArithmeticException e){
System.out.println("除数为0了");
e.printStackTrace();//打印异常的堆栈信息
}catch (NullPointerException e){
System.out.println("空指针异常");
e.printStackTrace();
}catch (Exception e){
System.out.println("其他异常");
}
finally {
System.out.println("程序结束");
}
System.out.println("代码执行");
}
}
运行结果为:
空指针异常
java.lang.NullPointerException
程序结束
at org.westos.demo4.MyTest1.main(MyTest1.java:10)
代码执行
在上面的代码中经过实验,如果把除数为0与空指针异常调换位置,那么输出的就是除数为0异常,空指针异常就不会输出。因此当有多个异常时catch语句中只能捕获一种,另外可以发现finally中的代码时肯定会执行的,并且finally之后的代码也是会执行的。
JDK1.7之后推出了多个异常的处理方案
try {
可能出现问题的代码 ;
}catch(异常名1 | 异常名2 | .... 变量名){
对异常的处理方案 ;
}
优点:简化了代码。
缺点:对多个异常的处理方式是一致的,不能明确到底是哪一个出现了异常。
四 . Java中的异常通常分为两类:
- 编译期异常(非RuntimeException类及其子类的实例)
Java程序必须显示处理,否则程序就会发生错误,无法通过编译 - 运行期异常(RuntimeException类及其子类的实例)
无需显示处理程序依旧可以执行
throws的方式处理异常
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。
throw的方式处理异常
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
五 .编译期异常发生在编译期。必须处理通常有两种处理方式
- thorws把编译器异常跑出去给调用者,谁调用谁处理
- 捕获处理
编译器的注意事项
- 子类重写父类方法时,子类的方法必须抛出相同的异常,或者父类异常的子类,或者子类不抛出异常。
- 如果父类抛出了多个异常,子类重写父类方法是,只能抛出相同的异常或者其子类,子类不能抛出父类没有的异常,或者子类不抛出异常
- 如果被重写的方法没有抛出异常,那么子类的方法不可以抛出异常,如果子类产生异常,那么子类只能用try不能用throws抛出。
第一种抛出处理
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class MyTest3 {
public static void main(String[] args) {
try {
parse();
}catch (ParseException e){
e.printStackTrace();
}finally {
System.out.println("释放资源");
}
System.out.println(12345);
}
private static void parse() throws ParseException {
String str = "2019-5-17";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(dateFormat.parse(str));
}
}
运行结果为:
Fri May 17 00:00:00 CST 2019
释放资源
12345
第二种捕获处理
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class MyTest4 {
public static void main(String[] args) {
parseDate();
}
private static void parseDate() {
String str = "2019-4-27";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
System.out.println(dateFormat.parse(str));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
运行结果为:
Sat Apr 27 00:00:00 CST 2019
六 .运行时处理异常的处理方式
捕获处理
public class MyTest5 {
public static void main(String[] args) {
int a = 10;
int b = 0;
int[] arr = {1, 2};
try {
System.out.println(a / b);
System.out.println(arr[7]);
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (NullPointerException e1) {
e1.printStackTrace();
} finally {
System.out.println("释放资源");
}
System.out.println(123);
}
}
运行结果为:
java.lang.ArithmeticException: / by zero
释放资源
at org.westos.demo4.MyTest5.main(MyTest5.java:9)
123
七.自定义异常
在实际的开发中jdk不可能对每一个异常都有与之对应的异常类,因此需要我们自己定义异常类,该类继承自Exception 或者是RuntimeException。
例如判断成绩的范围是[0,100],此时就需要自己定义异常类。
public class NumException extends RuntimeException {
public NumException() {
}
public NumException(String s){
super(s);
}
}
import java.util.Scanner;
public class MyTest6 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的成绩0-100");
int score = sc.nextInt();
testScore(score);
}
private static void testScore(int score) {
if(score>100||score<0){
throw new NumException("成绩不合法");
}else{
System.out.println(score);
}
}
}
运行结果为:
请输入你的成绩0-100
99
99
八. throw和throws的区别
throw
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常的对象名,这个异常对象可以是编译器异常也可以是运行时异常,表示抛出的异常,由方法体内的语句处理。
- throw是抛出了异常,执行throw则一定是抛出了某种异常
throws
- 用在方法声明后,跟的是异常类名,可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的执行者进行处理
- throws表示的是抛出异常的一种可能性,并不一定会发生这些异常。
import java.util.InputMismatchException;
import java.util.Scanner;
public class MyTest2 {
public static void main(String[] args) {
while (true){
Scanner sc = new Scanner(System.in);
try{
System.out.println("请输入一个整数");
int i = sc.nextInt();
break;
}catch (InputMismatchException e){
System.out.println("你输入的格式不正确");
}
}
}
}
运行结果:
请输入一个整数
r
你输入的格式不正确
请输入一个整数
2