异常(exception)
创始人
2024-04-15 10:42:09
0

异常

什么是异常

  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 Exceptionf2();}public static void f2() {//throws Exception//抛出了异常int a =1;int b =0;System.out.println(a/b);// 异常发生点}public static void main(String[] args) {//throws Exceptionf1();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{@Overridepublic 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手动生成异常对象的关键字方法体中异常对象

相关内容

热门资讯

国家发展改革委:地方要建立消费... 中国青年报客户端北京12月31日电(中青报·中青网记者 贾骥业)2025年大众在享受消费品以旧换新政...
华润微:副总裁兼总法律顾问、首... 每经AI快讯,12月31日,华润微公告,公司董事会于近日收到高级管理人员李舸先生提交的书面辞职报告。...
真心办事 倾心解忧——长春南关... “真是太感谢法院工作人员了,不仅帮我们解决了业主的合理诉求,还让拖欠的物业费得到了妥善处理。”近日,...
山东省第四届“十佳调解案例”揭... 齐鲁晚报·齐鲁壹点记者 栾海明 实习生 李敏 12月30日,由山东省司法厅、齐鲁晚报·齐鲁壹点联合开...
河套合作区深圳园区条例拟于明年... 深圳商报·读创客户端记者 张钧政 12月26日,《深圳经济特区河套深港科技创新合作区深圳园区条例》(...
山东出台《医疗纠纷预防和处理办... 齐鲁晚报·齐鲁壹点 记者 温向前 近日,山东省人民政府正式公布《山东省医疗纠纷预防和处理办法》(以下...
超23亿元索赔案:新能源产业链... 【文/观察者网 潘昱辰 编辑/高莘】日前,吉利极氪旗下电池公司威睿以电芯存在质量问题为由,将电芯供应...