java异常

1.1 异常的简介

1.1.1 什么是异常

异常,是对程序在运行过程中,遇到的种种的不正常的情况的描述。异常,在Java中,使用 Exception类来描述。如果程序遇到了未经处理的异常,将会导致程序无法编译或无法继续运行。

1.1.2 异常的继承体系

在Java中,使用类 Throwable 来描述所有的不正常的情况。 Throwable有两个子类,Error 和 Exception 。其中 Error 用来描述发生在JVM级别的错误信息,这些错误,无法被处理。 Exception 用来描述程序在编译或者运行的过程中遇到的异常信息,这些异常一旦处理了,对程序的编译和运行是没有影响的。

1.1.3 异常的分类

普通的异常发生,将会导致程序无法进行正常的编译。这种异常,称为None-Runtime Exception,非运行时异常,或者称为编译时异常,也有人称之为检查性异常,原因在于该种异常,都是javac编译器能直接检查到的异常。

编译器javac.exe  在将源文件(*.java)编译成字节码文件(*.class)时,会主动检查代码的语法格式,如果不正确,就不进行编译工作。如果正确了,才会执行编译工作。

而在Exception的子类RuntimeException中,对异常进行了自动的处理,使其可以正常的编译 运行。但是一旦出现未经处理的异常,依然会导致程序无法继续向下执行。这样的异常称为-- Runtime Exception,运行时异常。 这类异常,通常编译器是不做任何检查的,所以也叫非检查性异常。

异常可以分为 运行时异常编译时异常

2.2 异常的处理

异常如果不处理,程序就停止了。但是在实际生产环境中,不能因为异常就终止程序,否则客户体验度会非常不好。

2.1.1 异常处理的语法

try {
   // 将可能会出现异常的代码放到这里执行
   // 一旦try中的代码出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。
}catch (需要捕获的异常的类型  标识符) {
   // 如果try中的代码出现了异常,并且异常对象的类型和捕获的类型一致 
   // 这里的代码将会执行
   // 一旦一个异常被捕获了,那么这个异常将不再影响程序的编译和执行
}
1. 将可能出异常的代码片段放入try的语句块中, 
2. jvm在运行程序时,如果出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。
3. jvm在运行程序时,如果出现了异常,会自动创建一个具体的异常对象
4. catch(需要捕获的异常的类型  标识符){}用于捕获异常操作
5. jvm创建的异常对象如果与catch捕获的异常对象类型匹配(或者向上造型没问题),就会就将地址值赋值给异常类型变量。
6. catch捕获到异常对象后,就会执行对应的{}里的代码。这个异常将不再影响程序的编译和执行
7. catch模块如何处理异常呢? 比如打印异常信息,供程序员查看,然后进行调试,或者继续抛给调用者

案例:

// 1、实例化一个数组
int[] array = new int[5];
try {
   // 2、访问数组中的元素
   array[5] = 10;
   System.out.println("aaa");
}catch (RuntimeException e) {
   System.out.println("一个数组下标越界异常出现了 "); }
​
// 3、
System.out.println("hello world");

2.1.2 多种异常的处理

try {
   // 可能出现异常的代码
}catch (异常类型1 标识符) {
   // 针对异常类型1的处理
}catch (异常类型2 标识符) {
   // 针对异常类型2的处理
}catch (异常类型3 标识符) {
   // 针对异常类型3的处理
}
...

需要注意异常的书写顺序。

如果多个catch的异常之间不存在继承关系,不需要考虑书写顺序。
如果多个catch的异常之间存在继承关系,则必须子类异常在前,父类异常在后。

如果处理的多种异常没有继承关系,并且处理方式相同,可以简写成如下语法

try {
   // 可能会出现多种异常的代码
}catch (异常类型1 | 异常类型2 | ... 标识符) {
   // 出现了多种异常之一,都可以被这里捕获
}

或者使用父类异常处理

try {
   // 可能会出现多种异常的代码
}catch (Exception e) {
   // 处理的代码逻辑都一样,就一种。
}

案例演示:

public class Test01 {
    public static void main(String[] args) {
        /** 不存在继承关系的异常类型名 */
        try {
            String str = null;
            int a = str.length();
        }catch (IndexOutOfBoundsException e){
            System.out.println("------IndexOutOfBoundsException-------");
        }catch (NullPointerException e){
            System.out.println("------NullPointerException-------");
        }catch (RangeException e){
            System.out.println("------RangeException-------");
        }
​
​
        /** 存在继承关系的异常类型名 */
        try {
            FileInputStream fis = new FileInputStream("");
            //上述代码会出现IOException对象     和RuntimeException是并列关系
        }catch (RuntimeException e){
            System.out.println("------RuntimeException-------");
        }catch (Exception e) { //Exception 是 IOException父类型,因此变量e可以存储子类对象的地址
            System.out.println("---------Exception--------");
            e.printStackTrace();
        }
    }
}

2.1.3 finally模块

finally用在try后面,或者catch后面,作为异常捕获的结尾。

