JAVA系列关于异常的一些事

Posted 波风张三

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA系列关于异常的一些事相关的知识,希望对你有一定的参考价值。


前言

JAVA提供强大的异常处理机制,在Java中,所有的异常都会被封装到一个类中,程序出错时会将异常抛出。


一、如何知道程序错误

代码中的错误是客观存在的,没有十全十美的代码,代码多到一定程序,我们就需要不断维护、检查代码的错误,确保健壮性。

在Java中,我们有以下两种方式告诉程序代码发生错误:

  1. LBYL: Look Before You Leap. 在操作之前就做充分的检查
  2. EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理.

简单来说就是第一种没有使用异常,第二采用了异常的方法

代码风格如下:

LBYL: Look Before You Leap. 在操作之前就做充分的检查:

boolean ret = false;
ret = 登陆游戏();
if (!ret) 
 处理登陆游戏错误;
    return; 
ret = 开始匹配();
if (!ret) 
 处理匹配错误;
    return; 
ret = 游戏确认();
if (!ret) 
 处理游戏确认错误;
    return; ret = 选择英雄();
if (!ret) 
    处理选择英雄错误;
    return; 
ret = 载入游戏画面();
if (!ret) 
 处理载入游戏错误;
    return; 
......

It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理:

try 
    登陆游戏();
    开始匹配();
    游戏确认();
    选择英雄();
    载入游戏画面();
   ...
 catch (登陆游戏异常) 
    处理登陆游戏异常;
 catch (开始匹配异常) 
 处理开始匹配异常;
 catch (游戏确认异常) 
 处理游戏确认异常;
 catch (选择英雄异常) 
 处理选择英雄异常;
 catch (载入游戏画面异常) 
 处理载入游戏画面异常; 
.....

对比两种不同风格的代码, 我们可以发现:

使用第一种方式, 正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱.

而第二种方式正常流程和错误流程是分离开的, 更容易理解代码

二、常见的异常

所谓异常指的就是程序在 运行时出现错误时通知调用者的一种机制

异常的种类有很多, 不同种类的异常具有不同的含义, 也有不同的处理方式,下面介绍几种常见异常:

1.算术异常

public static void main(String[] args) 
        //1.算术异常
        System.out.println(10/0);
    

2.数组越界异常

public static void main(String[] args) 
        int []arr=new int[4];
        System.out.println(arr[10]);
    

3.空指针异常

public static void main(String[] args) 
        String str=null;
        System.out.println(str.length());
    

二、异常的基本用法

1.如何捕获异常

异常处理是由try、catch与finally等3个关键字组成的程序块。
其异常的基本语法:

try
		要检查的程序语句;
		...
		
catch(异常类 对象名称)
		异常发生时的处理语句;
		
[
catch(异常类 对象名称)
		异常发生时的处理语句;
		
catch(异常类 对象名称)
		异常发生时的处理语句;
		
		...
]
[finally
		一定会运行到的程序代码;
		
]	
		

异常的处理流程如下:

  1. 首先,我们把所有可能发生异常的语句都放到一个try之后由所形成的区块,这个区块称为“try区块”。程序通过try区块准备捕捉异常。try程序块若有异常发生,程序的运行便重点,并抛出“异常类所产生的对象”。
  2. 抛出的对象如果属于catch()括号内欲捕获的异常类,catch则会捕捉此异常,然后进入catch的块里继续运行。
  3. 无论try程序块是否捕捉到异常,或者捕捉到的异常是否与catch()括号内的异常相同,最终一定会运行finally块里的程序代码。

【注意】
finally块时可以省略的,如果省略了finally块,那么在catch()块运行结束后,程序将跳到try-catch块之后继续执行。

流程图如下:
下面看一个例子:
不进行异常处理:

public static void main(String[] args) 
        int[] arr = 1, 2, 3;
        System.out.println(arr[10]);
       
    


使用异常处理就能正常编译:

 public static void main(String[] args) 
        int[] arr = 1, 2, 3;
        try
            System.out.println(arr[10]);
        catch (ArrayIndexOutOfBoundsException e)
            System.out.println("我在这里捕捉到一个异常");
        
        System.out.println("hello!!!");

    


如果我想要知道详细的异常信息的话,则需要使用异常对象的printStackTrace()方法:


