异常(exception)

异常

什么是异常

  1. 程序在运行过程中发生的意外情况,称之为异常:除数为0,数组小标越界…
  2. 异常是一种信号,用于调用者传递信息,表示程序发生了意外情况
  3. 程序运行时一旦出现了异常,将会导致程序立即终止,异常之后的代码都无法继续执行,那么为了保持程序有一个健壮的体系,不会因为一点小错误而终止,就需要对异常进行处理(使用java中的try catch,异常处理机制)

异常入门

先看以下代码:

public class test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a/b);
        System.out.println("程序继续执行");
    }
}

因为被除数是不能为0的,所以被报一个算术错误ArithmeticException,导致下方的代码无法执行。
那么在现实开发中,一般都是比较大的项目,需要保持一个健壮的体系,不会因为一点小错误而终止。
此时就需要用到java中的异常处理机制:
try{
//放可能会异常的代码
}catch(Exception e){//exception类对象
//通过exception的对象,使用显示错误信息
}
当这样处理异常后,程序虽然还是会继续报错,但是异常之后的代码可以继续执行

public class test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        try {
            System.out.println(a/b);
        } catch (Exception e) {
            System.out.println(e.getMessage());//通过对象打印错误信息
        }
        System.out.println("程序继续执行");
    }
}

运行结果
/ by zero
程序继续执行

异常基本概念

Java中,将程序执行中发生的不正常情况称之为“异常”,(语法错误和逻辑错误不是异常)

执行过程中所发生的异常事件可分为两大类:

  • Error(错误):Java虚拟机无法解决的严重问题

如JVM系统内部错误,资源耗尽等严重情况。或者StackOverflowError[栈溢出],OOM(out of memory),Error是严重错误,程序会崩溃

  • Exception(异常):其他因编程错误或偶然外在因素导致的一般性问题,可以使用针对性代码处理

比如空指针访问,网络连接中断等等

  1. Exception又分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]

异常体系图

在这里插入图片描述
在这里插入图片描述

小结

  1. 异常分为两大类,运行时异常和编译时异常
  2. 运行时异常,编译器检查不出来,如:逻辑错误,java.lang.RuntimeException类及它的子类都是运行时异常
  3. 运行时异常可以不作处理,因为这类异常很普遍,若全处理可能会对可读性和与运行效率有影响
  4. 编译时异常,是编译器要求必须处置的异常。

五大常见运行时异常

运行时异常,编译通过后,程序在运行时发生的异常

  • NullPointerException空指针异常
  • ArithmeticException数学运算异常
  • ArrayIndexOutOfBoundException数组下标越界异常
  • ClassCastException类型转换异常
  • NumberFormatException数字格式不正确异常

NullPointerException空指针异常

NullPointerException:
当应用程序试图在需要使用对象的地方发现该引用指向null时,抛出该异常

举例:

public class test1 {
    public static void main(String[] args) {
        String s = null;
        System.out.println(s.length());
        CAT Cat = null;
        System.out.println(Cat.a);
    }
}
class CAT{
    int a = 1;
}

以上代码会发现,找不到字符串s的长度。因为它指向null,也无法找到对象Cat的属性a,因为没有分配空间

ArithmeticException数学运算异常

ArithmeticException:
当出现异常的运算条件时,抛出此异常。比如除数为0

ArrayIndexOutOfBoundException数组下标越界异常

ArrayIndexOutOfBoundException:
用非法索引访问数组时抛出的异常,非法索引就是数组中没有的下标。

举例:

