黑马程序员_Java基础_异常

时间:2022-02-22 21:44:18
一,异常的概念:

在没有学习异常之前,我们所有的程序都是默认在理想状态下运行的,在实际开发中可能存在各种各样的问题,比如用户输入的数值不正确,,格式不正确,都会引起程序运行的错误。所谓的异常就是程序运行时出现的不正常的情况。异常也是现实生活中一个个具体的事物,java中的异常时通过java类的形式进行描述,并且封装成对象的。异常对象都是派生于Throwable类的一个实例。

二,java中的异常的体系结构:

Throwable

|——Error

|——Exception

|——RuntimeException

|——IOException

Error是严重异常,是JVM处理不了的异常,所以用户也处理不了,所以我们不需要去管。当然这种严重异常出错的概率也是比较小的。我们重点讨论的是Exception,需要我们程序员去处理的东西。

 

1RuntimeException

Exception的一个特殊子类异常RuntimeException(运行时异常),该异常在函数

部通过throw关键字抛出后可以不用再函数上声明,编译一样会通过,如果在函数上声明,调用者不用进行处理,也会通过;(面试常考)

 

2,体系中的省略号是指继承Exception,就是Exception的除RuntimeException的子类。继承Exception的子类,在函数内部抛出异常对象如果不在函数上声明,则编译不能通过,声明之后调用者不进行处理,编译也不能通过;

之所以不在函数上声明是因为不希望调用者处理,当该异常发生时,希望程序停止,因为在运行时出现了无法继续运行的情况,这时就需要停止程序对代码进行修改;

 

3,异常体系的特点:异常体系中所有类以及建立的对象都具有可抛性。

也就是说可以被throwthrows关键字操作;只有异常体系具备这个特点;

 

throwthrows的用法:

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);
}
}

示例三:自定义继承自ExceptionRuntimeException的综合例子。

需求:老师用电脑上课;(面向对象思想:名词提炼法)

老师类,电脑类

 

可能出现的异常:

电脑蓝屏;--->可以解决,重启电脑,继续上课;

电脑冒烟;--->老师不能解决,该异常跟老师没关系,如果继续往外抛,别的老师也解决不了,此时抛出老师自己的异常,不能上课了;当校长接收到这一异常时可以解决,就是放假休息的,等待电脑修好了继续上课;


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));
}
}