Java异常结构图

Throwable: 是最顶层的父类,有两个重要的子类:Exception(异常)和 Error(错误),异常是指程序本身可以处理的异常,异常又分为编译时异常和运行时异常。

异常(exception)

  • 编译时异常:是指在程序运行之前代码出现的错误导致程序无法正常运行,例如代码下面出现红线提示,需要我们抛出(throws)异常或者try catch异常捕获。

  • 运行时异常(RuntimeException):指我们在开发中测试功能时程序终止,控制台出现的异常,常见的运行时异常有 NullPointerException(空指针异常)、 IndexOutOfBoundsException(下标越界异常), ClassCastException(类转换异常), ArrayStoreException(数据存储异常,操作数组时类型不一致) , IO操作的BufferOverflowException异常

错误(error):error类体系描述了Java运行系统中的内部错误以及资源耗尽的场景,例如堆内存溢出,栈溢出等

关于try-catch-finally

为了更好的理解try-catch-finally,先看下面几段代码

//代码片段一
public static String GetStr(){
       try{
          Console.WriteLine("走到:try");
          return "这里是try返回值";
        }catch (Exception e){
          Console.WriteLine("走到:catch");
          return "这里是catch返回值";
        }finally{
          Console.WriteLine("走到:finally");
        }
        return "这里是方法底部返回值";
}
//输出结果
走到:try
走到:finally
这里是try返回值
​
//代码片段二
public static string GetStr()
        {
            try
            {
                int value = 0;
                Console.WriteLine("走到:try");
                var i = 1 / value;//这里会出错 0不能被整除
                return "这里是try返回值";
            }
            catch (Exception e)
            {
                Console.WriteLine("走到:catch");
                return "这里是catch返回值";
            }
            finally
            {
                Console.WriteLine("走到:finally");
            }
            return "这里是方法底部返回值";
        }
//输出结果
走到:try
走到:catch
走到:finally
这里是catch返回值
    
//代码片段三
public static string GetStr()
        {
            string str = "";
            try
            {
                str = "修改成了a";
                Console.WriteLine("走到:try");
                // return "这里是try返回值";
                return str;
            }
            catch (Exception e)
            {
                Console.WriteLine("走到:catch");
                return "这里是catch返回值";
            }
            finally
            {
                str = "修改成了b";
                Console.WriteLine("走到:finally");
            }
            return "这里是方法底部返回值";
        }
//输出结果
走到:try
走到:finally
修改成了a

总结:

  • 不管try中是否有异常,finally方法块都会被执行,如果try{}中有异常,则异常下面代码不执行

  • 就算try和catch方法都有return,finally都会执行

  • 只要try或者catch 有return返回,try catch 之外的return都无效;

  • 虽然finally方法会被执行但是返回结果不会被改变,也就是如果finally是在return之后执行的那么他会把返回结果先保存起来,然后不管finally代码执行了什么,都不会影响到返回结果,等finally执行完成在返回结果。但是如果finally中有return语句,执行的是finally中的return语句,如果进行值操作将会覆盖try中的值

问题:finally真的就一定会执行吗?

有特殊情况finally块是不会执行的①System.exit(),表示中止当前虚拟机,虚拟机都被中止了,finally代码块自然不会执行,②守护(daemon)线程被中止时,Java线程分为两类,守护线程和非守护线程。当所有的非守护线程中止时,不论存不存在守护线程,虚拟机都会kill掉守护线程从而中止程序。 虚拟机中,执行main方法的线程就是一个非守护线程,垃圾回收则是另一个守护线程,main执行完,程序就中止了,而不管垃圾回收线程是否中止。 所以,如果守护线程中存在finally代码块,那么当所有的非守护线程中止时,守护线程被kill掉,其finally代码块是不会执行的。

注意:try、catch,finally组合写法,不能写多个try,但是可以写多个catch,可以try/catch或者try/finally组合。

public class Test {

    public static void main(String[] args) {
        try{            
            int y=10/0;        
        }
        catch(ArithmeticException ae){        
            System.out.println("Arithmetic Exception");       
        }catch(Exception e){       
            System.out.println("Exception");        
        }   
        System.out.println("finished");        

    }

}
输出结果为:
Arithmetic Exception
finished

如果将上面的catch(ArithmeticException ae) 和 catch(Exception e) 的位置交换,程序会报错,因为Exception e 是 ArithmeticException ae 的父类。当父类在子类前面,抛出的异常已经被父类处理了,后面的子类就不能接收异常。

总结:在异常处理中,若try中的代码可能产生多种异常则可以对应多个catch语句,若catch中的参数类型有父类子类关系,此时应该将父类放在后面,子类放在前面

throws和throw的区别?

throws是在我们处理非运行异常时要么使用throws抛出或者try/catch捕获异常,throw是我们在进行try/catch时将异常抛出,是在方法内部

异常到底什么时候抛出什么时候捕获?

说一下个人理解,当我们在做web开发时,一般都是基于三层架构去开发,即controller(控制层),service(业务逻辑层),dao(持久层),假如我们在控制层去抛出了异常,这其实对用户来说是很不友好的,因为controller和用户页面对接,用户也看不懂我们程序抛出的异常,所以需要在controller层进行全局异常处理,封装一些错误码,友好的提示用户,接着说一下service层,业务层需要处理业务逻辑,当然这一层不需要考虑参数是否为空,需要考虑的仅仅是从dao查询相关的异常,比如ConnectionExcepion、SQLException、BadSQLGrammerException等异常,这些异常我通常会去捕获,因为这些异常涉及到业务逻辑是否能正常执行,做法是try...catch...捕获所有Exception,然后在catch中用logger.error("message, e = {}", e.getMessage())记录日志信息,用于到时候定位error代码位置,然后根据实际业务情况,看这个异常捕获后是否需要用一个异常封装一下信息抛给控制层,然后由控制层统一处理也好或者直接返回给用户)。

spring MVC全局异常处理

一、创建自定义异常类CustomException

public class CustomException extends Exception{//如果继承RuntimeException就是处理运行时异常
    private String message;
    public CustomException(String message){
        this.message=message;
    }
    public String getMessage(){
        return message;
    }
}

二、创建自定义异常处理器CustomExceptionResolver

public class CustomExceptionResolver implements HandlerExceptionResolver{
    ModelAndView mav=new ModelAndView();
    @Override
    public ModelAndView resolveException(HttpServletRequest re,HttpServletResponse rp,Object o,Exception e){
        if(e instanceof CustomException){
            //如果这个异常类型是我们自定义异常类型,加入到自定义异常类,通过getMessage拿到异常信息
            mav.setAndView("error");
            mav.addObject("message",e.getMessage());
        }else{
            mav.setAndView("error");
            mav.addObject("message",系统错误!);
        }
    }
}

三、在springMVC.xml中配置异常处理器,将它注入容器

<bean id="CustomExceptionResolver" class="com.demo.resolver.CustomExceptionResolver"></bean>

四、测试

public class testController {
    @RequestMapping(value="/test")
    public String test(){
        try{
            int i=10/0;
        }catch(Exception e){
            throw new CustomException("整数初0啦...")
        }
        return "target";//跳转页面
    }
}
以上就是我个人对Java异常的总结,如果对你有帮助希望点点赞,谢谢啦........
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