面向对象——异常

异常

异常:就是程序在运行时出现的不正常情况。
异常的由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述,并封装成对象。异常其实就是java对不正常情况进行描述后的对象体现。

异常的体系

对于问题的划分有两种:一种是严重的问题,一种是非严重的问题。

  • 对于严重的,java通过Error类进行描述(通常出现重大问题如:运行的类不存在或者内存溢出等)。对于Error一般不编写针对性的代码对其进行处理。
    Error是由系统底层发生的,它将告诉JVM,JVM告诉使用者。Error一旦出现不做针对性的处理,直接修改代码。
  • 对于非严重的,java通过Exception类进行描述。Exception是由JVM发生的,并告诉调用者,对于Exception可以使用针对性的处理方式进行处理。

但无论Error或者Exception都具有一些共性内容,比如:不正常情况的信息,引发原因等。

异常的处理

java提供了两种对异常的处理方式:

  • 遇到问题不进行具体的处理,而是继续抛给调用者。其实就是在函数上通过throws关键字声明异常,告诉调用者处理。
    对这种处理方式的详细解释:在编写功能时,编写者会知道该功能有可能发生问题,而这个问题很容易来自于调用者传递的参数,而导致功能无法运行。这时发生的问题就应该让调用者知道,并最后让调用者有预先的处理方式,所以在定义功能时,需要在功能上对有可能发生的问题进行声明。声明问题需要使用关键字(throws):throws 异常类,声明的目的:就是让调用者可以进行处理。
  • 针对性的处理方式:捕获!

    try 
    {
        // 有可能发生异常的代码
    }
    catch(异常类 变量)
    {
        // 这是真正的捕获,处理异常的代码;
    }
    finally
    {
        // 一定会被执行的代码;
    }

对捕获到的异常对象进行常见方法操作

  • getMessage();获取异常信息,返回字符串
  • toString();获取异常类名和异常信息,返回字符串
  • printStackTrace();获取异常类名和异常信息,以及异常出现在程序中的位置,返回值为void。JVM默认处理收到的异常就是调用这个方法,将信息显示在屏幕上
  • printStackTrace(PrintStream s);通常用该方法将异常内容保存在日志文件中,以便查阅
    例,

    class Demo {
        int div(int a, int b) throws Exception { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
            return a/b; // new ArithmeticException();
        }
    }
    class ExceptionDemo {
        public static void main(String[] args) {
            Demo d = new Demo();
            try {
                int x = d.div(4, 1); // new ArithmeticException();
                System.out.println("x="+x);
            } catch(Exception e) { // Exception e = new ArithmeticException();
                System.out.println("除零啦!");
                System.out.println(e.getMessage()); // / by zero
                System.out.println(e.toString());// 异常名称:异常信息
                e.printStackTrace(); // 异常名称,异常信息,异常出现的位置
                // 其实JVM默认的异常处理机制,就是在调用printStackTrace(),打印异常在堆栈中的跟踪信息
            }
            System.out.println("over");
        }
    }

    在函数上声明异常(throws)。便于提高安全性,让调用者进行处理,不处理编译失败。

对多异常的处理

  1. 声明异常时,建议声明更为具体的异常,这样处理的可以更具体。
  2. 对方声明几个异常,就对应有几个catch块,不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。
    建议在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义一句:e.printStackTrace();,也不要简单的就书写一条输出语句。
    例,

    class Demo {
        int div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
            int[] arr = new int[a];
    
            System.out.println(arr[4]);
            return a/b; // new ArithmeticException();
        }
    }
    class ExceptionDemo {
        public static void main(String[] args) {
            Demo d = new Demo();
            try {
                int x = d.div(5, 0); // new ArithmeticException();
                System.out.println("x="+x);
            } catch(ArithmeticException e) {
                System.out.println(e.toString());
                System.out.println("除零啦!");
            } catch(ArrayIndexOutOfBoundsException e) {
                System.out.println(e.toString());
                System.out.println("角标越界啦!");
            } catch(Exception e) {
                System.out.println("haha:"+e.toString());
            } 
            System.out.println("over");
        }
    }

自定义异常

