Java异常处理



一、异常的概念



异常定义了程序执行过程中,非致命的错误 , 比如数组脚标越界,空指针,类转换异常等等

public static void main(String[] args) { 
  
     /* int [] x={1,2,3}; 
   
     System.out.println(x[3]);  //java.lang. 
    ArrayIndexOutOfBoundsException 数组下标越界异常*/ 
   
     String xxx=null; 
   
     System.out.println(xxx.length());  
    //NullPointerException空指针异常 
   
     System.out.println("--------程序执行到这才算正常------------"); 
   
    }


什么是异常?(神解释)
我不知道大家都是怎么去理解的,我的理解很简单,那就是不正常的情况,比如我现在是个男的,但是我却有着女人所独有的东西,在我看来这尼玛肯定是种异常,简直不能忍。就拿上面的异常来说,你应该是有小弟弟的,但你却没有小弟弟,这就是不正常的情况了,所以Java这个妹子会告诉你,你应该有小弟弟的才能办事,或者你没小弟弟就别来办事,你没小弟弟也要强上别人,那也没法玩啊是吧?
常说我们人的祖宗是猿,异常Exception的祖宗那就是Throwable类了,除了Exception类外,Throwable还有一个子类Error 。Error这玩意就不说了,你如果遇到了它你可以把电脑砸了,呼呼呼...跟大家开个玩笑,反正这玩意不是我们程序能处理的,它在程序处理的范畴之外。至于异常Exception又分为傻傻啥的,我就不BB了,再BB就B到这上面了。
给大家随便列举几个异常:
RuntimeException:你不能跑得足够快,可能因为你太胖了。关掉你的电脑,出门锻炼吧。
NullPointerException:你没有狗。请你先找一只狗,比如一只布烈塔尼獵犬,然后再试一次。
IndexOutOfBoundsException:你把食指放在了无法接收的地方,重新放置,再试一次。
FileNotFoundException:一名木匠应该总是知道他的工具放在哪里。
NotSerializableException:你正试图把一部电影改成电视剧。


二、try catch 语句


try...catch语句将可能引发错误的代码放在try块中,并且对于一个响应,然后可能有异常被抛出,首先要清楚,如果没有try的话,出现异常可能会导致程序崩溃,而try则可以保证程序正常运行下去。

//例-计算器 

   甲设计的计算器 

   public class Calc{ 
 

    public int add(int a,int b){ 
  

    return a+b; 
  

    } 
  

    public int divide(int a,int b){ 
  

    int result=a/b; 
  

    return result; 
  

    } 
  

   } 
 

   乙,要使用甲设计的计算器 
 

   public class MathTest { 
 

    public static void main(String[] args) { 
  

    计算(); 
  

    }

没有注意到可能发生异常,最后引起程序崩溃

static void 计算(){ 
  

    int a=90; 
  

    int b=0; 
  

    Calc c=new Calc(); 
  

    int addResult=c.add(a, b); 
  

    System.out.println("加法的结果是:"+addResult); 
  

    int divResult=c.divide(a, b); 
  

    System.out.println("除法的结果是:"+divResult);  
   //ArithmeticException 算术异常,除数等于零所导致 
  

    System.out.println("=======老板,你让我算的数我算好了 ======"); 
  

    } 
  

   }



//例二 乙使用try catch 处理可能发生的异常 
 

    static void 稳稳当当的计算(){ 
  

    int a=90; 
  

    int b=0; 
  

    Calc c=new Calc(); 
  

    int addResult=c.add(a, b); 
  

    System.out.println("加法的结果是:"+addResult); 
  

   //下面,使用try catch对异常信息进行了捕获,并处理 
 

    try{  
  

    int divResult=c.divide(a, b); 
  

    System.out.println("除法的结果是:"+divResult);  //ArithmeticException 算术异常 
  

    } 
  

    catch(Exception ex){ 
  

    ex.printStackTrace(); 
  

    //String msg=ex.getMessage(); 
  

    //System.out.println("哈哈,出错了我挺开心出错的信息是"+msg); 
  

    } 
  

    System.out.println("=======老板,你让我算的数我算好了 ======"); 
  

    } 
  

    注意 try 和catch是紧密相连的,中间不能加任何其他语句 
  

    try{ 
  

    } 
  

    //这里不能加任何语句 
  

    catch(){ 
  

    }


