异常
异常的概念
概念:在java代码运行过程中,遇到不正常的事件,从而导致代码运行中断的就是异常。
异常的分类
看图理解:
Throwable类下有两个类:Erro类和Exception类
Erro类:java程序执行过程中发生错误且无法解决,则退出执行。例如:java.lang.StackOverFlowErro
Exception类又分为两个类:
- Exception直接子类:称为编译时异常/受控异常/检查异常。java中规定,此类异常在java编译阶段必须处理。否则编译无法通过。
- RunTimeException:称为运行时异常/非受控异常/未检查异常。java中规定,运行时异常可以不在编译阶段处理也能通过编译。
异常的处理
处理异常的关键字:
-
throw:抛出异常
-
throws:上抛异常
-
try:监控异常代码,配合catch和finally使用
-
catch:捕捉异常,需要配合try使用
-
finally:不论结果如何最终finally中的代码总会输出
异常的处理方法: -
try…catch:捕捉异常
try{
这里写要监控的代码,也就是可能出现异常的代码
}catch(这里写可能出现的异常名称 变量名){
e.printStackTrace();//这个方法表示输出异常详细信息
e.getMessage();//这个方法表示获取异常简单信息,注意,这是获取,不会输出
}
例如:按照控制台提示输入1~3之间任一个数字,程序将输出相应的课程名称根据键盘输入进行判断。如果输入正确,输出对应课程名称。如果输入错误,给出错误提示
/**
* 课程查询
*/
public class CurriculumQuery {
public static void main(String[] args) {
//创建键盘扫描器
Scanner s = new Scanner(System.in);
//课程代号
int cno;
System.out.print("请输入课程代号(1~3之间的数字):");
//判断输入是否正确,正确则输出课程,错误则输出提示信息
try {
cno = s.nextInt();
judgeCno(cno);
}catch (InputFigureException e) {
//给出详细提示
//e.printStackTrace();
//给出简单提示
System.out.println(e.getMessage());
}finally {
System.out.println("欢迎提出建议");
}
}
/**
* 判断输入是否正确,错误则返回提示。正确则输出对应课程
* @param cno : 课程代号
* @throws InputFigureException : 输入错误异常提示
*/
public static void judgeCno(int cno) throws InputFigureException{
if (cno > 3 || cno < 1 ){
throw new InputFigureException("输入的数字不在范围内");
}else{
if (cno == 1){
System.out.println("C#编程");
}else if (cno == 2){
System.out.println("Java编程");
}else if (cno == 3){
System.out.println("Python编程");
}
}
}
}
/**
* 输入数字异常
*/
class InputFigureException extends Exception{
//无参构造
public InputFigureException() {
}
//有参构造
public InputFigureException(String message) {
super(message);
}
}
- throws上抛异常
即自身不处理异常,而是将异常抛出给调用类或jvm。
例如上面代码中的方法:
划线处就是上抛异常,将异常抛给调用这个方法的调用者。让调用者去处理,若是调用者无法处理可以继续上抛直到抛给jvm。(我是这样理解的,应该是最多抛到jvm)
自定义异常
自定义异常步骤:
- 自定义异常类需要继承一个异常类作为父类
- 自定义异常类中的构造方法使用父类中的构造方法
补充知识
- try…catch…finally中catch和finally都必须和try在一起才能使用,否则报错。
单独catch ——> 报错
catch(){}
单独finally ——> 报错
finally{}
catch和finally ——> 报错
catch(){}finally{}
- 关于finally代码块的执行顺序问题:finally代码块中的代码无论如何最后都会被执行,那么有没有例外呢?
当然有啊!!!
先看这个代码:
public class Test {
public static void main(String[] args) {
int a = 10;
int b =0;
int c = 0;
try{
c = a/b;
}catch (Exception e){
e.printStackTrace();
}finally{
System.out.println("报错了");
}
}
}
看看输出结果是什么:这个时候finally代码块中的代码是执行了的。
再看看这个代码:
public class Test {
public static void main(String[] args) {
int a = 10;
int b =0;
int c = 0;
try{
c = a/b;
}catch (Exception e){
e.printStackTrace();
System.exit(0);
}finally{
System.out.println("报错了");
}
}
}
输出结果:
可以看到,此时finally代码块中的代码就没有执行了。
System.exit(0):表示的是终止当前运行的java虚拟机。查文档可以查到。
我们再来看另一种情况:
看代码:问输出的i是多少?
答案:11
public class T {
public static int show() {
int i = 0;
try{
i = 10;
}finally {
i++;
}
return i;
}
public static void main(String[] args) {
System.out.println(show());
}
}
再看代码:
此时输出的是多少?
答案:10
public class T {
public static int show() {
int i = 0;
try{
i = 10;
return i;
}finally {
i++;
}
}
public static void main(String[] args) {
System.out.println(show());
}
}
为什么呢?
我知道肯定你们会说因为代码自上而下执行啊。当执行到return i;之后,就返回 i 结束方法了。finally不会执行。实时真是如此吗?
看看这个代码:
public class T {
public static int show() {
int i = 0;
try{
i = 10;
return i;
}finally {
i++;
System.out.println("finally输出:"+i);
}
}
public static void main(String[] args) {
System.out.println(show());
}
}
输出结果:
显而易见,finally代码块最终还是会执行的。那到底为什么返回的是10 呢?我看了很多帖子,我不能说他们讲的不好,但是我敢说的是,我讲的更清楚。其实很简单,看图就完了:
看蓝色行,蓝色行就是当前执行行。我们通过打断点debug测试,看看代码的执行顺序。
首先,代码main方法执行,调用show()方法,这没问题吧。
进入show()方法
按照自上而下的顺序一次执行
注意,这里执行了return
继续执行finally代码块
输出11
划重点,发现没有,执行完finally代码块后回回到第7行 return i 这里。然后结束show()方法
show()方法结束,返回值赋值给o。
这时候发现,其实o变量存储的值是10。
看完流程。我们就可以得到一个结论了。
结论:在finally前面存在return的时候,return会先将值返回,然后程序继续往下执行,当finally代码块中的代码执行完毕后将会再次返回到return行执行return结束方法的功能,但此时的return不会再返回值。return再整个过程中被分为了两部分,先传值,然后等待finally代码块的执行,再结束方法。
关于finally和return的执行还有一点:finally代码块外面有return并且位置处于finally代码块之上,同时finally代码块中还有return。此时我们可以忽略finally代码块之上的return。因为最终执行并返回的是finally代码块中的return。文字很抽象,但是咱还是老办法。打断点debug。
public class Test {
public static int show() {
int i = 0;
try{
i = 10;
return i;
}finally {
i++;
System.out.println("finally输出:"+i);
return i;
}
}
public static void main(String[] args) {
int o = show();
System.out.println(o);
}
}
图是在太多,我就直接标号给你们了
看顺序,你会发现第7行,也就是第4个执行的代码 return i;其实确确实实执行了,按照上面我说法,先执行的是返回值,然后代码继续向下执行。当执行到finally代码块中的 return i ;时,也就是12行时,按照上面的说法,执行完finally代码块应该掉头回到第7行执行return语句的功能结束代码。但实际上却并不是如此。当finally代码块中存在return语句的时候,此时finally代码块中的return语句后面并不会像第7行的return语句一样将返回值和结束方法功能分开执行。它就是一个正常的return语句。也就是说,finally代码块中的return不仅返回了值还结束了方法。既然结束了方法,那么自然不会再回到第7行去执行return语句。同时,我们根据debug可以知道上面的return语句时执行过的,也就是返回过值,而下面的return又返回了一次,根据执行结果我们能够看出,下面的return返回的值覆盖了上面return的值。所以照我看来,当finally代码块中存在return语句的时候。finally代码块外面的return可以视作空气,忽视掉它。
今天的知识有点绕,大家好好消化。我也要好好消化一下。