一、异常的产生
先看下面的这个demo:
/*
* 异常的产生
*/
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println(0/0);
System.out.println("hello world");
}
}
此时程序就产生 的异常如下:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exceptions.ExceptionDemo.main(ExceptionDemo.java:11)
分析:
- JVM检测到了问题(0是除数的问题),于是程序发生了异常
- JVM就把这个异常进行了对象的封装
new ArithmeticException()
- 异常对象被抛给调用者main中,main方法接收到这个异常后,由于程序中没有对异常进行处理的方法,因此mian方法也不能处理此异常,于是异常又被抛出到JVM中
- JVM接受到异常后,采取了默认的处理措施,停止运行程序,于是后面的这个syso语句没有执行。
异常也是对象,上述例子中异常对象的描述类是ArithmeticException,它是用来描述数学中算数问题的 异常类。比如还有常见的空指针异常描述类NullPointerException。
二、异常的继承体系
Throwable类是所有异常和错误的父类
Error 错误
-
Exception 异常
错误:程序出现了严重的问题,不修改代码,根本不能运行,人得了非典,艾滋,癌
异常:程序出现了比较轻的问题,处理掉之后,继续运行,人得了阑尾炎,感冒
Exception类是所有异常的父类
- 非RuntimeException
- RuntimeException
Throwable中的方法:
- String toString()重写Object类的方法,异常信息的详细描述?
- String getMessage() 返回异常信息的简短描述
- void printStackTrace() 异常信息输出到控制台
三、异常的两种处理方式
-
直接处理掉异常
demo:
try{ 尝试捕获异常的代码 }catch(异常类 异常变量){ 异常处理代码 }
-
第二种处理方式就是抛出异常
- throw 手动抛出异常,后面写的是new异常的对象,写在方法中
- throws 方法声明抛出异常,后面写的是异常类,写在方法的声明上
demo:
/* 异常第二种处理方式,抛出异常 throw throws的用法 ExceptionDemo2.java:21: 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明 以便抛出 throw new Exception(); 方法中,有异常抛出,但是没有处理过,因此编译失败 异常的编译提示,是Java编译时的最后提示 */ class ExceptionDemo2 { public static void main(String[] args) throws Exception { //System.out.println("Hello World!"); //main中调用了method方法,方法抛出了异常 //main有2个选择方法,一个是try...catch //另外一个是,异常我也不处理,交给我的调用者处理 method(-5); } /* 如果方法的参数小于0 程序出现异常,如果参数大于0 ,程序是正常的 方法自己,不想处理这个异常,把异常交给调用者处理 在方法声明上,抛出异常,声明出来有异常,交给调用者 throws 异常类 */ public static void method(int x)throws Exception{ if(x < 0) //程序出现了问题 //手动抛出异常 throw new Exception("程序出现了异常了"); else System.out.println("程序正常"); } }
四、多层异常的处理
demo
/*
多层的异常处理方法调用
*/
class ExceptionDemo3
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
try{
methodA();
}catch(Exception e){
}
}
public static void methodA()throws Exception{
methodB();
}
public static void methodB()throws Exception{
methodC();
}
public static void methodC()throws Exception{
throw new Exception();
}
}
五、finally代码块
- finally可以跟随try出现,也可以跟随try...catch出现
- finally代码块中的程序,必须要运行
- finally实际的开发意义,释放资源
demo
/*
finally代码块
一定要执行
*/
class ExceptionDemo4
{
public static void main(String[] args)
{
try{
method(1);
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("这里的程序必须执行");
}
}
public static void method(int x)throws Exception{
if(x == 1)
throw new Exception("异常了!!");
else
System.out.println("程序正常了");
}
}
一个finally的特例:
/*
demo:true or false
*/
class ExceptionDemo5{
public static void main(String[] args){
System.out.println(method());
}
public static boolean method(){
try{
return false;
}catch(Exception e){
}finally{
return true;
}
}
}
//结果:true
/*
demo2:2 or 10
开发中,不要再try catch中写return
*/
class ExceptionDemo6{
public static void main(String[] args){
System.out.println(method());
}
public static int method(){
int i = 1;
try{
return ++i;
}catch(Exception e){
return 100;
}finally{
i=10;
}
}
}
//结果:2
六、编译时期的异常
调用一个方法,这个方法抛出一个异常,此时调用者必须处理异常,否则编译失败。
Demo
/*
编译时期的异常
*/
class ExceptionDemo2{
public static void main(String[] args){
System.out.printf("hello wordl");
method();
}
public static void method() throws Exception{}
}
结果如下:
ExceptionDemo2.java:10: 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明
以便抛出
method();
^
1 个错误
改进,加上try catch对异常进行处理,错误提示消失。
/*
编译时期的异常
*/
class ExceptionDemo2{
public static void main(String[] args){
System.out.printf("hello wordl");
try{
method();
}catch(Exception e){}
}
public static void method() throws Exception{}
}
七、运行时期的异常
运行时期的异常一旦发生了,后面的所有程序都不会接着往下执行,所以设计运行时期的异常的初衷就很明显了,这个异常就是让开发人员看的,发生运行异常,就必须去修改原代码,而不是去处理异常。
Demo
/*
运行时期的异常的特点
*/
class ExceptionDemo3{
public static void main(String[] args){
//调用者不知道方法会出现异常,所以就不用处理
//这种时候,需要修改代码,而不需要处理
method();
}
public static void method(){
//手动抛出一个异常
throw new RuntimeException();
}
}
Demo2
/*
要求:计算正方形面积,边长的平方
定义方法:求面积,返回结果
*/
class ExceptionDemo4{
public static void main(String[] args){
System.out.println(method(-8));
}
public static int method(int num){
if(num <= 0){
//边长不合法,没有必要计算
throw new RuntimeException("<= 0");
}
return num * num;
}
}
常见的运行时期的异常:
异常对象 | 实际含义 |
---|---|
IndexOutOfBoundsException | 越界(字符串,和数组) |
NullPointerException | 空指针 |
ClassCastException | 类型转换异常 |
NoSuchElementException | 没有元素被取出 |
IllegalArgumentException | 无效参数异常 |
八、自定义异常
Java中异常体系,将很多的情况都做了异常封装,但是实际的开发中,不可能把所有的异常都描述完毕,需要自定义的异常,用来描述自己程序中可能发生的异常。
自定义异常步骤:
- 定义类,后缀名Exception继承Exception类,或者继承RuntimeException
- 异常信息,自定义的异常类的构造方法,把异常信息使用super传递到父类
注意:只有异常类,才具备可抛性 throw new 异常体系的类
通过刚才案例:
- 如果一个方法中,抛出多个异常,必须要throws声明多个异常(运行时起除外)
- 调用者,使用多个catch进行异常的捕获
- 多个catch中,最大(继承关系)的父类,写在最后面,否则编译失败
Demo
package exceptions;
/*
* 自定义异常
*/
//负数异常
class FuShuException extends Exception{
FuShuException(){}
FuShuException(String info){
super(info);
}
}
//0异常
class ZeroException extends Exception{
ZeroException(){}
ZeroException(String message){
super(message);
}
}
public class ExceptionDemo5 {
public static void main(String[] args) {
try{
getArea(0);
}catch(FuShuException e){ //多异常,就多catch,范围越大的往后写
e.printStackTrace();
}catch(ZeroException e){
e.printStackTrace();
}
}
public static int getArea(int num) throws FuShuException,ZeroException{
if(num < 0){
throw new FuShuException("边长是负数");
}
else if(num == 0){
throw new ZeroException("边长为0");
}
return (int)Math.pow(num,2);
}
}
九、继承异常
前提:子类重写父类的方法
- 父类的方法抛出了异常,子类重写后,异常:父类抛异常,子类可抛可不抛,但是,如果子类抛,不能抛出比父类还大的异常
- 父类的方法不抛异常,子类重写后,子类不能抛出异常。如果子类重写的方法中,调用了一个抛异常的方法,子类别无选择,只能
try...catch
处理异常
demo
package exceptions;
/*
* 自定义异常
* 1.父类方法抛异常,子类重写后可抛可不抛,若是抛,则要小于父类(前提是子类重写了父类方法)
* 2.父类方法不抛异常,子类也不能抛,若是子类调用的方法抛了异常,子类只能try catch
*/
//A是父类异常类
class AException extends Exception{
}
//B继承A
class BException extends AException{
}
//C和A是兄弟类
class CException extends Exception{
}
class Zi extends Fu{
public void show() throws BException{}
}
class Fu{
public void show() throws AException{}
}
public class ExceptionDemo6 {
public static void main(String[] args) throws AException{
Fu f = new Zi();
f.show();
}
}