Java异常

异常是如何产生:

首先在讲异常之前,我们先来看一张图:

Throwable 是所有可以作为异常被抛出的类的超类。Throwable 类包括两个重要的子类:Error 和 Exception。 

Error表示严重得错误,jvm会直接停止程序得运行

Exception表示还可以解决得错误,jvm首先会向上抛出。

Exception得分类:

  • Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。),这类异常同时也叫受检异常
  • RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。),这类异常也叫做非受检异常

举例说明:

编译时异常其实很明显,一般编译器都会提示

下面举一个运行时异常得例子:

package a_Exception;

public class Demo1Ex {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        method(arr);
    }
    public static void method(int[] arr){
        System.out.println(arr[4]);
        System.out.println("我想要执行");
    }

}

这一段代码很明显发生了一个数组越界

然后我们对报错得异常进行追踪一下

很清楚,发现这就是一个运行时异常。

同时,我们可以研究一下这个异常的处理过程

从这张图中可以看到,当jvm检测到异常之后就会直接向上抛,最后如果没有人处理的话,虚拟机就会用自己的方式处理异常

从上面的代码的控制台也可以看出,我在数组越界之后还输出了一句话:我想要执行

可是控制台并没有输出 

异常的解决方法:

1:throws向上抛:

格式:在方法参数和方法体之间位置上写
 throws 异常

throws关键字主打一个谁调用我这个方法我就把这个异常抛给谁

下面上一个案例:

首先我们创建一个异常对象,但不对这个异常对象做任何处理

接着我在add方法上抛了异常,然后这个异常就抛到了add方法上

最后只能在main方法上把异常抛了才算解决

throws 抛出多个异常:

1.格式:throws 异常1,异常2
    
2.注意:
  如果throws的多个异常之间有子父类继承关系,我们可以直接throws父类异常
  如果不知道多个异常之间是否有子父类继承关系,我们可以直接throws Exception  

其实总体总结下来,throws这种方式和jvm默认的处理方式一样,

像上面的代码:如果add功能受到影响,下面的方法都用不了,这很不符合我们开发

所以对开发来说意义不大

 2:try…catch:

格式:


  try{
      可能出现异常的代码
  }catch(异常 对象名){
      处理异常的代码-> 将来开发会将异常信息保存到日志文件中
  }

基本使用: 