public class test1 {
    public static void main(String[] args) {
       int [] arr = {1,2,3};
        for (int i = 0; i <= arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

因为arr数组下标只有 0 ,1,2没有3,而循环中的条件是<=所以允许自增到3.而用3作为下标去访问该数组,就是下标越界

ClassCastException类型转换异常

ClassCastException:
当试图将对象强制转换为不是实例的子类时,抛出该异常

举例:

public class test1 {
    public static void main(String[] args) {
       A b = new B();//向上转型
       B b2 = (B)b;//向下转型
        C c = (C)b;//C和B没有直接关系,所以不能转
    }
}
class A{}
class B extends A{}
class C extends A{}

NumberFormatException数字格式不正确异常

NumberFormatException:
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常

举例:

public class test1 {
    public static void main(String[] args) {
       String some = "1234";
       int name = Integer.parseInt(some);
        System.out.println(name);//可以转的类型
        String move = "好好学习";
        int didi = Integer.parseInt(move);
        System.out.println(move);//不可以转的类型
    }
}

编译时异常

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译

常见的编译异常

  • SQLException//操作数据库时,查询表可能发生异常
  • IOException//操作文件时,发生的异常
  • FileNotFoundException//当操作一个不存在的文件时,发生的异常
  • ClassNotFoundException//加载类,而该类不存在时,异常
  • EOFException//操作文件,到文件末尾,发生的异常
  • IIIeglArguementException//参数异常

异常小练习

  1. String friends[] = {“tom”,“jack”,“milan”};
    for(int i = 0;i<4;i++){
    System.out.println(friends[i]);
    }
    数组下表越界异常ArrayIndexOutOfBoundException,自增到3了,数组只有0,1,2

  2. Cat c = new Cat();
    cat = null;
    System.out.println(cat.name);
    空指针异常NullPointerException

  3. public class AAA{
    int x;
    public static void main(String[]args){
    int y;
    AAA a = new AAA();
    y =3/a.x;
    }
    }
    运算异常ArithmeticException,除数不能为0

  4. class Person{
    public static void main(String[]args){
    Object obj = new Date();
    Person person;
    person = (Person)obj;
    System.out.println(person);
    }
    }
    类型转换异常ClassCastException,Person和Date没有关系

异常处理机制

异常处理就是在异常发生时,对异常进行的处理
通常对于异常有两种处理方式

  1. try-catch-finally:程序员在代码中捕获发生的异常,自行处理
  2. throws:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM

try-catch-finally
语法:
try{
//可能会发生异常的代码
}catch(Exception e){
//1.当异常发生时将异常封装成Exception对象e,传递给catch
//2.得到对象后,程序员在这个代码块中自行处理
//3.如果没有发生异常,catch代码块不执行
}finally{
//不管有没有发生异常,都要执行以下finally代码块里的内容,一般用来放释放内存的代码
}

throws
throws就是将异常抛出,不管这个异常,让调用者去处理。
比如 main方法是JVM调用的,main方法又调用了f1方法,f1方法又调用了f2方法。而f2方法体中又个异常。
此时f2方法有两个选项,第一个使用try-catch-finally自己解决,第二个使用throws关键字抛给f1方法,让它去解决

示意图:
在这里插入图片描述

try-catch处理异常

Java提供try和catch来处理异常,try代码块用于包含可能出错的代码,catch代码块用于处理try代码块中的异常
语法:
try{
可疑代码
将异常生产对象传递给catch
}catch(异常){
对异常的处理
}finally{
有没有异常都会执行
}

finally有没有都行,语法也通过

try catch注意事项:

  1. 如果try代码块中的代码发生了异常,那么异常代码后面的语句都不执行,直接进入catch代码块
  2. 如果异常没有发生,则顺序执行try的代码块,不会进入catch
  3. 如果希望无论是否有异常,都执行某一段代码,可以写在finally代码块里
  4. 当try代码块中有多个异常时,可以使用多个catch代码块传入对于的异常子类,进行分别捕获。但是必须子类在前,父类在后。负责父类会全部捕获掉
  5. 可以使用try-finally配合使用,不管有没有异常都要执行某一段代码,就算有异常也不对异常进行操作

注意点演示:
第一点:

public class test1 {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            System.out.println(a/b);
            System.out.println("异常后的。。。");
        } catch (Exception e) {
            System.out.println(e.getMessage());;
            System.out.println("异常处理中...");
        }
    }
}

运行结果:
/ by zero
异常处理中…
可以看到异常代码后的语句没有输出。一有异常就直接进入catch了

第二点:

public class test1 {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 1;
            System.out.println(a/b);
            System.out.println("异常后的。。。");
        } catch (Exception e) {
            System.out.println(e.getMessage());;
            System.out.println("异常处理中...");
        }
    }
}

输出结果:
10
异常后的。。。
catch代码块中的语句没有输出

