【java】异常

目录

一、程序开发中三大常见异常

1、数组越界异常

2、NPE

3、类型转换异常

二、异常体系核心的两个父类

1、Error 

2、Exception

三、异常处理

1、捕获并处理异常。

(1)try catch组合代码块

 (2)cty catch finally组合代码块

2、抛回异常处理

(1)throws的使用方法

(2)throw的使用方法

四、自定义异常

1、自定义异常实现登录


概念:程序执行过程中发生的不正常行为。

观察异常:异常时发生在程序执行过程中,碰到某些代码执行不下去,称为异常。

语法的错误不是异常,编译阶段就报错的,程序并没有执行。

一场产生后会明确告诉异常产生的原因以及出错的代码位置,出现在哪个原文件的第几行代码。出现异常之后的代码就无法正常执行了,之前的还可以正常执行。

出现多个异常的时候只会出现一个异常,异常是有逻辑上的优先级的。

一、程序开发中三大常见异常

1、数组越界异常

索引下标非法

public class ExceptionLearn {
    public static void main(String[] args) {
        int[] num = new int[3];
        System.out.println(num[3]);
    }
}

因为数组只有三个数字并且下标是从零开始的所以查找下标为3的数组会出现数组越界的情况。

2、NPE

空指针异常,通过一个值为null的引用调用成员方法和属性。

public class ExceptionLearn {
    public static void main(String[] args) {
        String str = null;
        str.charAt(1);
    }
}

3、类型转换异常

发生在两个毫无关系的类之间对象的转换(要发生向下转型首先得发生向上转型)。

二、异常体系核心的两个父类

在java中异常也是类,一切皆对象。程序抛出的异常其实抛出的是异常对象,只不过这个对象是有JVM产生并返回的。

1、Error 

属于非受查异常:在编译阶段可以不进行任何的异常处理,但是在程序执行过程中出现的异常。除了非受查异常其他都属于受查异常:在程序编译阶段必须显示异常处理的异常,之后才能运行起来。

JVM无法解决的严重问题,当出现Error问题时,程序无法解决只能退出。

栈溢出(StackOverFlowError)和堆溢出(OutOfMemoryError)属于受查异常。

2、Exception

异常产生后,程序员可以通过异常的处理流程来解决此类问题,程序能继续执行。

三大类常见异常都属于Exception以及其子类。

三、异常处理

1、捕获并处理异常。

try catch finally代码块

可能产生的异常放在try代码块中,若捕捉到了相应类型的异常对象如何处理放在catch代码块中,无论是否有异常产生或者是否处理异常最终一定会执行的代码放在finally代码块中。

(1)try catch组合代码块

    public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            System.out.println(num[0]);
            System.out.println("try中的其他代码块");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("捕获到数组越界异常");
        }
        System.out.println("异常产生之后的代码");
    }

a.当没有异常产生时,不走catch代码块(输出num[0]时)

 b.当异常产生(输出num[4]时),且被正确的捕获到,走相应的catch代码块,try代码块中从出现异常之后的代码不再执行,异常体系之后的代码能够正常执行。

 有异常处理之后,保证程序抛出异常之后,正确捕获该异常,就可以是的异常体系之后的代码可以继续执行。

c.关于catch快捕获异常的说明

异常也是类,在catch代码块中只能捕获一种类型的异常,当try中的出现的异常,在catch块中并没有对应,就会报出异常。

    public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            num = null;
            System.out.println("try中的其他代码块");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("捕获到数组越界异常");
        }
        System.out.println("异常产生之后的代码");
    }

 这种时候就需要多个catch代码块。

    public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            num = null;
            System.out.println(num[0]);
            System.out.println("try中的其他代码块");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("捕获到数组越界异常");
        }catch(NullPointerException e){
            System.out.println("捕获到空指针异常");
        }
        System.out.println("异常产生之后的代码");
    }

 当try中可能会产生多种异常时,可以使用多个catch块来捕获,也可以捕获也可以捕获异常的父类,通过向上转型来进行异常捕获。

public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            num = null;
            System.out.println(num[4]);
            System.out.println("try中的其他代码块");
        }catch (Exception e){
            System.out.println("捕捉到异常");
        }
        System.out.println("异常产生之后的代码");
    }

这种情况下能捕获到所有Exception类的子类的异常。但是这种捕获并不推荐,最好还是捕获具体的子类。

 若捕获到相应的异常之后,输出错误产生的原因以及出错的位置,通过异常对象的printStackTrace方法来进行打印输出。

public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            num = null;
            System.out.println(num[0]);
            System.out.println("try中的其他代码块");
        }catch (Exception e){
            System.out.println("捕捉到异常");
            e.printStackTrace();
        }
        System.out.println("异常产生之后的代码");
    }

若catch代码块中有多个分支,且多个分支有父子类关系的情况,一定是子类异常的捕获要写在父类异常之前。若在定义时,父类异常写在子类异常前面,会报错。

    public static void tryCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            num = null;
            System.out.println(num[0]);
            System.out.println("try中的其他代码块");
        }catch(NullPointerException e){
            System.out.println("捕获到空指针异常");
            e.printStackTrace();
      }catch (Exception e){
            System.out.println("捕捉到异常");
            e.printStackTrace();
        }
        System.out.println("异常产生之后的代码");
    }

程序是出现了空指针异常,当遇到能捕获该异常的catch代码块就直接运行了,不会再去运行其他的catch。 

多个catch块只会走一个,从上向下匹配,发现匹配的catch就进入,其他的不执行。

 (2)cty catch finally组合代码块

