异常的分类
异常分为检查性异常、运行时异常、Error
1、检查性异常,又叫编译时异常,发生在代码编译过程中,编译器会报错,检查性异常出现时,代码编译无法通过
2、运行时异常是在出现运行过程中出现的,对于运行时异常,可以由程序进行处理,并继续运行程序。
3、Error是致命性的错误,Error类对象由java虚拟机生产并抛出,发生error时,JVM一般会选择线程终止。
注意:所有异常都是发生在程序运行阶段,因为异常的产生是异常类创建了对象,由于编译时异常是发生在编译阶段,编译器可提前预知,并报错,故叫编译时异常。
异常的继承结构
异常处理的两种方式
1、抛出异常。(throw与throws关键字)
首先在方法声明处使用throws关键字。出现异常时抛给上一级,也可以方法代码块中使用throw关键字,手动抛出异常。(如果是运行时异常抛给了main方法,且main方法没有进行捕捉处理,程序运行并出现错误后JVM会终止程序运行;如果是编译时异常,main方法不进行处理,编译器会无法通过编译。)
代码实例:
public class dome {
public static void main(String[] args) throws ClassNotFoundException{
/*
* 在main方法中调用dosome方法,dosome方法声明了执行过
* 程中会抛出 ClassNotFoundException,而 ClassNotFoundException
* 是编译时异常,main方法要进行处理,否则会报错。
*/
//
dosome();//如果main方法不加throws ClassNotFoundException,此处报错
if(true){
throw new ClassNotFoundException();//运行到这,main方法手动向JVM抛出异常,JVM结束程序运行。
}
}
public static void dosome() throws ClassNotFoundException{
System.out.println("dosome");
}
}
2、使用try…catch进行异常捕捉。
上述代码,也可以不在main方法出加 throws ClassNotFoundException,而是把dosome();改为如下代码
try {
dosome();
} catch (ClassNotFoundException e) {
//此处是对发生异常后的处理过程
e.printStackTrace();
}
异常处理的具体过程(try…catch)
try{}中的代码执行到出异常的语句后,该代码块中后面的代码不执行,进入catch代码块,执行完catch{}代码块后,再执行finally代码块。
try{
dosome();
dosome1();//假设这里出了异常,将进入catch代码块,dosome2()不执行
dosome2();
}catch(exception e){//假设dosome1()会出现ClassNotFoundException,此处可以写该异常的父类,因为
//有向上转型,Exception e=new ClassNotFoundException.
}catch(){
finally{
}
注意:
(1)、catch()中的括号可以写具体异常类,也可以写该异常类的父类。
(2)、无论try语句块中的代码是否出现异常,finally语句块中的代码一定会最后执行,即使是try语句块和catch语句块中有return;。但是要注意,如果return了一个值,如return a;(a=5),然后在finally语句中对a进行了修改,返回的结果依然是5。即返回的值无法在finally中改变。(但如果在执行到finally子句之前退出了JVM,则该语句块中的代码无法执行。)
(3)、catch语句块可以写多个,但要注意第一个catch的异常类必须是第二个catch异常类的子类,即从上往下是子类到父类的顺序,否则会报错,多个catch也一样。如下列代码:
try {
FileInputStream fis=new FileInputStream("C:\\Users\\STU\\Desktop");
fis.read();
} catch (IOException e) {
}catch(Exception e1) {
}
IOException是Exception的子类,这样才能编译通过。(出现异常时,JVM会从第一个catch开始寻找,如果第一个catch是异常父类,则第二个catch永远不会被执行到,故报错。)
自定义异常
自定义异常分为两个步骤:
1、编写一个类继承Exception或者RuntimeException。
2、提供两个构造方法,一个无参构造,一个带有String的构造方法。
(其中传入的String参数是e.getMessage()方法获得的信息。)
public class MyException extends Exception{
public MyException(){
}
public MyException(String s){
super(s);//调用父类Exception的构造方法。
}
}
对自定义异常的使用
public class demo {
public static void main(String[] args) {
Test t=new Test();
try {
t.func();
} catch (MyException e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());//输出结果为:自定义异常
}
}
}
class Test{
public void func() throws MyException {
throw new MyException("自定义异常");
}
}
class MyException extends Exception{
public MyException() {
自定义异常一定要手动创建并手动抛出,不能进行捕捉。因为自己创建的异常对象自己处理没有意义。(JDK源码中的异常类也是这样的形式。)
方法重写中抛出异常的问题
1、父亲犯了错,儿子犯的错误比父亲少。
若一个父类中的一个方法抛出了Exception,则子类在重写该方法时,只能抛出Exception或Exception的子类,也可以不抛出异常。(可以理解为父亲犯了错,儿子犯的错误比父亲少),如下列代码
class Son extends Dad{
public void DadMeth() throws NullPointerException {
throw new NullPointerException();
}
}
class Dad{
public void DadMeth() throws RuntimeException {
throw new RuntimeException();
}
}
NullPointerException是RuntimeException的子类。
2、父亲不犯错,儿子可以犯运行时错误。
如果父类方法没有抛出异常,则子类重写该方法时,可以抛出RuntimeException或其子类异常。
(可以理解为父亲不犯错,儿子可以犯错),如下列代码:
class Son extends Dad{
public void Dad() throws NullPointerException{
throw new NullPointerException();
}
}
class Dad{
public void Dad() {
}
}