【异常处理机制小结】
当异常发生时,通常用两种方法处理:

  1. 一种时交由Java默认的异常处理机制做处理。但这种处理方式,Java通常只能输出异常信息,接着便终止程序的运行
  2. 一种时用自行编写的try-catch-finally块来捕捉异常,这样的好处是可以灵活操控程序的流程,做出最适当的处理。

2.异常类的处理流程

习惯上将Error类与Exception类统称为异常类,但二者本质上是不同点。
Error类通常指的是Java虚拟机出错,用户无法在程序里处理这种错误。
Exception类包含了一般性的异常,这些异常在捕捉到之后便可做妥善的处理,以确保程序继续运行。

下面是异常处理的流程:

  1. 如果程序发生异常,那么会自动地由JVM根据异常的类型,实例化一个指定异常类的对象,如果这个时候程序之中没有任何的异常操作,则这个异常类的实例化对象将交给JVM进行处理——进行异常信息的输出,而后中断程序执行
  2. 如果程序之中存在异常处理,则会由try语句捕获产生的异常类对象;然后将该对象与try之后的catch进行匹配,如果匹配成功,则使用指定的catch进行处理,如果没有匹配成功,则向后面的catch继续匹配,如果没有任何的catch匹配成功,则这个时候将交给JVM执行默认处理。
  3. 不管是否有异常都会执行finally程序,如果此时没有异常,执行完finally,则会继续执行程序之中的其他代码,如果此时有异常没有能够处理(没有一个catch可以满足),那么也会执行finally,但执行完finally之后,将默认交给JVM进行异常信息的输出,并且程序中断。

3.throws关键字

我们可以使用 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常。
格式如下:

访问权限 返回值类型 方法名称(参数列表)throws 异常类
	//方法体;

上面格式包括两个部分:一个普通方法的定义,与方法定义模式无区别。方法后面紧跟”throws异常类“,它位于方法体 之前,用来检测当前方法是否有异常,若有,则将该异常提交给直接使用这个方法的方法。

 //定义私有化静态方法,并使用throws抛出异常
    //一旦方法出现异常,setZero()方法自己并不处理,而是将异常提交给它的上级调用这main()方法
    private static void setZero(int[] arr,int index)throws ArrayIndexOutOfBoundsException
        arr[index]=0;
    
    public static void main(String[] args) 
        int []arr=new int[5];
        try
            setZero(arr,10);
        catch (ArrayIndexOutOfBoundsException e)
            System.out.println("数组超出绑定方位!");
            System.out.println("异常:"+e);
        
        System.out.println("main()方法结束!");
    

4.throw关键字

上面的所有异常类对象全部都是由JVM自动实例化的,但优势我们也想能进行异常类对象的实例化操作,手工抛出异常,则此时需要throw关键字。

public static void main(String[] args) 
        try 
            //抛出异常的实例化对象
            throw  new ArrayIndexOutOfBoundsException("\\n我是手工异常:\\n数组下标越界");
        catch (ArrayIndexOutOfBoundsException e)
            System.out.println(e);
        
    

三、自定义异常类

为了处理各种异常,Java可通过继承的方式运行用户编写自己的异常类,因为所有可处理的异常均继承自Exception类,因此自定义异常类也不例外。

语法如下:

class 异常名称 extends Exception
...

我们可以在自定义异常类里编写方法处理相关时间,甚至不编写任何语句也可以正常工作,因为父类Exception已提供相当丰富的方法,通过几次子类均可以使用它们。

static class MyException extends Exception
        public MyException(String msg)
            super(msg);//调用Exception类的构造方法,存入异常信息
        
    
    public static void main(String[] args) 
        try
            throw new MyException("自定义异常");

         catch (MyException e) 
            e.printStackTrace();
            System.out.println(e);
        
    


最后

前路漫漫啊,前路漫漫啊…

以上是关于JAVA系列关于异常的一些事的主要内容,如果未能解决你的问题,请参考以下文章

关于JAVA 异常 基础知识/编码经验的一些总结

转:Java 异常结构体系

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

springcloud报错-------关于 hystrix 的异常 FallbackDefinitionException:fallback method wasn't found(代码片段

关于我用设计模式对公司代码重构的这件事

Nacos源码系列—关于服务注册的那些事