前言
程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。JAVA提供了更加优秀的解决办法:异常处理机制。
一、Java异常概念
异常:通俗来说就是异于常态,和正常情况不一样,程序员需要关心和处理程序中可能出现异常的情况。
我们要怎么处理异常?
JAVA提供了一套完整的异常处理机制,可以让程序尽大可能的恢复正常并继续执行。
Java中的异常可以是方法中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的。
只要在Java程序中产生了异常,就会产生一个异常对象,JAVA就会试图寻找异常处理程序来处理异常。
二、异常的体系
Java标准裤内建了一些通用的异常,这些类以Throwable为顶层父类。
Throwable又派生出Error类和Exception类。
Error类:如java.lang.StackOverFlowError(栈溢出)和Java.lang.OutOfMemoryError(内存溢出)。Error很少出现(属于物理层面上的错误),程序员应该关注Exception为父类的分支下的各种异常类(逻辑层面的异常)。
Exception以及他的子类:代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
Exception又可分为运行时异常和检查异常(编译期异常)。
运行时异常: RuntimeException 以及他们的子类,javac在编译时不会提示和发现这样的异常,不要求在程序处理这些异常。
1.如除0错误ArithmeticException
2.错误的强制类型转换错误ClassCastException,
3.数组索引越界ArrayIndexOutOfBoundsException
4.使用了空对象NullPointerException等等。
检查异常:javac强制要求程序员为这样的异常做预备处理工作,要么在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。
1.如解析异常:ParseException
2.编码异常:UnsupportedEncodingException
三、异常处理
异常处理机制:JAVA使用异常机制为程序提供纠错能力,通过try,catch,finally,throw,throws五个关键字实现。
1.try,catch,finally:捕获异常,并进行处理。
try{
//1.try块中放可能发生异常的代码。
//2.如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
//3.如果发生异常,则尝试去匹配catch块。
}catch (ArithmeticException s){
//1.每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。
//2.可以有多个catch,如果异常与之匹配则虚拟机将使用这个catch块来处理异常。
//3.如果try中没有发生异常,则所有的catch块将被忽略。
}catch(Exception e){
//...
}finally{
//1.finally块通常是可选的。
//2.无论异常是否发生,异常是否匹配被处理,finally都会执行。
//3.一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的, finally不会捕获异常。
//4.finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
}
public static int test() {
try {
int a = 10;
int b = 1;
int c = a / b;
return c;
} catch (ArithmeticException e) {
System.out.println("算数运算出错");
return -1;
} finally { //即使return了也能执行finally的代码
System.out.println("aaaaaaaaa");
}
}
public static void test1() {
try {
int a = 10;
int b = 0;
// int c = a / b;
}finally { //即使不处理异常也能执行finally里面的代码
System.out.println("执行finally代码块");
}
}
注意:
1.try块中的局部变量和catch块中的局部变量以及finally中的局部变量,他们之间不可共享使用。
2.应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。
3.finally块中的return将会覆盖别处的return语句。
4.finally中的异常会覆盖(消灭)前面try或者catch中的异常。
2.throw和throws关键字
1throw:抛出异常,手动抛出异常,throw语句的后面必须是一个异常对象。
public void test(int age){
if(age<0){
throw new IllegalArgumentException("年龄不能小于0");
}
}
2.throws:声明异常,声明方法可能抛出的各种异常,交给上一级处理,调用者可以捕获或者继续声明该异常,交给上一级继续处理。
//throws 一般处理编译期异常 交给上层来处理
public static void test3() throws ParseException, UnsupportedEncodingException {
SimpleDateFormat myDateFormat=new SimpleDateFormat("yyyy-MM-dd");
myDateFormat.parse("2021-053");
String str="aaaa";
str.getBytes("GBK");
}
采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。
注意:子类重写父类的方法,子类不能抛出比父类更大的异常。(针对编译期异常)
throws和throw的区别:
1.thorw用于方法体,用来抛出一个实际异常,要么捕获,要么用throws声明异常。
2.throws:声明异常,声明方法可能抛出的各种异常
四、自定义异常
自定义异常:当JAVA提供的异常类不能满足我们的需求的时候,我们可以写一个自定义异常,通常都是通过继承一个异常类来实现。
那么要怎么使用自定义异常呢?
1.要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常。(checked exception)
2.按照国际惯例,自定义的异常应该总是包含如下的构造函数。
1.一个无参构造函数
2.一个带有String参数的构造函数,并传递给父类的构造函数。
public class ScoreException extends Exception{ //自定义异常,继承Exception
public ScoreException(){
super(); //调用父类的方法
}
public ScoreException(String message){ //自定义异常类中往往不写其他方法,只重载需要使用的构造方法
super(message);
}
}
public class ScoreTest {
public static void main(String[] args) {
try {
test(101);
} catch (ScoreException e) {
e.printStackTrace();
}
}
public static void test(int score) throws ScoreException {
if(score>100){
throw new ScoreException("分数输入超过100"); //throw用于方法体中,用来抛出一个实际的异常对象
}
}
}
五、总结
有时候,我们会在编写代码中,碰到很多将会导致发生异常的情况,你不可能都知道是什么情况引发的,那么此时就用异常处理解决。一旦它发生异常,我们就可以进行处理,修正程序。使用异常类的好处就在于它为程序调试提供了很大的方便,并能保证程序在出现异常的情况下仍然能够继续执行下去,从而提高了程序的健壮性。