在没有学习异常之前,我们所有的程序都是默认在理想状态下运行的,在实际开发中可能存在各种各样的问题,比如用户输入的数值不正确,,格式不正确,都会引起程序运行的错误。所谓的异常就是程序运行时出现的不正常的情况。异常也是现实生活中一个个具体的事物,java中的异常时通过java类的形式进行描述,并且封装成对象的。异常对象都是派生于Throwable类的一个实例。
二,java中的异常的体系结构:
Throwable
|——Error
|——Exception
|——RuntimeException
|——IOException
Error是严重异常,是JVM处理不了的异常,所以用户也处理不了,所以我们不需要去管。当然这种严重异常出错的概率也是比较小的。我们重点讨论的是Exception,需要我们程序员去处理的东西。
1,RuntimeException:
Exception的一个特殊子类异常RuntimeException(运行时异常),该异常在函数
部通过throw关键字抛出后可以不用再函数上声明,编译一样会通过,如果在函数上声明,调用者不用进行处理,也会通过;(面试常考)
2,体系中的省略号是指继承Exception,就是Exception的除RuntimeException的子类。继承Exception的子类,在函数内部抛出异常对象如果不在函数上声明,则编译不能通过,声明之后调用者不进行处理,编译也不能通过;
之所以不在函数上声明是因为不希望调用者处理,当该异常发生时,希望程序停止,因为在运行时出现了无法继续运行的情况,这时就需要停止程序对代码进行修改;
3,异常体系的特点:异常体系中所有类以及建立的对象都具有可抛性。
也就是说可以被throw和throws关键字操作;只有异常体系具备这个特点;
throw和throws的用法:
throw定义在函数内部,用于抛出异常对象;
throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开;
当内容有throw抛出的异常对象,并未进行try处理必须要在函数上用throws关键字抛出(声明),否则编译失败;
注意:函数内如果抛出的是RuntimeException,函数上可以不用声明。
三,异常的处理:
1,如果在函数上面进行声明过的异常,也就是用关键字throws在函数上面声明的异常,当在某个方法中调用这样的方法的时候,要么执行try...catch处理,要么向上一级抛出去,也就是在这个函数上面继续throws。这一点我们在使用java文档的时候可能会看到很多。当然不是所有的异常都可以进行throws的,将会在后面的笔记中总结到。
2,异常的分类:有两种:
(1),编译时被检测异常;
该异常在编译时如果没有处理(没有抛,也没有try)则编译失败。
该异常被标示,代表可以被处理;
(2),运行时异常,编译时不检测;
编译时不需要处理,编译不检查;
该异常发生时,建议不要处理,让程序停止,需要对代码进行修正;
3,异常处理的语句:
try{
需要被检测的代码;
}catch(){
处理异常的代码;
}finally{
一定会执行的代码;
}
三种形式:
1,形式一:
try{
}catch(){
}finally{
}
2,形式二
try{
}catch(){
}
3,形式三
try{
}finally{
}
Finally特点:
(1),fianlly里面通常定义的是关闭资源的代码,因为资源必须释放;
(2),finally只有一种情况下不会执行,当执行到System.exit(0)时,JVM结束,finally不会执行;
四,自定义异常方法:
1,自定义异常:是按照java的面型对象思想,将程序的特有问题进行封装;
在自定义异常中可以有两种选择,要么继承Exception要么继承RuntimeException
本程序重点讲解这两种用户自定义异常父类的不同。
(1),为了让该自定义类具有可抛性;
(2),让该类具备操作异类的共性方法;
当要定义自定义异常信息的时候,可以使用父类已经定义好的功能。异常信息传递给父类的构造函数;(多查看java文档)
class MyException extends Exception
{
MyException(String message) {
super(message);
}
}
示例一:编写一个程序,计算一个数除以另外一个数,在相除的时候,除数可能为零。定义一个负数异常继承Exception,当除数为0的时候,抛出异常,打印在控制台,并且终止整个程序,下面的程序不在执行。
class FushuException extends Exception {
/*
* private String msg; FushuException(String msg) { this.msg = msg; }
*
* public String getMessage() { return msg; }
*/
private int value;
FushuException(String msg, int value) {
super(msg);
this.value = value;
}
public int getValue() {
return value;
}
}
class Demon5 {
public int account(int a, int b) throws FushuException {
if (b < 0)
throw new FushuException("出现了负数的情况;", b);// 手动通过关键字自定义一个负数异常;
int c = a / b;
return c;
}
}
class ExceptionTest {
public static void main(String[] args) {
Demon5 d = new Demon5();
try {
System.out.println(d.account(4, -5));
} catch (FushuException e) {
System.out.println(e.toString());
// System.out.println("除数不能为负数!");
System.out.println("错误的负数是" + e.getValue());
}
}
}
示例二:将示例一的程序进行简化,可以直接将负数异常通过throw关键字在函数内部抛出,这时候就不需要在函数上面通过throws抛给上一级。下面的程序负数异常时继承自RuntimeException,当然也可以不用定义,直接throw new RuntimeException( “ 异常提示 ” );
class Test3 {
int div(int a, int b) {
if (b < 0)
throw new FushuException2("除数为负数了;");// 该异常发生后计算无法继续,希望程序停止,不需要在
// 函数上声明;
// 可以不用定义FushuException2,直接throw new RuntimeException("除数为零");
return a / b;
}
}
class ExceptionDemon {
public static void main(String[] args) {
Test6 t6 = new Test6();
int c = t6.div(5, -8);
System.out.println(c);
}
}
示例三:自定义继承自Exception和RuntimeException的综合例子。
需求:老师用电脑上课;(面向对象思想:名词提炼法)
老师类,电脑类
可能出现的异常:
电脑蓝屏;--->可以解决,重启电脑,继续上课;
电脑冒烟;--->老师不能解决,该异常跟老师没关系,如果继续往外抛,别的老师也解决不了,此时抛出老师自己的异常,不能上课了;当校长接收到这一异常时可以解决,就是放假休息的,等待电脑修好了继续上课;
class LanpingException extends Exception {
LanpingException(String msg) {
super(msg);
}
}
class MaoyanException extends RuntimeException {
MaoyanException(String msg) {
super(msg);
}
}
class NoplanException extends Exception {
NoplanException(String msg) {
super(msg);
}
}
class Computer {
private int state = 3;
public void run() throws LanpingException, MaoyanException {
if (state == 1)
System.out.println("电脑运行。。。");
else if (state == 2)
throw new LanpingException("电脑蓝屏了。。。请重启");
else if (state == 3)
throw new MaoyanException("电脑冒烟。。。");
}
public void reset() {
System.out.println("电脑重启。。。");
}
}
class Teacher {
private Computer cmpt;
private String name;
Teacher(String name) {
this.name = name;
cmpt = new Computer();
}
public void teach() throws NoplanException {
try {
cmpt.run();
} catch (LanpingException e) {
cmpt.reset();
} catch (MaoyanException e) {
test();
System.out.println(e.toString());
throw new NoplanException("无法继续上课。。。");
}
System.out.println(name + "老师讲课。。。");
}
public void test() {
System.out.println("学生做练习。。。");
}
}
class ExceptionExercise {
public static void main(String[] args) {
Teacher t = new Teacher("李");
try {
t.teach();
} catch (NoplanException e) {
System.out.println("全体放假。。。");
}
}
}
总结:前面已经给出了异常处理的几种代码的形式,其中finally也是十分重要的一个知识点。finally语句里面的内容无论什么情况都会执行;它的应用主要是在数据库上;因为数据库的连接资源是有限的,当发生异常时如果程序停止的话,数据库的连接如果没有断开,将会占用很多资源,这是finally语句就起到很大的作用。
public void method() throws SQLException {
连接数据库;
数据库的操作;
关闭数据库;//该动作无论数据库的操作是否成功,都必须进行;
try{
连接数据库;
数据库的操作;//throw new SQLException();
}catch(SQLException e) {
...
}finally {
关闭数据库;
}
}
try...catch的另外几种格式:try{...}catch(Exception e) {...} try{...}catch(Exception e) {...}finally {...} try{...}finally { //finally用于关闭资源...}//记住catch是用于处理异常,如果没有catch说明此异常没有被处理过,此时就必须声明出去;
五,子类覆盖父类时异常的处理规则:
1,子类在覆盖父类时如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2,如果在父类方法中抛出多个异常,子类再覆盖时只能抛出父类异常的子异常,或者不抛。例如:父类方法有A,B,C,D,E这几个异常,子类再覆盖该方法时只能抛出这五个异常的子集或者不抛。
3,如果父类或接口的方法中没有抛出异常,那么子类在覆盖该方法时也不可以抛出异常。如果子类方法发生了异常就必须用try语句解决,绝对不能向外抛。
示例分析:
class AException extends Exception {
...
}
class BException extends AException {
...
}
class CException extends Exception{
...
}
class Fu {
public void show() throws AException {
...
}
}
class Zi extends Fu
{
public void show() throws AException {//或者抛出BException,但是绝对不能抛CException,如果发生
//CException那么就必须在该方法中用try语句处理掉这个异常;
...
}
}
public class ExceptionTestDemon2 {
public static void main(String[] args) {
...
}
}
六,最后给出异常的综合应用的一个例子来说明异常的应用:
需求:计算一个园和长方形的面积;对于非法值得输入可以视为获取面积出现的问题;问题可以用异常来表示。
//首先自定义一个异常
class FushuException extends RuntimeException {
FushuException(String message) {
super(message);
}
}
//定义一个抽象类,将打印面积的方法放在抽象类中,子类必须重写该方法
abstract class Area {
public abstract void getArea();
}
//计算矩形面积
class Rect extends Area {
private double width, high;
Rect(double width, double high) {
if (width < 0 || high < 0)
throw new FushuException("长方形的长或宽不能为零。。。");
// 这里当然也可以用if语句来判断然后给出错误提示,但是在java中这样做是不提倡的;
// 因为这样写的话错误处理代码和正常流程代码联系非常紧密,阅读性比较差;正真做项目时
// 如果用if语句,那么if里面将会是一大片处理代码,阅读性非常差;而异常处理Exception的
// 好处在于可以将异常处理代码和正常流程代码分离开来,当问题发生时,可以不修改正常流程代码
// 而直接到异常处理代码区修改异常处理代码;
this.width = width;
this.high = high;
}
public void getArea() {
System.out.println(width * high);
}
}
//计算圆面积
class Circle extends Area {
private double radious;
public static final double PI = 3.14;
Circle(double radious) {
if (radious < 0)
throw new FushuException("圆半径为负数。。。。");
this.radious = radious;
}
public void getArea() {
System.out.println(radious * radious * PI);
}
}
class DoArea {
public void doArea(Area a) {
a.getArea();
}
}
class ExceptionArea {
public static void main(String[] args) {
DoArea d = new DoArea();
d.doArea(new Rect(2.0, 3.6));
d.doArea(new Circle(-2));
}
}