因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。所以对于这些特有的问题可以按照java的对问题封装的思想,将特有的问题,进行自定义的异常封装。
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部try catch处理,要么在函数上声明让调用者处理。一般情况下,函数内出现异常,函数上需要声明。
自定义异常如何定义异常信息呢?
答:因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类,通过super语句,那么就可以直接通过getMessage()获取自定义的异常信息。
自定义异常必须是自定义类继承Exception。继承Exception的原因:异常体系有一个特点,因为异常类和异常对象都被抛出,它们都具备可抛性,这个可抛性是Throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throwsthrow操作。
throwsthrow的区别:

  • 位置不同。
    • throws使用在函数上,throws后面跟的是异常类,可以跟多个,用逗号隔开。
    • throw使用在函数内,throw后跟的是异常对象。
  • 功能不同。
    • throws用来声明异常,让调用者知道该功能有可能出现的问题,并由调用者可以给出预先的处理方式。
    • throw抛出具体的问题对象,执行到throw,功能就已经结束了,跳转到调用者,并将具体的问题对象也抛给调用者。

例,

/*
需求:在本程序中,对于除数是-1,也视为是错误的,是无法进行运算的,那么就需要对这个问题进行自定义的描述
*/
class FuShuException extends Exception {
    private int value;
    FuShuException() {
        super();
    }
    FuShuException(String msg, int value) {
        super(msg);
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}
class Demo {
    int div(int a, int b) throws FuShuException {
        if(b < 0)
            throw new FuShuException("出现除数是负数的情况----- / by fushu", b); // 手动通过throw关键字抛出一个自定义异常对象
        return a/b;
    }
}
class ExceptionDemo2 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int x = d.div(4, -9);
            System.out.println("x="+x);
        } catch(FuShuException e) {
            System.out.println(e.toString());
            // System.out.println("除数出现负数了");
            System.out.println("错误的负数是:"+e.getValue());
        }
        System.out.println("over");
    }
}

RuntimeException

Exception中有一个特殊的子类异常RuntimeException(运行时异常)。

  • 如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。之所以不用在函数上声明,是因为不需要让调用者处理,当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,希望程序员对代码进行修正。
  • 如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

自定义异常时,如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。所以,对于异常可分为两种:

  • 编译时被检测的异常
  • 编译时不被检测的异常(运行时异常,RuntimeException以及其子类)

例,

class FuShuException extends RuntimeException {
    FuShuException(String msg) {
        super(msg);
    }
}

class Demo {
    int div(int a, int b) {
        if(b < 0)
            throw new FuShuException("出现了除数为负数啦!");
        if(b == 0) 
            throw new ArithmeticException("被零除啦!");
        return a/b;
    }
}
class ExceptionDemo3 {
    public static void main(String[] args) {
        Demo d = new Demo();
        int x = d.div(4, -9);
        System.out.println("x="+x);
        System.out.println("over");
    }
}

异常的小结:异常其实就是将问题封装成对象,并抛给调用者。如果声明了,就需要调用者处理(继续声明or捕获)。异常什么时候声明,什么时候捕获呢?功能内部可以解决,就捕获,不能解决或者解决了还必须告诉调用者问题,这时就应该声明。
现在来看一个异常的一个小练习:毕老师用电脑上课。
分析:开始思考上课中出现的问题。比如问题是:电脑蓝屏、电脑冒烟。要对问题进行描述,封装成对象。可是当冒烟发生后,出现讲课进度无法继续,出现了讲师的问题,课时计划无法完成。

// 蓝屏是可以处理的,继承Exception
class LanPingException extends Exception
{
    LanPingException()
    {
        super();
    }
    LanPingException(String message)
    {
        super(message);
    }

}

// 冒烟
class MaoYanException extends Exception
{
    MaoYanException()
    {
        super();
    }
    MaoYanException(String message)
    {
        super(message);
    }

}

// 课时无法进行
class NoPlanException extends Exception
{
    NoPlanException()
    {
        super();
    }
    NoPlanException(String message)
    {
        super(message);
    }   
}

class Computer
{
    private int state = 2; //  0:正常状态

    public void run() throws LanPingException, MaoYanException
    {
        if (state == 1)
            throw new LanPingException("电脑蓝屏啦!");
        if (state == 2)
            throw new MaoYanException("电脑冒烟啦!");
        System.out.println("电脑运行");
    }

    public void reset()
    {
        System.out.println("电脑重启");
        state = 0;
    }
}
class Teacher
{
    private String name;
    private Computer comp;

