编译时异常和运行时异常
Exception
Java将异常类Exception分为两类:运行时异常(RuntimeException,也称为未检查异常)和非运行时异常(也称为已检查异常)。运行时异常包含Java. Lang. RunTimeException类以及所有子类,除此之外的属于Exception类及其子类的所有异常都属于非运行时异常。
简单来说就是继承RuntimeEcception的异常是运行时异常,继承Exception的异常是编译时异常(运行时异常)
运行时异常(RuntimeException及其子类)属于不可查异常,即编译器不要求强制处置的异常,然而对于编译时异常(运行时异常),编译器会强制要求进行处理。
无论是运行时异常还是编译时异常,异常都出现在运行时,不要被名字误导。
运行时异常:编译阶段不会报错,运行时可能出现错误。
编译时异常;编译阶段就会报错,必须处理,否则代码不通过,不能运行。
具体关系见下图:
异常在Jvm中实现原理
下面通过一些代码来演示:
有这一段代码:在main方法中,实例化一个对象,通过对象调用SE类的re方法。输出两个数的相除的结果。
package com.Exception;
public class SC {
public static void main(String[] args) {
SE se=new SE();
se.re(10,0);
}
}
class SE{
public int re(int a, int b){
int c;
c=a/b;
return c;
}
}
正常的我们知道除数不能为0,但是JVM只会执行我们的程序。但我们让除数为0时运行。在控制台就会给我们这样的异常提示:
这样的异常提示出来告诉了我们程序有问题的行数,还有这样的一句话:Exception in thread “main” java.lang.ArithmeticException Create breakpoint:/ by zera,其中ArithmeticException其实是一个类,既然是一个类,那么我们就可以实例化一个对象出来,然后通对象看是否能调用某些方法打印出什么。
当我们尝试这样做,并且运行时,控制台就出现了几乎一样的异常提示:唯独少了/ by aero
既然已经打印了出来,那么我们将/ by aero作为参数传进去,接着运行:
于是就是就出现了和开头我们一样的异常提示。但是在原代码里,main方法中没有像这样的代码,于是通过这个示例说明了,JVM先是在底层里new 了一个对象,然后通过对象调用出方法,最后才在控制台给我们显示出这样的异常提示。
异常处理方法
有下面这样一段代码:
package com.Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class SC1 {
public static void main(String[] arg) {
String sd;
int sf;
sd = "xiaoming";
sf = 18;
System.out.println("姓名" + sd + "年龄" + sf);
System.out.println("111111111111111111111");
ArithmeticException arithmeticException = new ArithmeticException("/ by aero");
arithmeticException.printStackTrace();
FileInputStream fileInputStream = new FileInputStream("D:Program File");
}
}
这段代码有两个异常:一个是运行时异常,另外一个是编译时异常,在软件里new FileInputStream(“D:Program File”);中的FileInputStream有红线标识,如果我们直接运行,可得到这样的结果;
这个就是编译时异常,只有异常提示,其他代码部分没有执行,说明我们必须处理,否则代码不通过,不能运行。这里说到必须对其进行捕获或声明以便抛出。
其实这就是处理异常的方法;捕获和抛出。
就像我们身边出现问题,要么自己解决要么一级一级往上报。
捕获可以理解为自己解决,抛出理解为一级一级往上报,有点类似甩锅一样。
我们将鼠标放在划红线的FileInputStream,可以找到系统也给我们了两个选择;
第一个就是添加异常方法标识,也就是声明的意思,第二个就是用try/catch将它围绕或者说包含起来
用上面代码处理为例:
声明以抛出:
package com.Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class SC1 {
public static void main(String[] arg)throws FileNotFoundException {
String sd;
int sf;
sd = "xiaoming";
sf = 18;
System.out.println("姓名" + sd + "年龄" + sf);
System.out.println("111111111111111111111");
ArithmeticException arithmeticException = new ArithmeticException("/ by aero");
arithmeticException.printStackTrace();
FileInputStream fileInputStream = new FileInputStream("D:Program File");
}
}
看到我们在main方法处加上了throws FileNotFoundException,然后运行程序;
可以看到程序能够正常运行,其他代码部分能够执行出来,说明抛出可以这样处理异常。
第二种方法:捕获——try…catch…
package com.Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class SC1 {
public static void main(String[] arg) {
String sd;
int sf;
sd = "xiaoming";
sf = 18;
System.out.println("姓名" + sd + "年龄" + sf);
System.out.println("111111111111111111111");
ArithmeticException arithmeticException = new ArithmeticException("/ by aero");
arithmeticException.printStackTrace();
try {
FileInputStream fileInputStream = new FileInputStream("D:Program File");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
看到我们将==FileInputStream fileInputStream = new FileInputStream(“D:Program File”);==写在了try…catch…里面。
再次运行出来,结果是一样的。
这两种方法处理异常后,代码在执行是JVM在底层会new一个对象,通过对象调用方法,将异常提示在控制台通知我们。
try…finally语法讲解
try…finally
有这样的有一段代码:
package com.Exception;
public class SC2 {
public static void main(String[] args) {
int a;
a=10;
try{
System.out.println(a);
return;
}finally {
System.out.println(a++);
}
}
}
我们不知道这是什么,但是按照我们的逻辑来思考,程序肯定会先执行finally部分,最后在try部分。毕竟要是先执行try部分,那么return之后就结束了。肯定是不符合这个语句的,
于是我们的逻辑出来的结果是这样的;在控制台输出10和11;
但其实不然,它的输出结果是这样的:
为什么呢,不是a++了吗, a的值应该是11了的。
为了符合try 的语法又要符合catch 的语法,
其实在运行过程中,代码部分会变成这样的:
int a;
a=10;
innt b;
b=a;
try{
System.out.println(a);
return;
}finally {
System.out.println(b++);
}
在JVM中编译器对我们的代码进行了优化,进行指令重排,提高了性能,也使得程序运行能够符合try和catch两个语法句。
偶尔出现的执行顺序相反简介
有这样的一段代码:
package com.Exception;
public class SC {
public static void main(String[] args) {
String sd;
int sf;
sd="xiaoming";
sf=18;
System.out.println("姓名"+sd+"年龄"+sf);
System.out.println("111111111111111111111");
ArithmeticException arithmeticException = new ArithmeticException("/ by aero");
arithmeticException.printStackTrace();
}
}
一般而言,我们理解为代码的运行顺序是从上而下,同时我们编写代码时头脑想的运行效果也是这样来预估的。上面的片段代码,按照我们的逻辑:程序会先执行两个println方法,然后再执行后面的实例化对象。最后运行出来是两行输出内容,然后是异常提示:
看到结果如我们想的一样。但其实不然。如果我们尝试多运行几次,会有那么几次出现这样的运行结果:
这样的结果与我们逻辑上的,完全相反。看到两行输出语句的内容在我们异常提示的下面。
为什么出现会出现这样的结果呢?
这是因为Java代码在编译执行的过程中,并不一定是从上到下,每句代码部分依次顺序执行的。只是因为大多数情况下。我们编写的代码运行出来和我们逻辑上是一样的,故而我们在潜移默化中,默认为代码的运行顺序是上而下。偶尔出现几次相反的结果,也不会在意。
代码的执行可以想象成操场上同一起跑线上的几个人,只是裁判员,先让一号跑多少秒,在才让2号跑多少秒,有可能继续让一号在跑多少秒,想起2号,在让2号跑。代码它是交替执行,每个代码部分执行速度不一样,先跑完就先显示出来。
也可以比喻成同时听歌和写作业,但你沉浸在写作业那一刻,你并没有听进去音乐,而你听歌的那一刻,你也没有在思考作业题。代码执行就像多个任务同时进行,但是每一个时刻只有一个任务在跑。所以说Java代码在执行过程中,并不一定是从上到下,每句代码依次顺序执行的。