黑马程序员——Java基础:异常处理机制

时间:2023-02-16 22:18:15

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
异常Throwable—java运行时发生的问题,jvm将问题(异常名,位置,信息)封装成对象,异常体系的最大特点是体系中以及类产生的对象,都具体可抛性。
1、异常包括
Error:由系统底层发生的严重异常,告诉jvm,jvm告诉使用者。
—不做针对性处理,直接修改代码
Exception:jvm发生,并告诉使用者。

2 、异常的机制
1)语句发生问题,jvm自动将这个已知的问题分装成对象new **Exception()
2)throw new **Exception();将异常告知给调用者
3)调用者没有处理方式,就继续向上抛知道主函数,主函数没有处理就抛给jvm
4)jvm使用默认处理方式(调用方法e.printStackTrace()),将问题呢名称+信息+位置打印出来,并结束程序

3、异常处理方式:
3.1、遇到问题不进行具体的处理,而是抛给调用者
1)在编写功能时,编写者知道该功能可能发生问题,而该问题应让调用者知道,并预先做好处理方式
所以在功能上对可能发生的问题声明,使用throws 异常类
2)调用了有声明异常的方法,必须处理。
3.2、捕获异常
1)try{有可能发生异常的代码} catch(异常类 变量){处理异常的代码} finally{}
2)处理了异常后,程序继续往下执行
3)异常处理方法:
e.getMessage()—异常信息;
e.toString()—-异常名称+异常信息
e.printStackTrace()—-异常名称+异常信息+位置
—–没有使用catch语句处理时,jvm默认处理收到的异常,调用此方法将信息显示在屏
—–程序中如果有catch语句,jvm就不调用该方法

4异常的原则
4.1功能内部throw抛出非运行时异常及其子类异常,功能上一定要throws声明,抛什么就声明什么
4.2调用有异常申明的函数,调用者必须处理异常:可以抛,也可以捕获
因为—–如果调用者不处理,编译失败

4.3特殊情况:当函数内通过throw抛出了RuntimeException及其子类的异常对象(编译时不检测),函数上可以不用throws声明
原因—–不声明的目的就是不让调用者处理,让调用者的程序停止并打印异常,防止后面程序使用错误运算结果
——区别于Error是系统底层发生错误,程序不能正常运行;RuntimeException是程序正常运行
应用—–导致功能的失败,抛出不需要声明的异常,使程序停止并打印异常,要求修改代码

4.4自定义异常—–功能本身的有问题,可以对问题进行描述,创建异常类
——继承Exception,RuntimeException,复写父类的构造函数,super();
—–声明or捕获:1)功能可以解决,就捕获;
—–捕获后解决,对外不暴露
2)异常不能解决,抛出+声明;或解决了要告诉调用者,也要声明
—-捕获后解决,异常转换:封装本层异常,对外抛出对方能识别的异常

4.5在多catch语句——–父类的catch一定要放在子类catch的下面,否则编译失败
4.6子类覆盖父类抛异常的方法——-原则:子类的异常必须在父类的异常处理控制中
—–子类只能抛出父类方法异常或该异常的子类
——父类抛出多个异常,子类只能抛出父类异常的子集
特殊:被覆盖的方法没有抛出异常,那么子类在覆盖时,子类方法中发生了异常,就只能try无法throws
例子:

interface Inter {
        void sow();
    }
    class Demo implements Inter {
        //父类没有抛异常,子类就不能抛,这时可以把编译异常转化成运行异常抛出,但不申明异常
        public void show()
        {
            try
            {
                throw new Exception();
            }
            catch (Exception e)
            {
                throw new RuntimException();//将编译时检测异常转换为运行异常抛出去
            }
        }
    }

5 自定义异常
当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。

自定义异常步骤
1)定义一个子类继承Exception或Error,让该类具备可抛性。
异常体系有一个独有特点,只有这个体系的类和对象具备可抛性,可以被throw和throws操作。
2)自定义异常时,如果该类异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException
3)定义异常信息?因为父类已经把信息的操作都完成了,子类只需通过super()将异常信息传给父类,就可以直接通过getMessage()方法获取异常信息。
4)当在函数内部出现了throw抛出异常对象,那么必须要给出处理动作,要么在内部try+catch处理,要么在函数上声明,让调用者处理。一般情况下,函数内出现异常,函数上需要声明。


6 throws与throw的区别:
thorws用在方法上,声明该方法需要处理的异常类型,后面跟异常类名,可以是多个异常类。
throw用在方法内,用于抛出具体异常类的对象,后面跟异常对象,只能是一个。
6.1 声明异常(throws)
在可能出现异常的方法上声明抛出可能出现异常的类型:
声明的时候尽可能声明具体的异常,方便更好的处理.
当前方法不知道如何处理这种异常,可将该异常交给上一级调用者来处理(非RuntimeException类型的异常)。
方法一旦使用throws声明抛出方法内可能出现的异常类型, 该方法就可以不再过问该异常了;
一个方法调用另一个使用throws声明抛出的方法,自己要么try…catch , 要么也throws;
格式:

public 返回值类型  方法名(参数列表...) 
            throws 异常类A,异常类B... {   
}

6.2 关键字throw
自行抛出一个异常对象,抛出异常类的对象;
若throw抛出的是Runtime异常:
程序可以显示使用try…catch来捕获并处理,也可以不管,直接交给方法调用者处理;
若throw抛出Checked异常:
要么放在try里自己处理,要么放在一个throws声明的方法里面,交给调用者处理。