a.没有捕获到异常

    public static void finallyCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            System.out.println(num[4]);
            System.out.println("try中的其他代码块");
        }finally{
            System.out.println("finally代码块");
        }
        System.out.println("异常产生之后的代码");
    }

b.使用catch捕捉到异常

    public static void finallyCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            System.out.println(num[4]);
            System.out.println("try中的其他代码块");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("捕获到数组越界异常");
        }finally{
            System.out.println("finally代码块");
        }
        System.out.println("异常产生之后的代码");
    }

 无论是否有异常产生,且是否有返回值,JVM保证finally代码块一定会执行。

当没有异常产生时走完finally代码块程序程序就会结束,体系之外的代码就不会执行,如果有异常会执行体系之外的代码。

以后写的所有的关于资源关闭的操作都放在finally代码块中,确保该资源一定会被关闭。

c.若finally中有return值

无论是否有异常产生,都会返回finally的返回值,因此一般不在finally中写return。

    public static int finallyCode(){
        System.out.println("异常代码之前的代码");
        int[] num = new int[4];
        try {
            System.out.println(num[4]);
            System.out.println("try中的其他代码块");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("捕获到数组越界异常");
            return 1;
        }finally{
            System.out.println("finally代码块");
            return 2;
        }
    }

d.finally不执行的情况。

调用System.exit(0); 时将系统退出,JVM进程直接退出,则不执行finally。

2、抛回异常处理

若出现异常,不处理,将异常抛回给调用者处理。

(1)throws的使用方法

用在方法声明上,明确表示该方法可能产生某些异常,但是该方法不处理,若出现异常,将异常对象抛回给调用者处理。

如果在正给调用过程中,没有一个位置处理异常,最终这个异常对象就会抛回给JVM,然后整个程序退出。

关键点:

throws关键字在方法列表之后进行定义。

throws可以抛出多个异常,多个异常使用逗号分割,若有父子关系,只需抛出父类异常即可。

throws抛出的必须是Exception以及其子类。

若throws抛出的是受查异常,则调用者必须进行显式的异常处理(要么用try-catch捕获,要么继续通过throws向上输出)

a.继续通过throws向上输出

public static void main(String[] args) throws CloneNotSupportedException {
        ThrowTest throwTest = new ThrowTest().clone();
    }
    public ThrowTest clone()throws CloneNotSupportedException{
        return (ThrowTest) super.clone();
    }

b. 用try-catch捕获

    public static void main(String[] args) {
        try{
            ThrowTest throwTest = new ThrowTest().clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
    }

    public ThrowTest clone()throws CloneNotSupportedException{
        return (ThrowTest) super.clone();

(2)throw的使用方法

用在方法内部,程序员在出现异常时,自己产生异常对象并向外抛出(原来异常对象是由JVM产生的,现在程序员自己产生对象)一般搭配自定义异常使用。

抛出异常之后的代码不能被执行。

a.自定义异常并且不被捕获

    public static void main(String[] args) {
        throwTest();
    }
    public static void throwTest(){
        System.out.println("异常代码之前的代码");
        throw new RuntimeException("抛个异常");
    }

 b.自定义异常也可以被捕获

    public static void throwTest(){
        System.out.println("异常代码之前的代码");
        try{
            throw new RuntimeException("抛个异常");
        }catch (RuntimeException e){
            System.out.println("捕获异常");
        }
        System.out.println("异常产生之后的代码");
    }

若throw抛出的是受查异常,则调用者必须进行显式的异常处理(要么用try-catch捕获,要么继续通过throws向上输出)

 无论是那种方式产生的对象,JVM产生的还是自己throw new出来的对象,跟到底怎么产生异常对象无关。

到底是否需要显式进行异常处理,要看这个异常对象是受查异常还是非受查异常。只要是抽查异常,无论那种方式产生的异常对象,都必须显式进行异常处理。

四、自定义异常

JDK内部提前定义好了很多的内置异常,这些异常在具体的业务场景下是不够用的。

在写具体项目时会有很多问题是和场景有关的,例如用户名错误,这些问题JDK无从得知,所以就需要继承异常类,拓展属于自己的异常类。

需要拓展的是非受查异常,继承RuntimeException类

若需要拓展的是受查异常,继承Exception类。

1、自定义异常实现登录

public class MeException {
    static String userName = "洋洋";
    static String password = "123456";
    public static void main(String[] args) throws PasswordException, UserNameException {
        Scanner s = new Scanner(System.in);
        System.out.println("请输出您的用户名:");
        String name = s.nextLine();
        System.out.println("请输出您的密码:");
        String pass = s.nextLine();
        Login(name,pass);
    }
    public static void Login(String userName,String password) throws PasswordException, UserNameException {
        if(!password.equals(MeException.password)){
            throw new UserNameException("用户名错误!");
        }
        if(!userName.equals(MeException.userName)){
            throw new PasswordException("密码错误");
        }
        System.out.println("登陆成功!");
    }
}
class UserNameException extends Exception{
    public UserNameException(String message){
        super(message);
    }
}
class PasswordException extends Exception{
    public PasswordException(String message) {
        super(message);
    }
}

在上述代码中定义了两个类做Exception的子类,借用他们抛出异常,在login方法中抛出用户名错误和密码错误两个异常。

当用户名和密码都输入正确时,会提示登陆成功。

 当密码输入错误时,会抛出PasswordException异常,提示密码错误。

当密码和用户名都输入错误的时候,只会抛出一个异常 UserNameException,并且会提示用户名错误。

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世界级白日梦冠军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值