三、throw 关键字


throw是语句抛出一个异常,一般是在代码块的内部,当程序出现某种逻辑错误时由程序员主动指定抛出某种特定类型的异常

可以使用 throw 关键字,抛出一个异常

try{ 
 
 
 

   int divResult=c.divide(a, b); 
 
 
 

   System.out.println("除法的结果是:"+divResult);  //ArithmeticException 算术异常 
 
 
 

   } 
 
 
 

   catch(Exception ex){ 
 
 
 

   //throw(ex);  用于抛出一个异常 
 
 
 

   throw new NullPointerException("空空");  //抛出一个其他类型的异常 
 
 
 

   }


四、throws 关键字 



throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常,在后面调用时必须处理异常)





//甲设计的计算器 
 
 
 

   public class Calc{ 
 

    public int add(int a,int b){ 
  

    return a+b; 
  

    } 
  

    public int divide(int a,int b) throws Exception{ 
  

    if(b==0){ 
  

    throw new Exception("你傻啊? 你家除数能为0啊 !? "); //checked  
  

    } 
  

    int result=a/b; 
  

    return result; 
  

    } 
  

   } 
 
 
 

   //调用者 
 

    static void 计算() { 
  

    int a=90; 
  

    int b=0; 
  

    Calc c=new Calc(); 
  

    int addResult=c.add(a, b); 
  

    System.out.println("加法的结果是:"+addResult); 
  

    try{ 
  

    int divResult=c.divide(a, b); //由于方法已经用throws声明了抛出异常,所以这里必须处理,否则编不过去 
  

    System.out.println("除法的结果是:"+divResult);   
  

    } 
  

    catch(Exception ex){ 
  

    System.out.println(ex.getMessage()); 
  

    } 
  

    System.out.println("=======老板,你让我算的数我算好了 ======"); 
  

    }

五、自定义异常类


为什么要使用自定义异常,有什么好处?

1.我们在工作的时候,项目是分模块或者分功能开发的 ,基本不会你一个人开发一整个项目,使用自定义异常类就统一了对外异常展示的方式。

2.有时候我们遇到某些校验或者问题时,需要直接结束掉当前的请求,这时便可以通过抛出自定义异常来结束,如果你项目中使用了SpringMVC比较新的版本的话有控制器增强,可以通过@ControllerAdvice注解写一个控制器增强类来拦截自定义的异常并响应给前端相应的信息(关于springMVC控制器增强的知识有空再和大家分享)。

3.自定义异常可以在我们项目中某些特殊的业务逻辑时抛出异常,比如"中性".equals(sex),性别等于中性时我们要抛出异常,而Java是不会有这种异常的。系统中有些错误是符合Java语法的,但不符合我们项目的业务逻辑。

4.使用自定义异常继承相关的异常来抛出处理后的异常信息可以隐藏底层的异常,这样更安全,异常信息也更加的直观。自定义异常可以抛出我们自己想要抛出的信息,可以通过抛出的信息区分异常发生的位置,根据异常名我们就可以知道哪里有异常,根据异常提示信息进行程序修改。比如空指针异常NullPointException,我们可以抛出信息为“xxx为空”定位异常位置,而不用输出堆栈信息。

说完了为什么要使用自定义异常,有什么好处,我们再来看看自定义异常的毛病:

毋庸置疑,我们不可能期待JVM(Java虚拟机)自动抛出一个自定义异常,也不能够期待JVM会自动处理一个自定义异常。发现异常、抛出异常以及处理异常的工作必须靠编程人员在代码中利用异常处理机制自己完成。这样就相应的增加了一些开发成本和工作量,所以项目没必要的话,也不一定非得要用上自定义异常,要能够自己去权衡。

最后,我们来看看怎么使用自定义异常:

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

所有异常都必须是 Throwable 的子类。