    Teacher(String name)
    {
        this.name = name;
        comp = new Computer();
    }

    // 讲课
    public void prelect() throws NoPlanException
    {
        try
        {
            comp.run();
            System.out.println("讲课");
        }
        catch (LanPingException e)
        {
            System.out.println(e.toString());
            comp.reset();
            // 继续讲课
            prelect();
        }
        catch (MaoYanException e) // MaoYanException e = new MaoYanException("...");
        {
            System.out.println(e.toString());
            test();
            throw new NoPlanException("课时进度停止"); // 继续抛,但进行异常转换。
        }
    }

    public void test()
    {
        System.out.println("练习");
    }
}

class ExceptionTest 
{
    public static void main(String[] args) 
    {
        Teacher t = new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch (NoPlanException e)
        {
            System.out.println("换老师");
        }

    }
}

finally代码块

定义一定执行的代码。通常用于关闭资源,即无论是否有异常发生,都要对资源进行释放。释放资源的动作就定义在finally代码块中。
例1,

class ExceptionDemo
{
    public static void main(String[] args) 
    {
        try
        {
            int num = 4 / 0;
            System.out.println("num = " + num);
        }
        catch (Exception e)
        {
            System.out.println(e.toString());
            return;
            // System.exit(0); // 退出JVM,只有这种情况,finally才不执行。
        }
        finally
        {
            System.out.println("finally");
        }
        System.out.println("over");
    }
}

例2,

class NoValueException extends Exception {

}
public void operate() throws NoValueException
{
    // 连接数据库
    try 
    {
        // 数据操作 throw new SQLException();
    } 
    catch (SQLException e)
    {
        // 解决了数据库异常
        throw new NoValueException();
    }   
    finally {
        // 关闭数据库
    }
}

记住一点:catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。

异常在子父类覆盖中的体现

  • 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类或者不抛。
  • 如果父类方法抛出多个异常,那么子类在覆盖这个方法时,只能抛出父类异常的子集(不抛也行)
  • 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。

练习:有一个圆形和长方形,它们都可以获取面积,对于面积如果出现非法的数值,视为是获取面积出现问题。问题通过异常来表示。
解:先要对这个程序进行基本设计。

class NoValueException extends RuntimeException {
    NoValueException(String message) {
        super(message);
    }
}

interface Shape {
    void getArea();
}

class Rec implements Shape {
    private int len, wid;
    Rec(int len, int wid) {
        if(len <= 0 || wid <= 0) 
            throw new NoValueException("出现非法值");
        this.len = len;
        this.wid = wid;
    }
    public void getArea() {
        System.out.println(len*wid);
    }
}

class Circle implements Shape {
    private int radius;
    public static final double PI = 3.14;
    Circle(int radius) {
        if(radius <= 0)
            throw new NoValueException("非法值");
        this.radius = radius;
    }
    public void getArea() {
        System.out.println(radius*radius*PI);
    }
}

class ExceptionTest {
    public static void main(String[] args) {
        Rec r = new Rec(3, 4);
        r.getArea();
        Circle c = new Circle(-8);

        System.out.println("over");
    }
}

总结

异常:对问题的描述,将问题进行对象的封装。


异常体系:

Throwable
   |---Error
   |---Exception
           |---RuntimeException

异常体系的特点:异常体系中的所有类以及建立的对象都具有可抛性,也就是说可以被throwthrows关键字所操作。只有异常体系具备这个特点。


throwthrows的用法:

  • throw定义在函数内,用于抛出异常对象。
  • throws定义在函数上,用于抛出异常类,可以抛出多个,用逗号隔开。

当函数内容有throw抛出异常对象,并未进行try处理,必须要在函数上声明,否则编译失败。
注意:RuntimeException除外,也就是说,函数内如果抛出的是RuntimeException异常,函数上可以不用声明。


如果函数声明了异常,调用者需要进行处理,处理方式可以throws也可以try
异常有两种:

  • 编译时被检测异常
    • 该异常在编译时,如果没有处理(没有抛也没有try),编译失败
    • 该异常被标识,代表着可以被处理
  • 运行时异常(编译时不检测)
    • 在编译时,不需要处理,编译器不检查
    • 该异常的发生,建议不处理,让程序停止,需要对代码进行修正

异常处理语句:

try {
    需要被检测的代码;
} catch() {
    处理异常的代码;
} finally {
    一定会指执行的代码;
}

