黑马程序员——Java基础---异常

时间:2022-02-22 21:44:06
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

异常处理

一、概述。

        所谓异常,就是程序在运行过程中出现的不正常现象。这些现象有一些是不合Java的规则造成的错误,有一些则是不合乎自然逻辑而导致的问题。对这些问题的描述也是现实生活中的一个具体的事物,可以看成Java中的一个类来描述,并封装成对象,也就是Java对不正常情况进行描述后的对象的体现。

        在Java中,可以将问题分成两类:一类是严重的错误,Java将这类错误用Error类来描述;一种是不是非常严重的错误,Java通过Exception类来描述。对于严重的错误,一般都不编写代码对其进行处理,对Exception的错误我们可以使用针对性的处理方法进行处理。但是不论何种错误,都有其共性:不正常的信息产生,引发错误的原因等,我们将这个封装成一个最大的类:Throwable。

        则异常类的体系为:

         Throwable
               |--Error
               |--Exception

二、异常的处理方式。

        程序发生异常处理有两种可能:

        1、对可能发生异常的类或者方法上,JavaApi往往会申明该方法或者对象如果出现错误就会抛出异常类对象,这时候我们要让程序编译通过则必须对异常进行处理。

        2、当我们自定义的一个异常在程序运行时被抛出,那么我们也需要对异常进行处理才能让程序运行。

       在Java中,异常处理有特定的处理语句:

Try
{
需要被检测到的代码;
}
catch(异常类 变量名)
{
处理异常的代码(处理方式)
}
finally
{
一定会被执行的代码。
}
        在被检测的代码中如果抛出了异常,那么该检测代码立即终止运行,同时在catch()里接收到该异常对象,立即执行需要处理异常的代码,而在finally代码块中则是一定会被执行的语句,该语句常常用来关闭资源。以最简单的a/b为列子,当b等于0时,一定会抛出错误:

package com.itheima;
import java.lang.*;
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(5, 0);
System.out.println("X="+x);
}
catch(Exception e)
{
System.out.println("除零啦");
System.out.println(e.getMessage());// / by zero;
System.out.println(e.toString());// 异常名称 : 异常信息。
e.printStackTrace();//异常名称,异常信息,异常出现的位置。
//其实jvm默认的异常处理机制,就是在调用printStackTrace方法。
//打印异常的堆栈的跟踪信息。
}
}
}

class Demo
{
int div(int a,int b)throws Exception//在功能上通过throws的关键字声明了该功能有可能会出现问题。
{
return a/b;
}
}
注意以上三种处理异常的方法getMessage()、toString()、printStackTrace()这三种方法的功用。

三、异常的申明和多异常的处理

        对于异常的申明某一个方法内部发生异常时,需要在该段代码下立即抛出异常,基本格式为throw new Exception(XXX),如果该段语句没有在try代码块之内的话我们需要在该代码块所在的方法上申明异常,基本格式为:throws Exception,由调用者处理这个异常。而在try、catch代码块内的话说明我们即将处理该异常,则不需要在方法上申明。注意:在代码下写上throwthrow new Exception(XXX)且确实发生了异常,代表在该try代码块程序只能执行到这里,立即跳转到catch代码块,若没有catch代码块而是在方法上抛出异常代表该程序只能执行到这里,所以在throw语句下方不要写代码,这些代码很可能会无效!

        在这里我们明确throw和throws的区别:throw是定义在方法内部,是确实对产生的异常对象抛出,而throws则是定义在方法上,是对该方法会抛出异常的申明。

        如果方法内部一段代码产生了多个异常,我们需要在产生异常代码下方一个一个的抛出,而在该方法上也要一个一个的申明清楚注意在调用该方法时,使用多个catch代码块对多个异常进行分别处理,特别是,如果各个异常有继承关系,由于多态的原因,我们需要将父类的异常处理放在最后处理,这样才能保证异常处理的精确性。列子:

package com.itheima;
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(5, 0);
System.out.println("X="+x);
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("角标越界啦");
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
catch(Exception e)
{
System.out.println("除零啦");
System.out.println(e.getMessage());// / by zero;
System.out.println(e.toString());// 异常名称 : 异常信息。

e.printStackTrace();//异常名称,异常信息,异常出现的位置。
//其实jvm默认的异常处理机制,就是在调用printStackTrace方法。
//打印异常的堆栈的跟踪信息。
}
}
}

class Demo
{
int div(int a,int b)throws Exception,ArrayIndexOutOfBoundsException//在功能上通过throws的关键字声明了该功能有可能会出现问题。
{
int[] arr = new int[a];
System.out.println(arr[6]);
return a/b;
}
}

四、自定义异常

        前文说过,对于java已经定义好了的异常,我们能够很好的处理这些异常。但是对于一些特有的,我们在实际操作编程过程中会出现一些我们自己所定义的一些特有的问题,比如说对a/b这句话,我们如果希望除数不会是负数,一点是负数就抛出异常,这就是一个未被Java描述且封装的异常。所以,这些异常需要我们自己按照Java所描述的对问题的封装思想对这些异常进行封装,这就是自定义异常。

        首先,自定义异常是一个类,这个类刚定义是没有办法描述一个我们所需要的特定异常的,而Exception异常已经很好的将各种异常的处理封装其中,而且异常类和异常对象都被抛出。他们都具备可抛性,这个可抛性是Throwable这个体系中独有特点,只有这个体系中的类和对象才可以被throws和throw操作。所以我们可以将Exception作为该自定义类的父类。

             其次,如以上代码所明确,对我们产生的异常调用getMessage()方法会产生异常信息,但是我们的自定义异常是没有定义异常信息的。如何解决?因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类通过super语句,那么就可以直接通过getMessage方法获取自定义的异常信息。列子:

package com.itheima;
class MyException extends Exception//自定义一个异常
{
private int value;
MyException(String message,int value)
{
super(message);
this.value=value;
}
public int getValue()
{
return value;
}
}
class Demo1
{
public int div(int a,int b) throws MyException,Exception//申明异常
{
if(b<0)
throw new MyException("除以负数了!",b);//在这里抛出异常

return a/b;
}
}

public class ExceptionDemo2
{
public static void main(String[] args)//异常处理
{
Demo1 d = new Demo1();
try
{
d.div(3,-1);
}
catch(MyException e)
{
e.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

五、异常Exception中的RuntimeException子类。

        在各种Exception异常的子类异常当中,大致可分为两类异常,一类是在编译时检测到的异常,异常抛出后必须要我们处理,若不处理也要申明抛出交给调用者来处理的另一类则是系统出现信息错误,由于这一类错误是很明显无法处理的,需要调用者自己修改代码,在编译时可以通过(不能被检测到),运行时不能通过,希望立即停止当前程序运行,这一类错误只需要抛出,不需要申明或者处理。

        前面所说的一类异常是Exception的大部分子类,而后者则一定是RuntimeException或者其子类!那么何时使用RuntimeException异常以及其子类呢?

        如果有某种异常是在编译时不能被检测到,但是运行时由于出行无法继续运算的情况而产生的错误那么就抛出RuntimeException异常或者其子类,若该异常是自定义异常,则选择继承RuntimeException异常。

        列子:

package com.itheima;
class fushuException extends RuntimeException//自定义异常,当除数为负数时,程序停止
{
private int value;
fushuException(String massege,int value)
{
super(massege);
this.value=value;
}
}

public class RuntimeExceptionDemo //很显然该程序编译一定会通过,但是我们不希望除数为负数,则必须在运行时爆出异常!
{
public static void main(String[] args)
{
DivDemo demo = new DivDemo();
int x = demo.div(4,-3);
}
}
class DivDemo
{
public int div(int a,int b)
{
if(b<0)
throw new fushuException("负数!",b);
return a/b;
}
}

六、Finally代码块的使用。

        我们已经了解到了异常的处理方式,在上面我们已经使用过了try,catch代码块,那么finally代码块是干什么用的呢?

        首先,对于异常处理我们有三种处理方式:1、try,catch代码块;2、try,finally代码块3、try,catch,finally代码块。对于try和catch中的代码块,如果抛出了异常那么throw下方的语句是不会被执行的,那么出现了一个问题,当我们使用某些资源的时候,特别是在数据库操作过程当中或者IO时,出现了一个异常,该段代码就不会再被执行,但是我们开启的资源依旧存在,不但占用内存而且这些资源使用的权限个数都是一定的,不能白白浪费,这时候关闭资源这个语句就立马要执行,这个时候finally这段代码块就体现了其重要作用。没错!所谓finally代码块就是在异常处理中所必须去执行的程序,一般用于关闭资源的操作。列子:      

package com.itheima;
import java.io.*;
public class FinallyDemo {
public static void main(String[] args) throws IOException
{
BufferedReader br=null;
PrintWriter pw=null;//try代码块里面的东西finally不会调用到,所以全局变量要在外部定义。
try
{
br = new BufferedReader(new InputStreamReader(System.in));
pw = new PrintWriter("1.txt");
String line = null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
break;
pw.println(line);
pw.flush();
}
}
finally{

pw.close();//PrintWriter关闭资源不会报错!
try//用多个try、catch块处理关闭IO资源程序,因为每一次关闭都可能出现异常,且每一个关闭动作都必须执行,所以不能将多个关闭资源动作定义在一个try代码块中。
{
br.close();
}
catch(IOException e)
{
throw e;
}

}
}
}

七、子父类中异常。

        当父类中抛出了异常时,对继承该父类的子类来说,就只能抛出父类异常的子类,若父类有多个异常,则子类只能抛出父类异常的字集。若子类要有父类没有的异常时,则必须在子类中处理,绝对绝对不能抛出去!

八、练习。

        有一个圆形和长方形。都可以获取面积。对于面积如果出现非法的数值,视为是获取面积出现问题,问题通过异常来表示。

package com.itheima;
class shouqianException extends RuntimeException{//定义一个RuntimeException异常
shouqianException(String massege){
super(massege);
}
}
class changfangxing{
private double chang;
private double kuan;
changfangxing(double chang,double kuan){
if(chang<0||kuan<=0)
throw new shouqianException("手欠啦!");//RuntimeException异常,不需要申明
this.chang=chang;
this.kuan=kuan;
}
public double getChang(){
return chang;
}
public double getKuan(){
return kuan;
}
public double getarea(){
return chang*kuan;
}
}
class yuan{
private double banjing;
private static final double Pi=3.14;
yuan(double banjing){
if(banjing<0)
throw new shouqianException("手欠啦!");//RuntimeException异常,不需要申明,也不需要处理,直接断掉程序。
this.banjing=banjing;
}
public double getbanjing(){
return banjing;
}
public double getarea(){
return banjing*banjing*Pi;
}
}
public class AreaDemo {
public static void main(String[] args){
yuan y = new yuan(4.5);
changfangxing c = new changfangxing(4.4,-6);
System.out.println(y.getarea());
System.out.println(c.getarea());
}
}



------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------