如果希望写一个检查性异常类,则需要继承 Exception 类。

如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

写一个类,继承自Exception 

//自定义异常类 

   public class MyException extends Exception{ 
 

    MyException(String msg){ 
  

    super(msg); 
  

    this.msg=msg; 
  

    } 
  

    private  String msg; 
  

    void log(){ 
  

    System.out.println("日志已经记录到文件"); 
  

    } 
  

    void showErrorMsg(){ 
  

    System.out.println("异常信息如下:"+msg +"发生时间:"+new Date()); 
  
 

   } 
 

    static void 计算() { 
  

    int a=2147483647;   
  

    int b=20; 
  

    Calc c=new Calc(); 
  

    try{ 
  

    int addResult=c.add(a, b); 
  

    System.out.println("加法的结果是:"+addResult); 
  

    } 
  

    catch(MyException ex){  //这是捕获的是自定义异常 
  
 ex.log();   
  
 ex.showErrorMsg(); 
  

      ex.printStackTrace();  
  

    } 
  

    try{ 
  

    int divResult=c.divide(a, b); 
  

    System.out.println("除法的结果是:"+divResult);  
  

    } 
  

    catch(Exception ex){ 
  

    System.out.println(ex.getMessage()); 
  

    } 
  

    System.out.println("=======老板,你让我算的数我算好了 ======"); 
  

    }


六、运行时异常(uncheck) 和 check 异常



public class Test2 {  
 

    public static void main(String[] args) { 
  

    try{ 
  

    test();  //这两个必须处理,因为它们抛出的是 check异常 
  

    test2(); 
  

    } 
  

    /*catch(IOException  | SQLException   b ){  //可以这样写 
  

    }  */ 
  

    catch(IOException ex){ 
  

    } 
  

    catch(SQLException ex){ 
  

    } 
  

    test3();  //可以不处理,因为它抛出的是运行时异常 
  

    } 
  

    static void test3() throws NullPointerException { 
  

    throw new NullPointerException();  //uncheck 异常 (运行时异常,来自 RuntimeException) 
  

    } 
  

    static void test() throws IOException{ //在编译的时候,如果不处理,编译不过去,check 异常 
  

    throw new  IOException(); 
  

    } 
  

    static void test2() throws SQLException{ 
  

    throw new  SQLException(); 
  

    } 
  

   } 
 


   //例子 
 

    public static void main(String[] args) { 
  

    test("d:/1.txt"); 
  

    System.out.println("ok"); 
  

    } 
  

    static void test(String fileName) { //在编译的时候,如果不处理,编译不过去,check 异常 
  

    try{ 
  

    FileWriter f=new FileWriter(fileName); 
  

    f.write("我是一行文本"); 
  

    f.close(); 
  

    } 
  

    catch(Exception ex){ 
  

    //ex.printStackTrace(); 
  

    throw new RuntimeException(ex);  //将一个普通异常,变成一个运行时异常抛 出去 
  

    } 
  

    }



七、在子类中处理异常



public class Student { 
 

    void suanshu() throws SQLException{ 
  

    throw new SQLException(); 
  

    } 
  

   }



class GoodStudent extends Student{ 
 

    void suanshu() throws SQLException{  //和父类抛出同样的异常,可以 
  

    } 
  
 void suanshu() throws SQLException,IOException {  //不可以,因为抛出了父类中没有声明的异常  IOException 
  

    } */ 
  

   }



class VeryGoodStudent extends Student{  
 

    void suanshu() throws SerialException{  //可以,因为 SerialException是 SQLException的子类 
  

    } 
  

   }



//如果在父类的方法中声明抛出异常, 那么在子类重写这个方法的时候,可以不抛出异常,或抛出和原来异常相同的异常

//或父类异常的子集,而不能抛出新的异常


八、finally 语句


finally :最终的 写在 finally里的语句,不管发不发生异常,都要执行, 通常,有资源释放之类的操作,都放在 finally 中 

哪怕前面有return 它也会执行

public static void main(String[] args) { 
  

    writeFile("2.txt"); 
  

    } 
  

    static void writeFile(String fileName) { 
  

    FileWriter fw=null; 
  

    try { 
  

    fw = new FileWriter(fileName); 
  

    fw.write("这是要写入的文本"); 
  

    int a=9/0; 
  

    } catch (Exception ex) { 
  

    ex.printStackTrace(); 
  

    } 
  

    finally{ //表示不管出不出异常,这里的代码都要执行 
  

    fw.close();  (这里也有可能要try catch,略) 
  

    } 
  

    }



//finally语句都会执行 
  

    public static void main(String[] args) { 
  

    try{ 
  

    System.out.println("a"); 
  

    System.out.println("a"); 
  

    System.out.println("a"); 
  

    System.out.println(9/0); 
  

    System.out.println("a"); 
  

    return; 
  

    }catch(Exception ex){ 
  

    System.out.println("异常"); 
  

    }finally{ 
  

    System.out.println("finally"); 
  

    } 
  

    }

结果 :aaa  异常  finally




// finally 语句,在什么情况下执行不到

1) 程序根本没进行try语句快就结束了