第三点:

public class test1 {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 1;
            System.out.println(a/b);
            System.out.println("异常后的。。。");
        } catch (Exception e) {
            System.out.println(e.getMessage());;
            System.out.println("异常处理中...");
        }finally{
            System.out.println("嘀嘀嘀");
        }
    }
}

输出结果:
10
异常后的。。。
嘀嘀嘀

第四点:

public class test1 {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 01;
            System.out.println(a/b);//算术异常
            String name = "好好学习";
            int nu = Integer.parseInt(name);
            System.out.println(nu);//数字格式异常
        } catch(ArithmeticException a){
            System.out.println(a.getMessage());
            System.out.println("算术异常被捕获");
        }catch (NumberFormatException c){
            System.out.println(c.getMessage());
            System.out.println("数字格式异常被捕获");
        }catch (Exception e){
            System.out.println(e.getMessage());
            System.out.println("其他异常被捕获");
        }
    }
}

这里需要注意,当try同时有多个异常时,使用多个catch去捕获,不是同时捕获,而是类似查漏补缺的意思,比如当没有算术异常,就会跳过catch算术异常的代码块。
或者可以这样理解:由于try代码块中一旦发现异常,后面的语句就不执行了,就算后面还有异常也不执行,那么也就无法同时捕获多个异常,所以多个catch的使用其实就是为不同的异常准备不同的处理方案,而不是一上来就把异常全捕获掉。

第五点:

public class test1 {
    public static void main(String[] args) {
        try {
            String name = "12";
            int nu = Integer.parseInt(name);
            System.out.println(nu);//数字格式异常
        }finally {
            System.out.println("管你有没有异常");
        }
    }
}

try catch练习

一:以下代码会输出什么?

public class Exception01 {
    public static int method(){
        try {
            String [] names = new String[3];
            if (names[1].equals("tom")){
                System.out.println(names[1]);
            }else{
                names[3]="yyds";
            }
            return 1;
        }catch (ArrayIndexOutOfBoundsException e){
            return 2;
        }catch (NullPointerException e){
            return 3;
        }finally {
            return 4;
        }
    }

    public static void main(String[] args) {
        System.out.println(method());
    }
}

因为还没对数组进行赋值,就使用equals对比。所以会报空指针异常NullPointerException
按道理来说应该会直接返回 3 然后推出,但是这里有个小细节,finally是必须执行的,它的优先级比return更高,所以并不会在 catch空指针异常返回,而是到finally返回,所以最终输出 4

二:以下代码会输出什么?

public class Exception01 {
    public static int method(){
        int i =1;
        try {
            i++;
            String [] names = new String[3];
            if (names[1].equals("tom")){
                System.out.println(names[1]);
            }else{
                names[3]="yyds";
            }
            return 1;
        }catch (ArrayIndexOutOfBoundsException e){
            return 2;
        }catch (NullPointerException e){
            return ++i;
        }finally {
            return ++i;
        }
    }

    public static void main(String[] args) {
        System.out.println(method());
    }
}

这题跟上提差不多,发生空指针异常然后进入catch空指针,return ++i虽然不会直接返回,但是还是会执行,因此一共自增了3次。到finally返回,最后输出4

三:以下代码会输出什么?

public class Exception01 {
    public static int method(){
        int i =1;
        try {
            i++;
            String [] names = new String[3];
            if (names[1].equals("tom")){
                System.out.println(names[1]);
            }else{
                names[3]="yyds";
            }
            return 1;
        }catch (ArrayIndexOutOfBoundsException e){
            return 2;
        }catch (NullPointerException e){
            return ++i;
        }finally {
             ++i;
            System.out.println("i=" +i);
        }
    }

    public static void main(String[] args) {
        System.out.println(method());
    }
}

依旧发生空指针到catch空指针那,return ++i;此时i = 3,由于下面还有finally,所以不会立刻返回,但是下面的finally又没有返回语句,所以最后还是会在catch空指针这里返回,那么又得去执行下面finally的代码块,所以这里底层会先用一个临时变量保存此时的i,相当于 indxe = i.此时的i是3.然后再去执行finally的内容将i自增到4.接着返回到catch空指针,返回那个临时变量indxe。也就是 3.最终也输出3.
注意点,最终返回的不是i 是一个临时变量