有三种结合格式:

  1. try {
        需要被检测的代码;
    } catch() {
        处理异常的代码;
    }
  2. try {
        需要被检测的代码;
    } finally {
        一定会指执行的代码;
    }
  3. try {
        需要被检测的代码;
    } catch() {
        处理异常的代码;
    } finally {
        一定会指执行的代码;
    }

注意:

  • finally中定义的通常是关闭资源代码,因为资源必须释放。
  • finally只有一种情况不会执行,当执行到System.exit(0);finally不会被执行。

自定义异常:定义类继承Exception或者RuntimeException

  1. 为了让自定义类具备可抛性
  2. 让该类具备操作异常的共性方法

当要定义自定义异常的信息时,可以使用父类已经定义好的功能。将异常信息传递给父类的构造函数。

class MyException extends Exception {
    MyException(String message) {
        super(message);
    }
}

自定义异常:按照java的面向对象思想,将程序出现的特有问题进行封装。


异常的好处:

  1. 将问题进行封装
  2. 将正常流程代码和问题处理代码相分离,方便于阅读

异常的处理原则:

  1. 处理方式有两种:try或者throws
  2. 调用到抛出异常的功能时,抛出几个,就处理几个。一个try对应多个catch
  3. 多个catch,父类的catch放到最下面
  4. catch内,需要定义针对性的处理方式,不要简单的定义printStackTrace或者输出语句。也不要不写

当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。

try{
    throw new AException();
} catch(AException e) {
    throw e;
}

如果该异常处理不了,但并不属于该功能出现的异常。可以将异常转换后,再抛出和该功能相关的异常。

try{
    throw new AException();
} catch(AException e) {
    throw new BException();
}

或者异常可以处理,但需要将异常产生后,和本功能相关的问题提供出去,让调用者知道并处理,即将捕获异常处理后,转换新的异常抛出。(比如,汇款的例子)

try{
    throw new AException();
} catch(AException e) {
    对AException处理
    throw new BException();
}

异常的注意事项:在子父类覆盖时

  1. 子类抛出的异常必须是父类的异常的子类或者子集
  2. 如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛

包(package)

  • 对类文件进行分类管理
  • 给类提供多层命名空间
  • 写在程序文件的第一行
  • 类名的全称是包名.类名
  • 包也是一种封装形式

总结:

  • 包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
  • 不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
  • 包与包之间可以使用的权限只有两种:public/protected(只给子类使用)

四种访问权限

publicprotecteddefaultprivate
同一类中
同一包中
子类
不同包中

import

为了简化类名的书写,使用一个关键字——importimport导入的是包中的类,不导入包中的包
建议:

  • 不要写通配符*,需要用到包中的哪个类,就导入哪个类
  • 定义包名不要重复,可以使用url来完成定义,url是唯一的

例,

package pack;
import packb.haha.hehe.heihei.*;
import packa.*;

class PackageDemo {
    public static void main(String[] args) {
        DemoC c = new DemoC();
    }
}
  • 编译:javac -d . PackageDemo.java.指定的是当前目录,如果编译成功,会在当前目录下生成一个pack的文件夹,里面是编译的class文件。当然了也可以指定其他你想指定的任何目录,如:c:\myclass
  • 运行:java pack.PackageDemo

Jar包

Java的压缩包:

  • 方便项目的携带
  • 方便于使用,只要在classpath设置jar路径即可
  • 数据库驱动, SSH框架等都是以jar包体现的

Jar包的操作
通过jar.exe工具对jar的操作。

  • 创建jar包
    • jar -cvf mypack.jar packa packb
  • 查看jar包
    • jar -tvf mypack.jar [>定向文件]
  • 解压缩
    • jar -xvf mypack.jar
  • 自定义jar包的清单文件
    • jar –cvfm mypack.jar mf.txt packa packb

jar包中的内容很多,在dos命令行环境中一屏显示不过来,可用如下命令:

C:\myclass>jar -tf liayun.jar >c:\1.txt

即将yelei.jar包中的内容存放到c盘下1.txt文本文件中。
数据重定向——数据不想在一个地方(例如dos命令行)显示,而想在文件中显示。可用如下命令:

C:\>dir >c:\2.txt

即将c盘目录下的文件信息存放在c盘下2.txt文本文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值