public static void main(String[] args)throws FileNotFoundException {
        String s = "a.txt1";
        try {
            add(s);//添加功能
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        delete();//删除功能
        update();//修改功能
        find();//查询功能
    }
删除功能
修改功能
查询功能
java.io.FileNotFoundException: 文件找不到
	at a_Exception.Demo1Ex.add(Demo1Ex.java:22)
	at a_Exception.Demo1Ex.main(Demo1Ex.java:9)

下面是控制台输出内容,上面是用try-catch来包围的代码

我们可以看出try-catch这种方式处理的异常明显比较优雅,

即使你add这个方法报错,下面的内容也依旧会执行

当我们执行上面这个代码的时候,我们发现打印数组为空是java里面经典的NPE,空指针异常

FileNotFoundException这个异常抓不到这个空指针异常,所以就会让jvm处理这个异常。

所有推荐这种情况直接catch(Exception e)抓个大的异常

Finally关键字:

概述:代表的是不管是否触发了异常,都会执行的代码块

基本使用:都是配合try...catch使用
  try{
      可能出现异常的代码
  }catch(异常 对象名){
      处理异常的代码-> 将来开发会将异常信息保存到日志文件中
  }finally{
      不管是否有异常,都会执行的代码
  } 

直接上个案例:
 
public static void main(String[] args)throws FileNotFoundException {
        String s = "a.txt1";
        try {
            add(s);//添加功能
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            System.out.println("我必须滴执行");
        }
        delete();//删除功能
        update();//修改功能
        find();//查询功能
    }


private static void add(String s)throws FileNotFoundException {
        if (!s.endsWith(".txt")) {
            //故意创建异常
            throw new FileNotFoundException("文件找不到");
        }
        System.out.println("我要执行了");
    }
java.io.FileNotFoundException: 文件找不到
	at a_Exception.Demo1Ex.add(Demo1Ex.java:24)
	at a_Exception.Demo1Ex.main(Demo1Ex.java:9)
我必须滴执行
删除功能
修改功能
查询功能

整体的代码执行逻辑就是:调用add方法,进入if判断,然后抛出异常

进入catch语句,打印异常的信息,最后执行finally

来看下面一个案例:
public static void main(String[] args)throws FileNotFoundException {
        int result = method();
        System.out.println(result);
    }

    public static int method() {
        try {
            String s = null;
            System.out.println(s.length());//空指针异常
            return 2;
        } catch (Exception e) {
            return 1;
        } finally {
            System.out.println("我一定要执行");
//            return 3;
        }
    }

代码结果:

我一定要执行
1

 当程序走到s.length()时,发现异常进入catch,return 1

不过此时jvm发现下面还有一段finally,jvm还得执行finally之后再return(finally真是硬啊,不停虚拟机你就楞是不停啊)

如果finally里面的return 3没有被注释,那返回的结果result就是3

finally的使用场景:

关闭资源:
 

对象如果没有用了,GC(垃圾回收器)回收,用来回收堆内存中的垃圾,释放内存,但是有一些对象GC回收不了,比如:连接对象(Connection),IO流对象,Socket对象,这些对象GC回收不了,就需要我们自己手动回收,手动关闭

将来不能回收的对象new完之后,后续操作不管是否操作成功,是否有异常,我们都需要手动关闭,此时我们就可以将关闭资源的代码放到finally中

看下面这段代码:

public class Test {
public static void main(String[] args) {
  FileWriter fw = null;
  try {
      fw = new FileWriter("day13_exception_object\\1.txt");
      fw.write("哈哈哈");//假如这里写失败或者写成功了
  } catch (IOException e) {
      throw new RuntimeException(e);
  }finally {
      if (fw!=null){
          try {
              fw.close();
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }

  }
}
}

 抛异常的注意事项:
 

1.如果父类中的方法抛了异常,那么子类重写之后要不要抛?
  可抛可不抛  
2.如果父类中的方法没有抛异常,那么子类重写之后要不要抛?  
  不要抛  

3.如果你实现了父类的没有抛异常的方法,接着你在这个方法体中使用了一个父类抛了异常的方法,那这个时候,你处理异常的方式只能try-catch

第三点比较难理解,举个例子:

 在多线程章节,创建了一个实体类叫:MyThread,并且继承了Thread

Thread中的run方法是没有抛异常的,不过sleep方法是抛了异常的

然后我在MyThread类中重写Thread的run方法,并且在其中调用了sleep方法

代码如下:

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(MyThread.currentThread().getName()+"方法:"+i);
        }
    }
}

这个时候,当我输入 Thread.sleep(1000L)

编译器就会提示我出现了编译异常

这个时候我们只能用try-catch的方式处理异常

原因是:

重写方法是不能修改方法签名(方法签名就是:方法名称、参数列表和返回类型,但不包括方法体)

在Thread中,run()方法并没有声明任何异常抛出,因此在实现该接口时,您不能修改方法签名,包括添加throws子句来向上抛出异常。

自定义异常类:

 我们在写业务的时候,肯定会碰到很多java没有提供的异常,

比如密码错误异常,账号错误异常,或者各种业务异常之类的,这个时候就需要自己去创建异常了

首先;我们可以模拟需要处理异常的场景:
public class Test01 {
    public static void main(String[] args) {
        String test = "root";
        Scanner scanner = new Scanner(System.in);
        String next = scanner.next();
        if(next.equals(test)){
            System.out.println("success");
        }else {
            throw new LoginFailedException("登录错误");
        }

    }
}

这个时候其实是会爆红的

因为我们知道在java中,万物皆是对象,所以我们自己造的这个异常也是一个对象

所以:我们需要加入下面这段代码:

public class LoginFailedException extends RuntimeException{
    LoginFailedException(String msg){
        super(msg);
    }
}
LoginFailedException继承了运行时异常

当然也可以去继承编译时异常,不过如果继承了编译异常,需要在主函数中抛出或者try-catch

继承了运行时异常,我们创建一个构造方法:super(msg)

运行结果:

这一段可以和我之前写过的文章全局异常处理器结合着看。

全局异常处理器-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值