try-catch-finally执行内容注意点

  1. 如果try中没有出现异常,则执行try中所有内容,不执行catch的内容。如果有finally,不管有没有异常都要执行finally
  2. 如果try中出现异常,则try中异常后的内容不执行,直接执行catch中的内容,如果有finally,不管有没有异常都要执行finally

try-catch实践应用

要求客户输入一个整数,如果不是整数,就提示,且让他反复输入,知道输入整数为止(利用异常完成)

import java.util.Scanner;

public class Exception01 {
    public static void method(){
        try {
            System.out.println("请输入一个整数:");
            String num = new Scanner(System.in).next();
            int a = Integer.parseInt(num);
            System.out.println("您输入的整数为:"+a);
        } catch (NumberFormatException e) {
            System.out.println("输入错误,重新输入!");
            method();
        }catch (Exception e){
            System.out.println("未知错误");
        }
    }

    public static void main(String[] args) {
        method();
    }
}

throws抛出异常

  1. 如果一个方法(中的某条语句)可能会生产某种异常,但是不能确定如何处理这种异常,则此方法应显示的声明抛出异常,表明该方法不对这些异常进行处理,而由该方法的调用者处理
  2. 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

语法:
返回类型 方法名(形参列表) throws 异常列表/类型{
方法体
可能有异常的语句
}
入门案例:

public class Exception01 {
    public static void f1(){
        try {//在这解决异常
            f2();
        } catch (Exception e) {
            System.out.println("除数不能为0");
        }
    }
    public static void f2() throws ArithmeticException,Exception{//抛出了异常
        int a = 12;
        int b = 0;
        System.out.println(a/b);//异常发生点
    }

    public static void main(String[] args) {
        
        f1();
    }
}

由上案例可以分析以下几点:
1.throws 后面可以写多个可能的异常类型,使用,分开
2.f2发生的异常抛出给调用者f1,f1在方法体中解决

theows使用细节

  1. 对于编译异常,必须在程序中处理掉,使用try-catch或者throws
  2. 对于运行时异常,程序中如果没有处理,默认就是throws抛出
  3. 子类重写父类的方法时,对抛出异常的规定是:子类重写的方法,所排除的异常类型必须和父类抛出的异常类型一致,或者是父类抛出异常的子类型
  4. 在throws的时候,如果有方法try-catch,就相当于处理异常,可以不用throws,相当二选一。不处理就让别人处理

说明:
第一点
如果抛出的异常是编译异常,那么必须使用try-catch处理掉。
比如f2方法有编译异常,使用throws抛出了,而f1方法调用了f2,那么必须把f1的编译异常使用try-catch处理掉