2) 遇到了 System.exit(0);

九、异常处理相关练习




1. 编写一个除数为零的异常,并且捕获它。

2. 编写一个程序,从键盘输入一个字符串,再把这个字符串转化成整数并输出。第一次输入523看结果是什么,

第二次输入非数字字符看结果有是什么?那种情况会产生异常,是什么异常?怎么处理一下?

//NumberFormatException  把字符串转成数字的时候,发生的转换异常 
 
 
 

   public static void main(String[] args) { 
 
 
 

   System.out.println("请输入一行数字"); 
 
 
 

   Scanner scan=new Scanner(System.in); 
 
 
 

   String str =scan.nextLine();  //从键盘接收一行文本,可能是纯数字,也可能有别的  789 
 
 
 

   //把String类转成数字 
 
 
 

   try{ 
 
 
 

   int number= Integer.parseInt(str);  //它的作用是把一个合理的字符串转成String型 
 
 
 

   System.out.println("转换成功! 不信你看把它加1以后得到的结果"+(number+1)); 
 
 
 

   } 
 
 
 

   catch(Exception ex){ 
 
 
 

   System.out.println("输入的数字不正确!有非法字符 系统提示的信息是"+ex.getMessage()); 
 
 
 

   } 
 
 
 

   scan.close(); 
 
 
 

   }



3.编写一个程序,定义一个包含10个整数的数组a[10],用for循环给该数组放入10个值,试着分别访问a[3],

a[8],a[10],看会发生什么?如果发生异常的话是什么异常?


 


用于学生类,目的是控制学生的年龄不能小于1岁大于100岁。

//自定义异常类 
 
 
 

   public class StuException extends Exception{ 
 
 
 

   private String msg; 
 
 
 

   StuException(String msg){ 
 
 
 

   this.msg=msg; 
 
 
 

   } 
 
 
 

   void showMsg(){ 
 
 
 

   System.out.println(new java.util.Date() +": "+ this.msg); 
 
 


   } 
 
 
 
   class Student{ 
 

    public Student(int age, String name) throws StuException{ 
  

    if(age>100){ 
  

    throw new StuException("学生的岁数太大了,还是回家养老吧"); 
  

    } 
  

    else if (age<1){ 
  

    throw new StuException("还是等学生出生了再来吧"); 
  

    } 
  

    this.age = age; 
  

    this.name = name; 
  

    } 
  

    int age; 
  

    String name; 
  

   } 
 

   class Test2{ 
 

    public static void main(String[] args) { 
  

    try { 
  

    Student stu=new Student(190,"赵明明"); 
  

    } catch (StuException e) { 
  

    e.showMsg(); 
  

    } 
  

    } 
  

   }




5. 编写一个自定义异常类,来控制银行卡中每次取钱的金额不能大于5000元,并且卡上的余额不能小于10元。

//自定义异常类 
 
 
 

   public class MyException  extends Exception{ 
 

    private String msg; 
  

    MyException(String msg){ 
  

    super(msg); 
  

    this.msg=msg; 
  

    } 
  

    public void log(){ 
  

    try{ 
  

    FileWriter fw=new FileWriter("log.txt",true); 
  

    fw.append(this.msg+"\n");  
  

    fw.close(); 
  

    } 
  

    catch(Exception ex){ 
  

    ex.printStackTrace(); 
  

    } 
  

    } 
  

    public void showErrorMsg(){ 
  

    System.out.println("操作失败: 日期:"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())+": " + this.msg); 
  

    } 
  

   }