public static void main(String[] args) {
        try {
              fn1(1);
        } catch (Exception e) { e.printStackTrace(); }
        fn2(2);
    }
    public static void fn1(int a) throws Exception{
        if(a >0) { throw new Exception("fn1 -- a值不合法"); }
    }
    public static void fn2(int a) {
        if(a >0) { throw new RuntimeException("a值不合法"); }
    }

7 关键字finally
Finally代码块:一定执行的代码。通常用于关闭资源。
不管try块程序是否异常,也不管哪个catch执行,finally块总会执行。
try语句块或会执行的catch语句块使用了JVM系统退出语句例外;//System.exit(0);
try块必须和 catch块或finally同在,不能单独存在,二者必须出现一个。
不要在finally中使用return 或throw语句,否则将会导致try、catch中的return或throw失效。

/* 分层思想 连接数据库; 数据操作;//throw new SQLException();抛数据库异常 关闭数据库;//无论数据操作是否成功,一定要关闭资源 */
class NoException extend Exception
{
}
public void method()
{
    try
    {
        连接数据库;
        数据操作;//throw new SQLException();抛数据库异常
    }
    catch (SQLException e)
    {
        会对数据库进行异常处理;
        throw new NoException(); //不暴露本层问题,只暴露对方能识别的问题
    }
    finally
    {
        关闭数据库;//无论数据操作是否成功,一定要关闭资源
    }
}

8 异常在子父类覆盖中的体现
1)子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或该异常的子类或者不抛
2)如果父类抛出多个异常,则子类在覆盖该方法时,只能抛父类异常的子集
3)如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时也不可以抛出异常,如果子类发生了异常,就必须进行try处理

class AException extends Exception {
}
class BException extends AException {
}
class CException {
}
/* Exception |--AException |--BException |--CException */
class Fu {
    void show() throws AException
    {
    }
}
class Test {
    //接受Fu的引用,使用Fu的方法
    void function(Fu f)
    {
        try
        {
            f.show();//调用了一个抛出异常的方法
        }
        catch (AException e)
        {
        }

    }
}
class Zi extends Fu {
    void show() throws AException//或者BException但不能抛CException
}
class {
    public static void main(String[] args) 
    {
        Test t = new Test();
        t.function(new Fu());
    }
}

9 例题

/* 有一个圆形和长方形 都可以获取面积,对于面积如果出息非法的数组,视为获取面积 出现的问题,问题通过异常表示 分析:1.面积功能是图形的扩展功能,定义为接口;基本功能时,定义为类 2.自定义异常:数值不准确,影响到程序运行。故继承RuntimeException */
class NoValueException extends RuntimeException//自定义类有助于阅读和细化异常 {
    NoValueException(String message)
    {
        super(message);
    }
}
interface Shape {
    void getArea();
}
class Rec implements Shape {

    private int length,width;
    Rec(int length, int width) //throws NoValueException
    {
        if(length <=0 ||width <=0)
            throw new NoValueException("出现非法值"); //满足该条件程序调转
        this.length = length;
        this.width = width;
    }
    public void getArea()
    {
        System.out.println(length*width);
    }
}
class Circle implements Shape {
    public static final double PI = 3.14;
    private int radius;
    Circle (int radius)
    {
        if(radius <=0)
            throw new RuntimeException("圆半径出现非法值");
        this.radius = radius;
    }
    public void getArea()
    {
        System.out.println(radius*radius*PI);
    }
}
class ExceptionTest1 {
    public static void main(String  [] args)
    {
        Rec r = new Rec(3,4);
        r.getArea(); //如果参数有误长方形不存在,该方法不应该运行
        Circle c = new Circle(-6);
        c.getArea();
        System.out.println("over");//检测程序有了异常终止没有
    }
}

10 总结
我的总结:
1.异常就是对问题进行描述,将问题进行对象的封装
2.异常体系中所有类及其建立的对象都具备可抛性,即被throw和throws所操作
3.throw定义子在函数内,用于抛出异常对象;throws定义在函数上,用于抛出异常类,可以抛出多个异常类
4.当函数内有throw抛出异常对象,为用try处理,必须在函数上进行异常声明,
RuntimeException除外,即,函数内有RuntimeException异常,函数上可不声明异常
5.如果函数声明了异常,调用者需要进行处理,处理方法可try可throw
6.尽管catch块中有return,但finally块中的语句仍然会被读取;只有catch块有System.exit(0);是finally块中的语句才不会被对取。
7.自定义异常继承Exception或RuntimeException,具有可抛性;可以通过super语句使用父类中的功能,从而将程序中出现的特有问题分装。
8.异常处理原则:
a.调用抛出异常的功能时,抛出几个就用catch处理几个,父类的catch放在最下面;
b.处理异常不要用输出语句和e.printStackTrace();
c.对于本功能处理不了的异常,可继承在catch中抛出;
try
{
throw new AException();
}
catch (AException e)
{
throw e;
}
d.该功能处理不了有不属于该功能的异常,要转换后抛出相关异常
try
{
throw new AException();
}
catch (AException e)
{
throw new BException();
}
e.当异常可以处理,将其捕获处理,然后抛出和本功能相关的异常,让调用者知道并处理,比如:汇款的例子
try
{
throw new AException();
}
catch (AException e)
{
//处理了AException异常
throw new BException;
}

9.异常处理注意事项:
a.子类抛出的异常必须是父类异常的子类或者子集
b.如果父类或者接口没有异常抛出时,子类覆盖出现异常时只能try