public class Exception01 {
    public static void f1(){
        try {
            f2();//处理异常点
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static void f2() throws FileNotFoundException {//抛出了异常
        FileInputStream fis = new FileInputStream("d://aa.txt");// 异常发生点
    }

    public static void main(String[] args) {

        f1();
    }
}

可以看到f2的编译异常,被f1处理了。如果不适用try-catch处理,就会报错无法运行

而如果f2抛出的是运行时异常,比如抛出了一个ArithmeticException算术异常,那f1就不强制要求处理了,只是运行时JVM会直接报错退出.这是因为java有默认对于运行时异常的处理机制,第二点会说

第二点:
通过前面的知识点我们知道,throws只是把异常抛给调用者去处理,最高等级的调用者是JVM,它的处理方式就是显示异常然后退出。
所以在调用有运行时异常的方法时,就算这个方法没有显示的有throws,那么它也是默认的有throws的。
就比如main方法就有throws,而JVM会调用main方法。
举例:

public class Exception01 {
    public static void f1(){//throws Exception
       f2();
    }
    public static void f2() {//throws Exception//抛出了异常
        int a =1;
        int b =0;
        System.out.println(a/b);// 异常发生点
    }

    public static void main(String[] args) {//throws Exception
        f1();
        System.out.println("异常后》。。");
    }
}

以上代码虽然没有写throws,但是实际上是f2抛出算术异常,f1调用了f2.就接收了这个异常,然后f1也抛出这个异常,main方法又调用了f1,接收了这个异常,而JVM又调用了main方法,所以最终是由JVM来处理,直接报算术异常然后退出程序

运行可以发现 异常后》。。 这句话没有输出,就是直接发生异常然后退出了

第三点:
子类重写父类的方法抛出的异常只能是父类抛出异常的子类型,比如父类方法抛出的是一个运行时异常:RuntimeException
那么子类重写父类方法后只能抛出RuntimeException的子类,比如算术异常,空指针异常,类型转换异常等等。。

就修饰符差不多:修饰符是子类重写父类的方法,修饰符范围不能变小,只能变大或相等。而子类重写父类抛出的异常是只能变小或相等,不能变大

class A{
    public void Father()throws RuntimeException{}
}
class B extends A{
    @Override
    public void Father() throws ArithmeticException {
        
    }
}

自定义异常

  • 当程序出现了某些错误,但该错误信息 并没有在Throwable子类描述处理,这个时候可以自己设计异常类,用于描述错误信息

步骤:
1.定义类:自定义异常类名,继承Exception或RuntimeExcception
2.如果继承Exception就是编译时异常
3.继承RuntimeExcception就是运行时异常
一般情况都是继承RuntimeExcception运行时异常,这样可以使用java默认的机制。

使用方式:
自定义异常类,基础RuntimeExcception或者Exception
然后构造器传入一个参数,调用父类的构造器
接着在使用处,传入你需要的参数进行判断,如果不符合要求,就throw给一个异常类的对象传入错误信息

案例:
接收一个int类型的数字,要求范围在18~120之间,否则抛出一个自定义异常。

public class Exception01 {
    public static void main(String[] args) {
        int a = 233;
        if (!(a>=1&&a<=120)){
            //如果不符合要求就
            throw new ArgExeception("输入错误,要求在1~120之间");
        }
        System.out.println("输入正确");
    }
}
class ArgExeception extends RuntimeException{
    public ArgExeception(String message) {
        super(message);
    }
}

如果不是运行时异常,是编译异常就还得在方法形参列表后面显示的写上thorws给哪个异常

public class Exception01 {
    public static void main(String[] args) throws AgeExeception {
        int a = 233;
        if (!(a>=1&&a<=120)){
            //如果不符合要求就
            throw new AgeExeception("输入错误,要求在1~120之间");
        }
        System.out.println("输入正确");
    }
}
class AgeExeception extends Exception{
    public AgeExeception(String message) {
        super(message);
    }
}

throw和throws的区别

关键字意义位置后面跟的东西
throws异常处理的一种方式方法声明处异常类型
throw手动生成异常对象的关键字方法体中异常对象

异常练习题

一 编程题

  1. 编写应用程序EcmDef.java,接收命令行的两个参数(整数),计算两数相除
  2. 计算两个数相除,要求使用方法cal(int n1,int n2)
  3. 对数据格式不正确,缺少命令行参数,除0 进行异常处理
public class EcmDef {
    public static void main(String[] args) {
        try {
            if (args.length>2){
                throw new ArrayIndexOutOfBoundsException(" 参数个数不对");
            }
            int n1 = Integer.parseInt(args[0]);
            int n2 = Integer.parseInt(args[1]);
            int n3 = cal(n1,n2);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());;
        } catch (NumberFormatException e) {
            System.out.println("参数格式不正确");;
        }catch (ArithmeticException e){
            System.out.println("除数不能为0");
        }

    }
    public static int cal(int n1,int n2){
        return n1/n2;
    }
}

二 写出运行结果

public class test1 {
    public static void func(){
        try{
            throw new RuntimeException();
        }finally {
            System.out.println("B");
        }
    }
    public static void main(String[] args) {
        try{
            func();
            System.out.println("A");
        }catch (Exception e){
            System.out.println("C");
        }
        System.out.println("D");
    }
}

首先调用func方法,执行try,发现抛了一个运行时异常,所以try后面不执行了,直接执行finally 输出 B
然后回到main方法由于func方法有异常,所以try后面不执行,直接去catch执行输出 C
再然后因为异常已经被捕获了,所以会继续输出main方法后面的 D
结果是 B C D


  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值