// 银行类 
 
 
 

   public class Bank { 
 

    private int money ; // 余额 
  

    public Bank(int money){ 
  

    this.money=money; 
  

    } 
  

    public void takeMoney(int money) throws MyException { // 取款的方法 
  

    if (money > 5000) { 
  

    throw new MyException("取款金额大于5000!"); 
  

    } 
  

    if (this.money - money < 10) { 
  

    throw new MyException("如果取完以后余额不足将不足10元!"); 
  

    } else { 
  

    this.money -= money; 
  

    } 
  

    System.out.println(new Date() + ": 取款成功 取款金额:" + money); 
  

    } 
  

   }



// 测试 
 
 
 

   class T{ 
 

    public static void main(String[] args) { 
  

    Bank bank=new Bank(10000); 
  

    try { 
  

    bank.takeMoney(2000); 
  

    bank.takeMoney(5500); 
  

    bank.takeMoney(2000); 
  
 
   

    } catch (MyException e) { 
  

    e.log(); 
  

    e.showErrorMsg(); 
  

    } 
  

   } 
 
 
 

   }


十、异常处理相关记录


一、try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 

也许你的答案是在return之前,但往更细地说,我的答案是在return中间执行,请看下面程序代码的运行结果: 

public  class Test { 
 

    public static void main(String[] args) { 
  

    System.out.println(new Test().test());; 
  

    } 
  

    static int test(){ 
  

    int x = 1; 
  

    try{ 
  

    return x; 
  

    }finally 
  

    { 
  

    ++x;  //这个操作不会对上面的return的结果有影响 
  

    System.out.println("finally语句被执行了"); 
  

    //如果这么写 return x ;  则返回的结果是2 
  

    } 
  

    } 
  

   }

答案是1 



二、final, finally, finalize的区别

final 最终的, 用来修饰变量,方法,类 

用它修饰的变量就变成了常量,常量的值不可以改变,修饰的方法不能被重写,类不能被继承, static final 修饰的成员要直接初始化

finalize 来自Object类,对象在内存中的空间被回收的时候,会调用这个方法

finally 用于try catch 语句块之后,表示这里面的代码一定要被执行,这些代码多用来关闭资源

try 语句之后,可不可以没有catch ? 可不可以直接接上finally ?

  //答:可以 例如 下面的写法是可以

try{

}finally{

}



三、运行时异常与一般异常有何异同

异常表示程序运行过程中可能出现的非正常状态,

运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。

java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。



四、error和exception有什么区别

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。

exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况


 


五、Java中的异常处理机制的简单原理和应用

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,

现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,

Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,

该对象中包含有异常的信息。

Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,

所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,

Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,

例如,说内存溢出和线程死锁等系统问题。Exception表示程序还能够克服和恢复的问题,

其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,

软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,

例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、

类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,

例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。

java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,

所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以

,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。

提示答题者:就按照三个级别去思考:虚拟机必须宕机的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误;



六、请写出你最常见到的5个runtime exception

NullPointerException、  //空针针

ArrayIndexOutOfBoundsException、 //数组脚标越界

ClassCastException   //类转换异常

ArithmeticException  //算术异常

IllegalArgumentException  //错误的参数异常

ArrayStoreException //将错误的对象存诸到一个数组的时候

ClassNotFoundException  //类找不到异常



七、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 

可以,一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,

这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。

用try来指定一块预防所有“异常”的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的“异常”的类型。

throw语句用来明确地抛出一个“异常”。throws用来标明一个成员函数可能抛出的各种“异常”。

Finally为确保一段代码不管发生什么“异常”都被执行一段代码。

try块中可以抛出异常吗 //可以