zoukankan      html  css  js  c++  java
  • 5、异常

    异常概述

    异常(Exception)就是Java程序在运行过程中出现的错误,程序编译通过并不代表着在运行时不会出错,例如下面代码:

    public class ExceptionTest01 {
    
        public static void main(String[] args)    {
    
            int a = 1024;
            int b = 0;
    
            int c = a/b;    
            //下面的这个代码不会执行
            System.out.println("(~ ̄▽ ̄)~");    
        } 
    
    }

    上面程序虽然编译能够通过,但是在运行时将会报出ArithmeticException: / by zero。
    0是不能当做除数的。通过这个异常提示信息可以快速的知道程序的问题,有助于开发者编写出更加健壮的程序,这是异常最主要的作用
    将上面程序修改一下

    public class ExceptionTest01 {
    
        public static void main(String[] args)    {
    
            int a = 1024;
            int b = 0;
            if(b == 0){
                System.out.println("除数不能是0");
            }else{
                int c = a/b;
            }
    
        } 
    
    }

    异常的分类

    异常主要分为:Error、一般性异常、RuntimeException

    • Error:如果程序出现了Error,那么将无法恢复,只能重新启动程序,最典型的Error的异常是:OutOfMemoryError
    • 一般性异常:出现了这种异常必须在程序里面显示的处理,否则程序无法编译通过
    • RuntimeException:此种异常可以不用显示的处理,例如被0除异常,java没有要求我们一定要处理。

    所有异常的祖先类是Throwable,这个类在java.lang包下面。

    JVM是如何处理异常的

    • main方法自己将该问题处理,然后继续运行
    • 自己没有针对的处理方式,只有交给调用main的jvm来处理,jvm有一个默认的异常处理机制。例如上面出现的ArithmeticException,jvm在控制台里面打印出来了异常信息。

    1、throws关键字声明异常

    throws

    throws的作用是声明抛出异常,在方法声明的位置上使用throws关键字向上抛出异常。例如下面程序演示了一般性异常,编译无法通过,需要对异常进行处理

    import java.io.FileInputStream;
    
    public class ExceptionTest{
    
        public static void main(String[] args){
            //这句代码的意思是创建文件输入流,读取文件,遇到不认识的类可以查看API
            FileInputStream fis = new FileInputStream("d:/monkey1024.txt");
    
        }
    }

    可以使用throws将异常抛出

    import java.io.*;
    
    public class ExceptionTest {
    
        public static void main(String[] args) throws FileNotFoundException{
            //这句代码的意思是创建文件输入流,读取文件
            FileInputStream fis = new FileInputStream("d:/monkey1024.txt");
    
        }
    }

    jvm是怎么知道这个地方容易出现问题呢?来看下FileInputStream的源码

    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

    源码里面在构造方法上抛出了FileNotFoundException,所以jvm知道。

    深入throws

    其实使用throws抛出异常并不是真正的去处理异常,而是抛给其调用者去处理,比如你在工作中遇到问题了,交给了你的领导去解决,领导如果也不想解决就交给他的领导去解决。在上面程序里面,我们抛出了异常,最后是交给了jvm解决,jvm的解决方式就是将错误信息打印至控制台,然后关闭程序。
    下面示例展示了将异常抛出的情况

    import java.io.*;
    
    public class ExceptionTest04{
    
        public static void main(String[] args) throws FileNotFoundException{
    
            //抛给调用者,如果都不进行处理的话,最终抛给了main方法
            m1();
        }
    
        public static void m1() throws FileNotFoundException{
            m2();
        }
    
    
        public static void m2() throws FileNotFoundException{
            m3();
        }
    
        //向上抛出异常
        public static void m3() throws FileNotFoundException{
            FileInputStream fis = new FileInputStream("c:/sutaoyu.txt"); 
        }
    
    }

    这里不是说使用throws是不好,使用throws主要意图是暴露问题,如何解决让调用者去决定

    2、使用try-catch捕捉异常

    处理异常

    可以使用try…catch…处理异常,例如之前的程序可以使用try…catch…处理

    package com.monkey1024.exception;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class ExceptionTest02 {
    
        public static void main(String[] args) {
             try {
                FileInputStream fis = new FileInputStream("d:/monkey1024.txt");
                //捕捉FileNotFoundException异常
            } catch (FileNotFoundException e) {//jvm会创建FileNotFoundException的对象,然后将e指向这个对象
                //如果try里面的代码没有报错,则不会执行catch里面的代码
                e.printStackTrace();//打印出异常信息
                String msg = e.getMessage();
                System.out.println(msg);
            }
            System.out.println("monkey1024.com");//catch后面的语句会正常执行
        }
    
    }

    可以捕捉多个异常,但是catch里面必须从小类型异常到大类型异常进行捕捉,先捕捉子后捕捉父,最多执行一个catch语句块里面的内容。

    package com.monkey1024.exception;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class ExceptionTest02 {
    
        public static void main(String[] args) {
             try {
                FileInputStream fis = new FileInputStream("d:/monkey1024.txt");
                fis.read();
            } catch (FileNotFoundException e) {//捕捉FileNotFoundException异常
                e.printStackTrace();
            } catch (IOException e) {//捕捉IOException异常
                e.printStackTrace();
            } catch (Exception e) {//捕捉Exception异常
                e.printStackTrace();
            } 
        }
    
    }

    jdk7新特性

    jdk7新特性,可以将多个捕捉的异常放到一个catch里面

    package com.monkey1024.exception;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class ExceptionTest03 {
    
        public static void main(String[] args) {
    
            try {
                System.out.println(1024 / 0);
                FileInputStream fis = new FileInputStream("d:/monkey1024.txt");
                //jdk7新特性,可以将多个异常放到一个catch里面
            } catch (FileNotFoundException | ArithmeticException e) {
                e.printStackTrace();
            } /*catch (ArithmeticException e){
                e.printStackTrace();
            }*/
        }
    
    }

    3、finally关键字

    finally的特点

    被finally控制的语句体一定会执行,除非在执行finally语句体之前JVM退出(比如System.exit(0)),一般用于关闭资源

    finally如何使用?

    finally语句块可以直接和try语句块联用:try….finally…(这种用的比较少)
    也可以这样使用:try…catch….finally

    package com.sutaoyu.exception;
    
    public class ExceptionText03 {
        public static void main(String[] args){
            try {
                System.out.println(1024/0);
            }catch(ArithmeticException e) {
                e.printStackTrace();
            }finally {
                System.out.println("finally中的内容");
            }
        }
    }

    即使在方法里面执行了return,finally中的代码也会执行

    package com.sutaoyu.exception;
    
    public class ExceptionText03 {
        public static void main(String[] args){
            int i = m1();
            System.out.println(i); 
        }
        
        public static int m1(){
    
            int i = 10;
            try{
                return i;
            }finally{
                System.out.println("finally中的语句");
            }
    
        }
    }

    只有当finally语句执行之前,JVM退出了,finally才不会执行:

    package com.monkey1024.exception;
    
    public class FinallyTest02 {
    public static void main(String[] args){
    
            int i = m1();
            System.out.println(i); 
    
        }
    
        public static int m1(){
    
           try {
               int i = 1024;//try里面的变量在外部是无法被访问的
               System.exit(0);//让jvm退出,所以finally中的语句不会执行
               return i;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("finally中的语句");
            }
           return 10;
           // System.out.println(i);无法访问i,因为i是在try中声明的
    
        }
    
    }

    小练习:

    1.下面程序的打印结果是什么?

    package com.monkey1024.exception;
    
    public class FinallyTest03 {
    public static void main(String[] args){
    
            int i = m1();
            System.out.println(i); 
    
        }
    
        public static int m1(){
    
            int i = 10;
            try{
                return i;
             //这里没有catch,所以不用在后面写return了
            }finally{
                i++;
                System.out.println("finally中的i=" + i); 
            }
    
        }
    
    }
    
    答案:
    程序在执行到return i时,会先将i赋给一个临时变量,return的是这个临时变量:int temp = i;return temp;
    ,然后再执行finally中的语句,打印出了11,之后再执行main方法中的语句,打印出10。

    2.final,finally和finalize的区别?

    答案:

    这三个关键字其实没有什么关系,只不过在单词的拼写上面相似。

    • final、finally是try语句中的一个语句体,不能单独使用,语句体中的语句一定会执行
      • final可以修饰类,不能被继承
      • 修饰方法,不能被重写
      • 修饰变量,只能赋值一次
    • finalize是Object中的一个方法,当没有引用指向这个对象时,由对象的垃圾回收器在回收之前调用此方法。

    4、自定义异常

    自定义异常概述

    当java里面的异常无法满足开发者的需求时,可以自定义异常。

    package com.monkey1024.exception;
    
    public class UserService {
            //注册的方法
            public void register(String name){
    
                if(name.length()<6){
    
                    //需要在这里面一个抛出非法注册名的异常,不过java里面没有这个
                }
    
                //如果代码能执行到此处,证明用户名是合法的.
                System.out.println("注册成功!");
    
            }
    }

    上面是一个用户注册的代码,如果注册的用户名长度小于6,则需要抛出一个非法注册名的异常,不过java里面没有这个异常,这时,开发者可以自定义这个异常来满足需求

    如何自定义异常?

    可以看下其他Exception里面的源码进行参考。
    如果自定义异常是RuntimeException类型的,那就直接继承RuntimeException即可
    否则就继承Exception。
    继承之后一般提供两个构造方法,如下自定义名为IllegalNameException的异常

    package com.monkey1024.exception;
    
    public class IllegalNameException extends Exception{ //编译时异常
    //public class IllegalNameException extends RuntimeException{ //运行时异常
    
            //定义异常一般提供两个构造方法
            public IllegalNameException(){}
    
    
            public IllegalNameException(String msg){
                super(msg);
            }
    
        }

    使用自定义异常

    自定义好异常之后就可以使用了,将上面的代码修改一下:

    package com.monkey1024.exception;
    
    public class UserService {
            //注册的方法
            public void register(String name) throws IllegalNameException{
    
                if(name.length()<6){
    
                    //需要在这里面一个抛出非法注册名的异常,不过java里面没有类似的
                    //手动抛出异常
                    //注意是throw不是throws
                    //使用throw在方法体内抛出异常
                    throw new IllegalNameException();
                }
    
                //如果代码能执行到此处,证明用户名是合法的.
                System.out.println("注册成功!");
    
            }
    }

    关于throw,在方法内部出现某种情况,程序不能继续运行,就用throw把异常对象抛出。

    来写一个测试类

    public class RegisterTest{
    
        public static void main(String[] args){
    
            //假如用户提供的用户名如下
            String username = "mk";
    
            //注册
            CustomerService cs = new CustomerService();
    
            try{
                cs.register(username);
            }catch(IllegalNameException e){
                System.out.println(e.getMessage());
            }
    
    
        }
    }

    throw和throws的区别

    • throws
      • 用在方法声明后面,跟的是异常类名
      • 可以跟多个异常类名,用逗号隔开
      • 表示抛出异常,由该方法的调用者来处理
    • throw
      • 用在方法体内,跟的是异常对象名
      • 只能抛出一个异常对象名
      • 表示抛出异常,由方法体内的语句处理
  • 相关阅读:
    Codeforces 1294E Obtain a Permutation
    Codeforces 1292C Xenon's Attack on the Gangs
    Codeforces 1292B Aroma's Search
    Codeforces 1288E Messenger Simulator
    Codeforces 1288D Minimax Problem
    Codeforces 1285E Delete a Segment
    P3368 【模板】树状数组 2 题解
    P3374 【模板】树状数组 1 题解
    P1494 [国家集训队]小Z的袜子 题解
    P2260 [清华集训2012]模积和 题解
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/10027413.html
Copyright © 2011-2022 走看看