在计算机程序运行的时候,总会出现各总各样的错误,有一些是人为造成的,有一些是随机出现的。现在我们就来说说Java的异常处理,有啥问题请指出,谢谢。
什么异常
Java 中的异常(Exception)又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。Java的异常是class,并且从Throwable继承。
在一个程序中,有些错误是用户造成的,比如,我们希望用户输入一个int类型的身高,但是用户输入的是字符:
//假设用户输入了abc
String s = "abc";
int n = Integer.parseInt(s);
//NumberFormatException
还有一些则是随机出现的,并且是不可能避免的。比如:
- 断网了,连接不到服务器
- 内存不足,程序崩溃了
- …
所以一个程序必须处理各种各样的错误。Java内置了一套异常处理机制,总是使用异常来表示错误。
Throwable
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。异常和错误的区别是:异常能被程序本身可以处理,错误是无法处理。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。
Java规定:
- 必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常成为Checked Exception。
- 不需要捕获的异常,包括error及其子类,RuntimeException及其子类。
注意:编译器对RuntimeException及其子类不做强制捕获要求,不是指应用程序本身不应该捕获并处理RuntimeException。是否需要捕获,具体问题具体分析。
捕获异常
try…catch
在java中,凡是可能抛出异常的语句都可以使用try…catch语句,把可能发成异常的代码块放到try{…}中,然后使用catch捕获对应的Exception及其子类。
try {
// 可能会发生异常的程序代码
} catch (Type1 one){
// 捕获并处置try抛出的异常类型Type1
}
catch (Type2 two){
//捕获并处置try抛出的异常类型Type2
}
public class Main {
public static void main(String[] args) {
String a = "12";
String b = "x9";
// 捕获异常并处理
int c = stringToInt(a);
int d = stringToInt(b);
System.out.println(c * d);
}
static int stringToInt(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
System.out.println("Bad input");
return 0;
}
}
}
结果:Bad input
0
多catch语句
在异常处理中,可以使用多个catch语句,每个catch分别捕获对于的Exception及其子类。JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配。简单的说就是:多个catch语句中只能有一个能被执行。如:
public static void main(String[] args) {
try{
one();
two();
three();
}catch(IOException e){
System.out.println(e);
}catch(NumberFormatException e){
System.out.println(e);
}
}
存在多个catch的时候,catch的顺序非常重要:**子类必须写在前面。**否则将永远捕获不到子类。
finally语句
不管有没有异常,finally中的代码都会执行。这里就说一下try…catch…finally语句
try {
// 可能会发生异常的程序代码
} catch (Type1 one) {
// 捕获并处理try抛出的异常类型Type1
} catch (Type2 two) {
// 捕获并处理try抛出的异常类型Type2
} finally {
// 无论是否发生异常,都将执行的语句块
}
public static void main(String[] args) {
try{
one();
two();
three();
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("数组下标越界异常");
}catch(IOException e){
System.out.println("IO error");
}finally{
System.out.println("End");
}
}
以下是我在牛客刷题的时候看到一位大佬的总结
结论
- 不管有没有异常,finally中的代码都会执行
- finally语句不是必须的,可写可不写
- 当try、catch中有return时,finally中的代码依然会继续执行
- finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。
- finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
举例:
-
情况1:try{}catch{}finally{} return;
程序正常进行 -
情况2:try{return;}catch{}finally{} return;
a、先执行try中的语句,包括return后面的表达式,
b、然后执行finally中的语句,
c、最后执行try中的return
ps:返回值是try中的return后面的表达式的值,finally后面的return语句不会被执行 -
情况3:try{} catch{return;} finally{} return;
先执行try中的代码块
有异常:
a、先执行catch中的语句,包括return后面的表达式;
b、然后执行finally中的语句;
c、最后执行catch中的return,finally后面的return不会被执行
无异常:执行finally中的代码块,然后执行return语句 -
情况4:try{return;} catch{} finally{return;}
a、先执行try中的语句,包括return后面的表达式;
b、然后执行finally中的语句;
c、最后执行finally中的return 。
ps:返回的值是finally中return后面的表达式的值,因为finally中有return语句,所以会提前退出 -
情况5:try{} catch{return} finally{return};
先执行try中的代码块
有异常:
a、先执行catch中的语句,包括return后面的表达式;
b、然后执行finally中的语句;
c、最后执行finally中的return,因为finally中有return语句,所以会提前退出
无异常:执行finally中的代码块,然后执行finally中的return -
情况6:try{return;} catch{return;} finally{return;}
先执行try中的代码块,包括return后面的表达式
有异常:
a、先执行catch中的语句,包括return后面的表达式;
b、然后执行finally中的语句;
c、最后执行finally中的return,因为finally中有return语句,所以会提前退出
无异常:执行finally中的代码块,然后执行finally中的return
最总结论:在执行try、catch中的return之前一定会执行finally中的代码(如果finally存在),如果finally中有return语句,就会直接执行finally中的return方法,所以finally中的return语句一定会被执行的。编译器把finally中的return语句标识为一个warning.
抛出异常
当发生错误时,任何Java代码都可以抛出异常。抛出异常分两步:
- 创建某个Exception的实例
- 用throw或throws语句抛出
以下两种方法是引用一位博主的看法(其实是自己懒得写了,这位博主写的很好,结尾我会放上那篇博客的链接)
throws抛出异常
如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。
throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。throws语句的语法格式为:
methodname throws Exception1,Exception2,..,ExceptionN
{
}
使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。
pop方法没有处理异常NegativeArraySizeException,而是由main函数来处理。
Throws抛出异常的规则:
- 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
- 必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
- 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
- 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
void method1() throws IOException{} //合法
//编译错误,必须捕获或声明抛出IOException
void method2(){
method1();
}
//合法,声明抛出IOException
void method3()throws IOException {
method1();
}
//合法,声明抛出Exception,IOException是Exception的子类
void method4()throws Exception {
method1();
}
//合法,捕获IOException
void method5(){
try{
method1();
}catch(IOException e){…}
}
//编译错误,必须捕获或声明抛出Exception
void method6(){
try{
method1();
}catch(IOException e){throw new Exception();}
}
//合法,声明抛出Exception
void method7()throws Exception{
try{
method1();
}catch(IOException e){throw new Exception();}
}
使用throw抛出异常
throw总是出现在函数体中,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
我们知道,异常是异常类的实例对象,我们可以创建异常类的实例对象通过throw语句抛出。该语句的语法格式为:
throw new exceptionname;
例如抛出一个IOException类的异常对象:
throw new IOException;
要注意的是,throw 抛出的只能够是可抛出类Throwable 或者其子类的实例对象。下面的操作是错误的:
throw new String(“exception”);
这是因为String 不是Throwable 类的子类。
如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。
如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。
package Test;
import java.lang.Exception;
public class TestException {
static int quotient(int x, int y) throws MyException { // 定义方法抛出异常
if (y < 0) { // 判断参数是否小于0
throw new MyException("除数不能是负数"); // 异常信息
}
return x/y; // 返回值
}
public static void main(String args[]) { // 主方法
int a =3;
int b =0;
try { // try语句包含可能发生异常的语句
int result = quotient(a, b); // 调用方法quotient()
} catch (MyException e) { // 处理自定义异常
System.out.println(e.getMessage()); // 输出异常信息
} catch (ArithmeticException e) { // 处理ArithmeticException异常
System.out.println("除数不能为0"); // 输出提示信息
} catch (Exception e) { // 处理其他异常
System.out.println("程序发生了其他的异常"); // 输出提示信息
}
}
}
class MyException extends Exception { // 创建自定义异常类
String message; // 定义String类型变量
public MyException(String ErrorMessagr) { // 父类方法
message = ErrorMessagr;
}
public String getMessage() { // 覆盖getMessage()方法
return message;
}
}
Java常见异常
序号 | 异常名称 | 异常描述 |
---|---|---|
1 | java.lang.ArrayIndexOutOfBoundsException | 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。 |
2 | java.lang.ArithmeticException | 算术条件异常。如:整数除零等。 |
3 | java.lang.SecurityException | 安全性异常 |
4 | java.lang.IllegalArgumentException | 非法参数异常 |
5 | java.lang.ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
6 | java.lang.NegativeArraySizeException | 数组长度为负异常 |
7 | java.lang.NullPointerException | 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。 |
8 | IOException | 操作输入流和输出流时可能出现的异常 |
9 | EOFException | 文件已结束异常 |
10 | FileNotFoundException | 文件未找到异常 |
11 | ClassCastException | 类型转换异常类 |
12 | ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
13 | SQLException | 操作数据库异常类 |
14 | NoSuchFieldException | 字段未找到异常 |
15 | NoSuchMethodException | 方法未找到抛出的异常 |
16 | NumberFormatException | 字符串转换为数字抛出的异常 |
17 | StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 |
18 | IllegalAccessException | 不允许访问某类异常 |
19 | InstantiationException | 当应用程序试图使用Class类中的newInstance()方法创建;一个类的实例,而指定的类对象无法被实例化时,抛出该异常 |
20 | java.lang.ClassNotFoundException | 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。 |
自定义异常
我们在一个大型项目中,可以自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的。
一个常见的的做法是自定义一个BaseException作为“根异常”,然后派生出各种类型的异常。
BaseException需要从一个合适的Exception派生,通常建议从RuntimeException派生:
public class BaseException extends RuntimeException {
}
其他类型异常就可以从BaseException派生:
public class BaseException extends RuntimeException {
}
public class LoginFailedException extends BaseException{
}
自定义的BaseException应该提供多个构造方法:
public class BaseException extends RuntimeException {
public BaseException() {
super();
}
public BaseException(String message, Throwable cause) {
super(message,cause);
}
public BaseException(String message) {
super(message);
}
public BaseException(Throwable cause) {
super(cause);
}
}
引用链接:https://blog.csdn.net/hguisu/article/details/6155636?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160232221319724839245050%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160232221319724839245050&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v3~pc_rank_v2-2-6155636.first_rank_ecpm_v3_pc_rank_v2&utm_term=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&spm=1018.2118.3001.4187