特点:finally中的语句始终会执行。(无论try中的代码是否出现了异常,这里的代码都会执行)

使用场景:会在finally中做资源释放、流的关闭等操作。

案例1:

public class Demo1 {
    public static int test1(){
        int i = 1;
        try {
            i++;
            System.out.println("try block, i = "+i);
        } catch (Exception e) {
            i--;
            System.out.println("catch block i = "+i);
        } finally {
            i = 10;
            System.out.println("finally block i = "+i);
        }
        return i;
    }
    public static void main(String[] args){
        int result = test1();
        System.out.println(result);
    }
}
运行结果:
  try block, i = 2  
  finally block i = 10  
  10

案例2:

public class Demo2 {
    public static int test2(){
        int i = 1;
        try {
            i++;
            throw new Exception();
        } catch (Exception e) {
            i--;
            System.out.println("catch block i = " + i);
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
        return i;
    }
    public static void main(String[] args){
        int result = test2();
        System.out.println(result);
    }
}
运行结果:
  catch block i = 1  
  finally block i = 10  
  10

案例3:

public class Demo3 {
    public static int test3(){
        //try 语句块中有 return 语句时的整体执行顺序
        int i = 1;
        try{
            i++;
            System.out.println("try block, i = " + i);
            return i;
        } catch (Exception e) {
            i ++;
            System.out.println("catch block i = " + i);
            return i;
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
    }
    public static void main(String[] args){
        int result = test3();
        System.out.println(result);
    }
}
运行结果:
  try block, i = 2  
     finally block i = 10  
        2

案例4:

public class Demo4 {
    public static int test4(){
        //finally 语句块中有 return 语句
        int i = 1;
        try {
            i++;
            System.out.println("try block, i = " + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch block i = " + i);
            return i;
        } finally {
            i++;
            System.out.println("finally block i = " + i);
            return i;
        }
    }
    public static void main(String[] args){
        int result = test4();
        System.out.println(result);
    }
}
运行结果:
  try block, i = 2  
     finally block i = 3  
        3

面试题 : 简述  final、finally、finalize 的区别。

3.3 自定义异常

3.3.1 为什么要自定义异常

如果系统给我们提供的异常类型,已经不能满足我们的需求了,或者不知道用哪个了。此时就需要进行异常的自定义。

3.3.2 如何自定义异常

通过阅读异常源代码:发现java中所有的异常类,都是继承Throwable,或者继承Throwable的子类。这样该异常才可以被throw抛出。说明这个异常体系具备一个特有的特性:可抛性:即可以被throw关键字操作。

并且查阅异常子类源码,发现每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的封装。

例如NullPointerException异常类源代码:

public class NullPointerException extends RuntimeException {
    public NullPointerException() {
        super();//调用父类构造方法
    }
​
    public NullPointerException(String s) {
        super(s);//调用父类具有异常信息的构造方法
    }
​
}

因此,我们就可以模仿这种写法,来自定义我们自己的异常类型。

  • 继承自Exception类或者RuntimeException类或者异常子类。

  • 继承自Exception类的异常是 编译时异常。

  • 继承自RuntimeException类的异常是运行时异常。

格式如下:

Class 异常名 extends Exception{ //或继承RuntimeException
   public 异常名(){
   }
   public 异常名(String s){ 
      super(s); 
   }
}

案例演示:

定义Person类,包含name与age两个成员变量。在Person类的有参数构造方法中,进行年龄范围的判断,若年龄为负数或大于120岁,则抛出NoAgeException异常,异常提示信息“年龄数值非法”。

class NoAgeException extends Exception{
    NoAgeException() {
        super();
    }
​
    NoAgeException(String message) {
        super(message);
    }
}
class Person{
    private String name;
    private int age;
    Person(String name,int age) throws NoAgeException {
//加入逻辑判断。
        if(age<0 || age>200) {
            throw new NoAgeException(age+",年龄数值非法");
        }
        this.name = name;
        this.age = age;
    }
//定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
    public String toString() {
        return "Person[name="+name+",age="+age+"]";
    }
}

测试类

class ExceptionDemo{
    public static void main(String[] args) {
        try {
            Person p = new Person("xiaoming",20);
            System.out.println(p);
        }
        catch (NoAgeException ex){
            System.out.println("年龄异常啦");
        }
        System.out.println("over");
    }
}

3.3.3 throw和throws关键字

throw:

用在程序方法中,表示抛出异常。一个异常对象实例化完成后,没有任何意义,只有当把这个异常抛出之后,这个异常才会生效,具有阻止程序的编译或者运行的意义。

throws:

1. 书写位置:在方法的定义格式中,也就是形参列表的后面。用于声明异常类型。
     修饰词  返回值类型 方法名(形参列表) throws 异常类型名{
     
     }
2. 当我们throw的是RuntimeException类的异常时,不需要throws来声明异常类型, 声明没有意义,JVM运行程序才能知道。
3. 当我们throw的是Exception异常时,必须使用throws来声明异常类型,用来告知  谁调用这个方法,谁处理异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值