精华内容
下载资源
问答
  • 2022-01-27 11:21:36

    一、异常概述与异常体系结构

    异常概述

            在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
        
            程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常(开发过程中的语法错误和逻辑错误不是异常)。

            Java程序在执行过程中所发生的异常事件可分为两类:
            Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(内存溢出)。一般不编写针对性的代码进行处理。
            Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
                空指针访问
                试图读取不存在的文件
                网络连接中断
                数组角标越界

    Error示例代码:

    public class ErrorTest {
        public static void main(String[] args) {
            /*
             * 1、栈溢出:java.lang.StackOverflowError
             *   原因 : 函数调用栈太深了,注意代码中是否有了循环调用方法而无法退出的情况
             *   StackOverflowError 是一个java中常出现的错误:在jvm运行时的数据区域中有一个java虚拟机栈,当执行java方法时会进行压栈弹栈的操作。在栈中会保存局部变量,操作数栈,方法出口等等。jvm规定了栈的最大深度,当执行时栈的深度大于了规定的深度,就会抛出StackOverflowError错误。
             * */
            main(args);
    
            /*
             * 2、堆溢出:java.lang.OutOfMemoryError
             *   原因:Java中所有的对象都存储在堆中,通常如果JVM无法再分配新的内存,内存耗尽,垃圾回收无法及时回收内存,就会抛出OutOfMemoryError。
             * */
            Integer[] arr = new Integer[1024 * 1024 * 1024];
    
        }
    }

    Exception示例代码:

    import java.io.FileInputStream;
    
    public class ExceptionTest {
        public static void main(String[] args) {
            /*
             * 1、运行时异常:java.lang.ArithmeticException
             *  原因:ArithmeticException
             * */
            int a = 10;
            int b = 0;
            System.out.println(a / b);
    
            /*
             * 2、编译期异常:java.io.FileNotFoundException
             *   原因:文件找不到异常通常是两种情况:1、系统找不到指定的路径 2、拒绝访问(指定的是目录时,就会报拒绝访问异常)
             * */
            FileInputStream fis = new FileInputStream("a.txt");
    
        }
    }

            异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?

            Java提供了更加优秀的解决办法:异常处理机制。异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。

            Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。
     
            Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。JDK中内建了一些常用的异常类,我们也可以自定义异常。

    异常体系结构

            Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。

            Throwable又派生出Error类和Exception类。

            错误:Error类以及它的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。

            异常:Exception以及它的子类,代表程序运行时发生的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

    Error和Exception的区别:

            Error和Exception都有一个共同的根类是Throwable类。
        
            Error是系统中的错误,程序员是不能改变的和处理的,一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。因此我们编写程序时不需要关心这类错误。
        
            Exception,也就是我们经常见到的一些异常情况,表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

    异常体系结构图:

    异常分类

            总体上我们根据Javac对异常的处理要求,将异常类分为2类。
        
            我们常说的异常是狭义上的:就是指Exception及其子类,但是广义上的异常是包括Exception和Error;

            Java的异常(包括Exception和Error)从广义上分为检查异常(checked exceptions)和非检查的异常(unchecked exceptions)。

            其中根据Exception异常进行划分,可分为运行时异常和非运行时异常。

            需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。

    检查异常

    What:什么是检查异常(checked exception)?

            就是编译器要求你必须处置的异常。不知道你编程的时候有没有遇到过,你写的某段代码,编译器要求你必须要对这段代码try...catch,或者throws exception,如果你遇见过,没错,这就是检查异常,也就是说,你代码还没运行呢,编译器就会检查你的代码,会不会出现异常,要求你对可能出现的异常必须做出相应的处理。

            javac强制要求程序员为这样的异常做预备处理工作(使用try...catch...finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

            比如:我们调用日期格式化类解析字符串的时候;

    How:怎样处理检查异常(checked exception)?

            1、继续抛出,消极的方法,一直可以抛到java虚拟机来处理,就是通过throws Exception抛出。

            2、用try...catch捕获

            注意,对于检查的异常必须处理,或者必须捕获或者必须抛出

    Where:检查异常有哪些呢?

            除了RuntimeException与其子类,以及错误(Error),其他的都是检查异常(绝对的大家族)。

    非检查异常

    What:什么是非检查异常(unchecked exceptions)?

            编译器不要求强制处置的异常,虽然你有可能出现错误,但是编译器不会在编译的时候检查,没必要,也不可能。

            javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try...catch...finally)这样的异常,也可以不处理。

            对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

    How:对非检查的异常(unchecked exception )怎样处理?

            1、用try...catch捕获
            2、继续抛出
            3、不处理
            4、通过代码处理

            一般我们是通过代码处理的,因为你很难判断会出什么问题,而且有些异常你也无法运行时处理,比如空指针,需要人手动的去查找。

            而且,捕捉异常并处理的代价远远大于直接抛出。

    Why:为什么有非检查异常?

            你想想非检查异常都有哪些?NullPointerException,IndexOutOfBoundsException,VirtualMachineError等,这些异常你编译的时候检查吗?再说了,明明可以运行时检查,都在编译的时候检查,你写的代码还能看吗?而且有些异常只能在运行时才能检查出来,比如空指针,堆溢出等。

    Where:非检查异常有哪些?

            RuntimeException与其子类,以及错误(Error)。

    Exception异常划分

            Exception异常进行划分,它可分为运行时异常和编译期异常。

            运行时异常:
                    是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是非检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

                    运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

            编译期异常:
                    是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不要自定义检查异常。

    二、初识异常与常见异常

    初识异常

            下面的代码会演示2个异常类型:ArithmeticException 和 InputMismatchException。

            前者由于整数除0引发,后者是输入的数据不能被转换为int类型引发。

    import java.util.Scanner;
    
    public class AllDemo {
        public static void main(String[] args) {
            System.out.println("----欢迎使用命令行除法计算器----");
            CMDCalculate();
        }
    
        public static void CMDCalculate() {
            Scanner scan = new Scanner(System.in);
            int num1 = scan.nextInt();
            int num2 = scan.nextInt();
            int result = devide(num1, num2);
            System.out.println("result:" + result);
            scan.close();
        }
    
        public static int devide(int num1, int num2) {
            return num1 / num2;
        }
    }
    
    /*****************************************
    ----欢迎使用命令行除法计算器----
    2
    0
    Exception in thread "main" java.lang.ArithmeticException: / by zero
    	at AllDemo.devide(AllDemo.java:19)
    	at AllDemo.CMDCalculate(AllDemo.java:13)
    	at AllDemo.main(AllDemo.java:6)
    
    ----欢迎使用命令行除法计算器----
    ----欢迎使用命令行除法计算器----
    1
    r
    Exception in thread "main" java.util.InputMismatchException
    	at java.util.Scanner.throwFor(Scanner.java:864)
    	at java.util.Scanner.next(Scanner.java:1485)
    	at java.util.Scanner.nextInt(Scanner.java:2117)
    	at java.util.Scanner.nextInt(Scanner.java:2076)
    	at AllDemo.CMDCalculate(AllDemo.java:12)
    	at AllDemo.main(AllDemo.java:6)
    *****************************************/

            异常是在执行某个函数时引发的,而函数又是层级调用,形成调用栈的,因为,只要一个函数发生了异常,那么他的所有的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就形成的了异常追踪栈。

            异常最先发生的地方,叫做异常抛出点。

            从上面的例子可以看出,当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,因此调用它的CMDCalculate函数也无法正常完成,因此也发送异常,而CMDCalculate的caller——main 因为CMDCalculate抛出异常,也发生了异常,这样一直向调用栈的栈底回溯。这种行为叫做异常的冒泡,异常的冒泡是为了在当前发生异常的函数或者这个函数的caller中找到最近的异常处理程序。由于这个例子中没有使用任何异常处理机制,因此异常最终由main函数抛给JRE,导致程序终止。

            上面的代码不使用异常处理机制,也可以顺利编译,因为2个异常都是非检查异常。但是下面的例子就必须使用异常处理机制,因为异常是检查异常。

            代码中我选择使用throws声明异常,让函数的调用者去处理可能发生的异常。但是为什么只throws了IOException呢?因为FileNotFoundException是IOException的子类,在处理范围内。

    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class ExceptionTest {
        public void testException() throws IOException {
            //FileInputStream的构造函数会抛出FileNotFoundException
            FileInputStream fileIn = new FileInputStream("E:\\a.txt");
    
            int word;
            //read方法会抛出IOException
            while ((word = fileIn.read()) != -1) {
                System.out.print((char) word);
            }
            //close方法会抛出IOException
            fileIn.close();
        }
    }

    常见异常

            java.lang.RuntimeException: 运行时异常
                    ClassCastException: 类类型转换异常,当试图将对象强制转换为不是实例的子类时,抛出该异常;
                    ArrayIndexOutOfBoundsException: 数组下标越界异常,当你使用不合法的索引访问数组时会抛出该异常;
                    NullPointerException: 空指针异常,通过null进行方法和属性调用会抛出该异常;
                    ArithmeticException: 算术运算异常,除数为0,抛出该异常;
                    NumberFormatException: 数字转换异常,当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常;
                    InputMismatchException: 输入不匹配异常,输入的值数据类型与设置的值数据类型不能匹配。
            ...

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.Date;
    import java.util.Scanner;
    
    public class ExceptionTest {
        //ClassCastException
        public void test1() {
            Object obj = new Date();
            String str = (String) obj;
        }
    
        //IndexOutOfBoundsException
        public void test2() {
            //ArrayIndexOutOfBoundsException
            int[] arr = new int[10];
            System.out.println(arr[10]);
    
            //StringIndexOutOfBoundsException
            String str = "abc";
            System.out.println(str.charAt(3));
        }
    
        //NullPointerException
        public void test3() {
            int[] arr = null;
            System.out.println(arr[3]);
    
            String str = "abc";
            str = null;
            System.out.println(str.charAt(0));
        }
    
        //ArithmeticException
        public void test4() {
            int a = 10;
            int b = 0;
            System.out.println(a / b);
        }
    
        //NumberFormatException
        public void test5() {
            String str = "123";
            str = "abc";
            int num = Integer.parseInt(str);
        }
    
        //InputMismatchException
        public void test6() {
            Scanner scanner = new Scanner(System.in);
            int score = scanner.nextInt();
            System.out.println(score);
            scanner.close();
        }
    }

            java.io.IOExeption: 输入输出异常
                    FileNotFoundException: 文件找不到异常,通常是两种情况:1、系统找不到指定的路径 2、拒绝访问(指定的是目录时,就会报拒绝访问异常)
                    EOFException: 文件已结束异常,抛出EOFException一定是因为连接断了还在继续read;

            java.lang.ClassNotFoundException: 类找不到异常,当我们通过配置文件去查找一个类的时候,如果配置路径写错,就会抛出该异常,比如:web.xml文件中根本就不存在该类的配置或者配置的路径写错;(比较常见)
            
            java.sql.SQLException: SQL异常,数据库的各种信息的异常;

    import java.io.File;
    import java.io.FileInputStream;
    
    public class ExceptionTest {
        
        public void test7() {
            File file = new File("hello.txt");
            FileInputStream fis = new FileInputStream(file);
    
            int data = fis.read();
            while (data != -1) {
                System.out.print((char) data);
                data = fis.read();
            }
    
            fis.close();
        }
    
    }

    三、异常处理的处理机制

            在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。    

            在编写代码处理异常时,对于检查异常/非检查异常,都有2种不同的处理方式:
                    1、使用try...catch...finally语句块处理它。
                    2、在函数签名中使用throws 声明交给函数调用者caller去解决。
        
            比如现有一辆车,这个车你可以是方法,这辆车在可能存在各种风险,那么对于这些风险的处理方式,就相当于异常的处理方式:
            1、使用try...catch...finally语句块处理它。
                    我们把这辆车可能出现的问题都考虑清楚了,并提供了备选方案(出现问题怎么做),如果没有出现问题,那么用不到备选方案;
                    如果出现了问题,根据问题去找对应的备选方案,以保证车的正常运行;
                    如果出现了问题,但是又没备选方案,那么车就跑不了;
            2、在函数签名中使用throws 声明交给函数调用者caller去解决。
                    我知道车可能又问题,但是我不处理,谁来使用了,告诉调用者,这里可能有问题;
                    那么调用者可以处理这个问题,也可以不处理;如果它不处理,还是会出现问题,如果处理了,肯定没问题;
                    A -》 B -》 C

            Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

            Java提供的是异常处理的抓抛模型。
        
            Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。

            异常对象的生成:
                    由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出。
                    由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样。

            异常的抛出机制:
                    如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。
                    如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。
                    程序员通常只能处理Exception,而对Error无能为力。

    异常处理机制一:try-catch-finally

    	语法格式:
            try{
                ...... //可能产生异常的代码
            }
            catch( ExceptionName1 e ){
                ...... //当产生ExceptionName1型异常时的处置措施
            }
            catch( ExceptionName2 e ){
                ...... //当产生ExceptionName2型异常时的处置措施
            }
            [ finally{
                ...... //无论是否发生异常,都无条件执行的语句
            } ]
    
    	语法解释:
    		try:
    			捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
    			如果发生异常,则尝试去匹配catch块,catch块可以有多个(因为try块可以出现多个不同类型异常);
    			如果执行完try不管有没有发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。			
    		catch (Exceptiontype e):
    			在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
    			每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java可以将多个异常声明在一个catch中。 catch(Exception1 | Exception2 | Exception3 e)
    			catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
    			在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
    				①、getMessage() 获取异常信息,返回字符串。
    				②、printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
    			如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
    			如果try中没有发生异常,则所有的catch块将被忽略。
    			注意:如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。比如:可以用 ArithmeticException 类作为参数的地方,就可以用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)。
    
    		finally:
    			finally块通常是可选的。捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
    			不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
    			一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
    			finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
    public class Test_Input_01 {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            /*
    		 * 当我们只写一个try{}的时候,会报错:
    		 * 	Syntax error, insert "Finally" to complete TryStatement
    		 * 	语法错误,插入“Finally”来完成Try块
    		 * 
    		 * 总结: try不能单独存在,必须结合catch或finally来使用
    		 * 	try块中没有异常,会执行finally(如果有)
    		 *  try块中有异常,会执行对应的catch块(如果有),try中异常发生点之后的代码将不会执行
    		 */
            try{
                //try块中放可能发生异常的代码。
                //如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
                //如果发生异常,则尝试去匹配catch块。异常点之后的代码不会运行
    
                //这行代码有可能出错
                int num1 = sc.nextInt(); //这行代码如果出现异常,那么后面的输出语句就不执行
    
                int num2 = sc.nextInt();
    
                System.out.println(num1+"\t"+num2);
    
                System.out.println(num1/num2);
    
    
                String str = null;
                System.out.println(str.charAt(0));
    
            }catch(InputMismatchException | NullPointerException e) { 
                // catch(异常对象)	当异常发生了,catch就会捕获对应的异常
                // 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。 catch(Exception1 | Exception2 | Exception3 e)
                // catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
                // 在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。
                // 如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
                // 如果try中没有发生异常,则所有的catch块将被忽略。
                System.out.println("catch块 - try里面发生了异常 - 空指针和输入不匹配异常都走这个Catch");
            }catch(java.lang.ArithmeticException e){
                e.getMessage();
                System.out.println("算数异常:除数不能为0");
    
            }catch(Exception e){
                System.out.println("程序发生未知异常");
            }finally {
                // finally块通常是可选的。
                // 无论异常是否发生,异常是否匹配被处理,finally都会执行。
                // 一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
                // finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
                System.out.println("finally块");
            }
    
            System.out.println("异常捕获之后的代码");
        }
    }	

    需要注意的地方

            1、try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。

            2、每一个catch块用于处理一个异常。异常匹配是按照catch块的顺序从上往下寻找的,只有第一个匹配的catch会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。

            3、java中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理catch代码块去执行,异常被处理完后,执行流会接着在“处理了这个异常的catch代码块”后面接着执行。

            有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫做:resumption model of exception handling(恢复式异常处理模式)

            而Java则是让执行流恢复到处理了异常的catch块后接着执行,这种策略叫做:termination model of exception handling(终结式异常处理模式)

    public static void main(String[] args){
        try {
            foo();
        }catch(ArithmeticException ae) {
            System.out.println("处理异常");
        }
    }
    public static void foo(){
        int a = 5/0;  //异常抛出点
        System.out.println("Look me!!!");  //不会执行
    }

            finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。

            良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。

            需要注意的地方:

            1、finally块没有处理异常的能力。处理异常的只能是catch块。

            2、在同一try...catch...finally块中 ,如果try中抛出异常,且有匹配的catch块,则先执行catch块,再执行finally块。如果没有catch块匹配,则先执行finally,然后去外面的调用者中寻找合适的catch块。

            3、在同一try...catch...finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,然后去外围调用者中寻找合适的catch块。

            这是正常的情况,但是也有特例。关于finally有很多恶心,偏、怪、难的问题.

            具体在最后一节:finally块和return 讲解

    不捕获异常时的情况:

            前面使用的异常都是RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过( 但运行时会发生异常使得程序运行终止 )。

            如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为运行时异常。

    import java.io.*;
    
    public class IOExp {
        public static void main(String[] args) {
            try {
                FileInputStream in = new FileInputStream("hello.txt");
                int b;
                b = in.read();
                while (b != -1) {
                    System.out.print((char) b);
                    b = in.read();
                }
                in.close();
            } catch (IOException e) {
                System.out.println(e);
            } finally {
                System.out.println(" It’s ok!");
            }
        }
    }

    异常处理机制二:throws

            throws是另一种处理异常的方式,它不同于try...catch...finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。

            throws声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉或并不能确定如何处理这种异常,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理,否则编译不通过。

            在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

            采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。

            语法格式:
            修饰符 返回值类型 方法名() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN{ 
                //方法内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。
            }

    import java.io.*;
    
    public class ThrowsTest {
        public static void main(String[] args) {
            ThrowsTest t = new ThrowsTest();
            try {
                t.readFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void readFile() throws IOException {
            FileInputStream in = new FileInputStream("hello.txt");
            int b;
            b = in.read();
            while (b != -1) {
                System.out.print((char) b);
                b = in.read();
            }
            in.close();
        }
    }

    四、手动抛出异常:throw

            Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。

            首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。
                    throw exceptionObject

            程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
                    throw new String("你抛我试试.");

            throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。

    public class StudentTest {
        public static void main(String[] args) {
            try {
                Student s = new Student();
                s.regist(-1001);
                System.out.println(s);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
    
    class Student {
        private int id;
    
        public void regist(int id) throws Exception {
            if (id > 0) {
                this.id = id;
            } else {
                // System.out.println("您输入的数据非法!");
    
                //手动抛出异常对象
                // throw new RuntimeException("您输入的数据非法!");
                throw new Exception("您输入的数据非法!");
    
                //错误的
                // throw new String("不能输入负数");
            }
    
        }
    
        @Override
        public String toString() {
            return "Student [id=" + id + "]";
        }
    }

    throws和throw的区别:

            throw是语句抛出一个异常。
                    语法:throw (异常对象);
                        throw e;

            throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常)
                    语法:[(修饰符)](返回值类型)(方法名)([参数列表])[throws(异常类)]{......}
                        public void doA(int a) throws Exception1,Exception3{......}

            throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
            throws出现在方法函数头,表示在抛出异常,由该方法的调用者来处理。

            throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。
            throw是具体向外抛异常的动作,所以它是抛出一个异常实例。

            throws说明你有那个可能,倾向。
            throw的话,那就是你把那个倾向变成真实的了。

            两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

    五、异常的链化

            在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致一连串的异常。假设B模块完成自己的逻辑需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常,但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。

            异常链化:以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫它根源异常(cause)。

            查看Throwable类源码,可以发现里面有一个Throwable字段cause,就是它保存了构造时传递的根源异常参数。这种设计和链表的结点类设计如出一辙,因此形成链也是自然的了。

    public class Throwable implements Serializable {
        private Throwable cause = this;
    
        public Throwable(String message, Throwable cause) {
            fillInStackTrace();
            detailMessage = message;
            this.cause = cause;
        }
    
        public Throwable(Throwable cause) {
            fillInStackTrace();
            detailMessage = (cause == null ? null : cause.toString());
            this.cause = cause;
        }
    
        //........
    }

            下面是一个例子,演示了异常的链化:从命令行输入2个int,将他们相加,输出。输入的数不是int,则导致getInputNumbers异常,从而导致add函数异常,则可以在add函数中抛出一个链化的异常。

    public static void main(String[] args){
        System.out.println("请输入2个加数");
        int result;
        try{
            result = add();
            System.out.println("结果:"+result);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    //获取输入的2个整数返回
    private static List<Integer> getInputNumbers(){
        List<Integer> nums = new ArrayList<>();
        Scanner scan = new Scanner(System.in);
        try {
            int num1 = scan.nextInt();
            int num2 = scan.nextInt();
            nums.add(new Integer(num1));
            nums.add(new Integer(num2));
        }catch(InputMismatchException immExp){
            throw immExp;
        }finally {
            scan.close();
        }
        return nums;
    }
    
    //执行加法计算
    private static int add() throws Exception{
        int result;
        try {
            List<Integer> nums =getInputNumbers();
            result = nums.get(0)  + nums.get(1);
        }catch(InputMismatchException immExp){
            throw new Exception("计算失败",immExp);  /链化:以一个异常对象为参数构造新的异常对象。
        }
        return  result;
    }
    
    /*
    请输入2个加数
    r 1
    java.lang.Exception: 计算失败
        at practise.ExceptionTest.add(ExceptionTest.java:53)
        at practise.ExceptionTest.main(ExceptionTest.java:18)
    Caused by: java.util.InputMismatchException
        at java.util.Scanner.throwFor(Scanner.java:864)
        at java.util.Scanner.next(Scanner.java:1485)
        at java.util.Scanner.nextInt(Scanner.java:2117)
        at java.util.Scanner.nextInt(Scanner.java:2076)
        at practise.ExceptionTest.getInputNumbers(ExceptionTest.java:30)
        at practise.ExceptionTest.add(ExceptionTest.java:48)
        ... 1 more
    
    */

    六、自定义异常

    自定义异常规则

            如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。

            按照国际惯例,自定义的异常应该总是包含如下的构造函数:
                    一个无参构造函数
                    一个带有String参数的构造函数,并传递给父类的构造函数。
                    一个带有String参数和Throwable参数,并都传递给父类构造函数。
                    一个带有Throwable 参数的构造函数,并传递给父类的构造函数。

    下面是IOException类的完整源代码,可以借鉴。

    public class IOException extends Exception {
        static final long serialVersionUID = 7818375828146090155L;
    
        public IOException() {
            super();
        }
    
        public IOException(String message) {
            super(message);
        }
    
        public IOException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public IOException(Throwable cause) {
            super(cause);
        }
    }

    自定义异常构建

    首先写一个自定义异常,继承Exception,代码如下:

    public class MyException extends Exception {
    
        public MyException() {
            super();
        }
    
        public MyException(String message) {
            super(message);
        }
    
        public MyException(String message, Throwable cause) {
            super(message, cause);
        }
    
    
        public MyException(Throwable cause) {
            super(cause);
        }
    }

    使用自定义异常

            如果自定义异常是为了提示,在使用的时候,一定要用try..catch,不要直接用throw往外抛。

    public class Test {
        public static void main(String[] args) {
            A a = new A();
            try {
                a.show(-2);
            } catch (MyException e) {
                System.out.println(e.getMessage());
            }
        }
    }
    
    class A {
        public void show(int num) throws MyException {
            if (num < 0) {
                MyException me = new MyException("异常:" + num + "不是正数");
                throw me;//抛出异常,结束方法show()的执行
            }
            System.out.println(num);
        }
    }

    七、异常的注意事项

            1、当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。

            例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。

            至于为什么?我想,也许下面的例子可以说明。

    class Father {
        public void start() throws IOException {
            throw new IOException();
        }
    }
    
    class Son extends Father {
        public void start() throws Exception {
            throw new SQLException();
        }
    }
    
    /**********************假设上面的代码是允许的(实质是错误的)***********************/
    class Test {
        public static void main(String[] args) {
            Father[] objs = new Father[2];
            objs[0] = new Father();
            objs[1] = new Son();
    
            for (Father obj : objs) {
                // 因为Son类抛出的实质是SQLException,而IOException无法处理它。
                // 那么这里的try。。catch就不能处理Son中的异常。
                // 多态就不能实现了。
                try {
                    obj.start();
                } catch (IOException ioException) {
                    // 处理IOException
                }
            }
        }
    }

            2、Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。

            也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。

    八、finally块和return

    1、一个不容易理解的事实:在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。

    public static void main(String[] args){
        int re = bar();
        System.out.println(re);
    }
    private static int bar() {
        try{
            return 5;
        } finally{
            System.out.println("finally");
        }
    }
    /*输出:
    finally
    5
    */

    2、finally中的return 会覆盖 try 或者catch中的返回值。

    public static void main(String[] args){
        int result;
    
        result  =  foo();
        System.out.println(result);     /2
    
        result = bar();
        System.out.println(result);    /2
    }
    
    @SuppressWarnings("finally")
    public static int foo(){
        try{
            int a = 5 / 0;
        } catch (Exception e){
            return 1;
        } finally{
            return 2;
        }
    
    }
    
    @SuppressWarnings("finally")
    public static int bar(){
        try {
            return 1;
        }finally {
            return 2;
        }
    }

    3、finally中的return会抑制(消灭)前面try或者catch块中的异常

    class TestException {
        public static void main(String[] args) {
            int result;
            try {
                result = foo();
                System.out.println(result);           //输出100
            } catch (Exception e) {
                System.out.println(e.getMessage());    //没有捕获到异常
            }
    
    
            try {
                result = bar();
                System.out.println(result);           //输出100
            } catch (Exception e) {
                System.out.println(e.getMessage());    //没有捕获到异常
            }
        }
    
        //catch中的异常被抑制
        @SuppressWarnings("finally")
        public static int foo() throws Exception {
            try {
                int a = 5 / 0;
                return 1;
            } catch (ArithmeticException amExp) {
                throw new Exception("我将被忽略,因为下面的finally中使用了return");
            } finally {
                return 100;
            }
        }
    
        //try中的异常被抑制
        //J2SE 提供的最后一个批注是 @SuppressWarnings。该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。
        @SuppressWarnings("finally")
        public static int bar() throws Exception {
            try {
                int a = 5 / 0;
                return 1;
            } finally {
                return 100;
            }
        }
    }

    4、finally中的异常会覆盖(消灭)前面try或者catch中的异常

    public class TestException {
        public static void main(String[] args) {
            int result;
            try {
                result = foo();
            } catch (Exception e) {
                System.out.println(e.getMessage());    //输出:我是finaly中的Exception
            }
    
    
            try {
                result = bar();
            } catch (Exception e) {
                System.out.println(e.getMessage());    //输出:我是finaly中的Exception
            }
        }
    
        //catch中的异常被抑制
        @SuppressWarnings("finally")
        public static int foo() throws Exception {
            try {
                int a = 5 / 0;
                return 1;
            } catch (ArithmeticException amExp) {
                throw new Exception("我将被忽略,因为下面的finally中抛出了新的异常");
            } finally {
                throw new Exception("我是finally中的Exception");
            }
        }
    
        //try中的异常被抑制
        @SuppressWarnings("finally")
        public static int bar() throws Exception {
            try {
                int a = 5 / 0;
                return 1;
            } finally {
                throw new Exception("我是finally中的Exception");
            }
    
        }
    }

    上面的3个例子都异于常人的编码思维,因此我建议:

            不要在fianlly中使用return。

            不要在finally中抛出异常。

            减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。

            将尽量将所有的return写在函数的最后面,而不是try ... catch ... finally中。

    更多相关内容
  • java异常 — — 异常的处理

    千次阅读 2021-08-31 12:21:02
    Java异常处理的五个关键字: try、catch、finally、throw、throws 2.1、抛出异常throw 在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时...

    二、异常的处理

    Java异常处理的五个关键字: trycatchfinallythrowthrows

    2.1、抛出异常throw

    在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不台法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。

    在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。

    抛出异常后:

    ① 创建一个异常对象。封装一些提示信息(信息可以自2编写)。

    ② 需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw 异常对象。

    throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

    使用格式:

    throw new xxxException(“异常产生的原因”);

    注意:

    ① throw关键字必须写在方法的内部。

    ② throw关键字后边new的对象必须是Exception或者Exception的子类对象

    ③ throw关键字抛出指定的异常对象,我们就必须处理这个异常对象。

    Throw关键字后边创建的是RuntimeException或者是RuntimeException的子 类对象。我们可以不处理默认交给JVM处理(打印异常对象,中断程序)。

    throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异 常,要么throws,要么try…catch。

    以后(工作中)我们首先必须对方法传递过来的参数进行合法性校验,如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题。

    2.2、Objects非空判断

    0bjects类由一些静态的实用方法组成 。这些方法是null-save (空指针安全的)或null-tolerant (容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操作。

    public static T requireNonNull(T obj) : 直看指定引用对象不是null。(可以直接用这个方法判断是否为空)

    查看源码发现这里对为null的进行了抛出异常操作:

    在这里插入图片描述

    2.3、声明异常throws

    声明异常: 将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理。

    关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)。

    声明异常格式(方法声明时使用)
    在这里插入图片描述

    注意事项:

    ① throws关键字必须写在方法声明处。

    ② throws关键字后边声明的异常必须是Exception或者是Exception的子类。

    ③ 方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常如果 抛出的多个异常对象有子父类关系,那么直接声明父类异常即可

    ④ 调用了一个声明抛出异常的方法,我们就必须的处理声明的异常要么继续使用 throws声明抛出,交给方法的调用者处理,最终交给JVM要么try…catch自己 处理异常。

    在这里插入图片描述

    2.4、捕获异常try…catch

    如果异常出现的话,会立刻终止程序,所以我们得处理异常:

    ① 该方法不处理而是声明抛出,由该方法的调用者来处理(throws)。

    ② 在方法中使用try…catch的语句块来处理异常。

    try-catch的方式就是捕获异常。

    捕获异常: Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方 式的处理。

    捕获异常语法如下:

    try{
        		//可能产生异常的代码
        }catch(/*定义一个异常的变量,用来接收try中抛出的异常对象*/){
        		//异常的处理逻辑,异常对象之后,怎么处理异常对象
        		//一般在工作中。会把异常的信息记录到一个日志中
        }
        catch(/*异常类名变量名*/){
    }
    

    注意:

    ① try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象

    ② 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,维续执行try…catch之后的代码

    ③ 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码

    2.5、Throwable类中的异常处理方法

    Throwable类中定义了一些查看方法:

    public String getMessage(): 获取异常的描述信息,原因(提示给用户的时候就提示错误原因),返回此throwable 的简短描述。
    public String toString() : 获取异常的类型和异常描述信息(不用),返回此throwable 的详细消息字串。
    public void printstackTrace(): 打印异常的跟踪栈信息并输出到控制台,默认此方法,打印的异常信息是最全面的。

    包含了异常的类型异常的原因还包括异常出现的位置在开发和调试阶段都得使用printStackTrace。

    在这里插入图片描述

    在开发中呢也可以在catch将编译期异常转换成运行期异常处理。

    2.6、finally代码块

    finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

    什么时候的代码必须最终执行?

    当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

    finally的语法:

    try…catch…finally : 自身需要处理异常,最终还得关闭资源。

    注意finally不能单独使用

    比如之后的I0流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

    finally代码参考如下:

    在这里插入图片描述

    2.7、异常注意事项

    多个异常使用捕获处理方式:

    ① 多个异常分别处理。

    ② 多个异常一次捕获,多次处理。

    ③ 多个异常一次捕获一次处理。

    一般是使用一次捕获多次处理方式,格式如下:

    在这里插入图片描述

    注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理。父类异常在下面的catch处理。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    ● 运行时异常被抛出可以不处理,即不捕获也不声明抛出,默认给虚拟机处理。

    ● 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。

    ● 如果finally有return语句,永远返回finally中的结果,避免该情况。

    在这里插入图片描述

    ● 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集

    ● 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

    父类异常是什么样,子类异常就是什么样。

    在这里插入图片描述
    在这里插入图片描述

    一个try多个catch注意事项:

    catch里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则就会报错。

    在这里插入图片描述

    展开全文
  • java异常处理(较详细)

    万次阅读 多人点赞 2020-02-21 17:30:34
    java异常处理(较详细) 在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的。 比如: 客户输入数据的格式, 读取文件...

    在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的。
    比如: 客户输入数据的格式, 读取文件是否存在, 网络是否始终保持通畅等等。

    异常:在Java语言中, 将程序执行中发生的不正常情况称为“异常” 。
    (开发过程中的语法错误和逻辑错误不是异常)
    Java程序在执行过程中所发生的异常事件可分为两类:

    • Error: Java虚拟机无法解决的严重问题。 如: JVM系统内部错误、 资源耗尽等严重情况。
      比如: StackOverflowError和OOM。 一般不编写针对性的代码进行处理。
    • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题, 可以使用针对性的代码进行处理。 例如:
      1、空指针访问
      2、试图读取不存在的文件
      3、网络连接中断
      4、数组角标越界

    对于这些错误, 一般有两种解决方法:
    一种是遇到错误就终止程序的运行。
    一种方法是由程序员在编写程序时, 就考虑到错误的检测、 错误消息的提示, 以及错误的处理。

    • 捕获错误最理想的是在编译期间, 但有的错误只有在运行时才会发生。
      比如: 除数为0, 数组下标越界等
      分类: 编译时异常和运行时异常

    1.运行时异常
    是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。 java.lang.RuntimeException类及它的子类都是运行时异常。
     对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。

    2.编译时异常
    是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。
    编译器要求Java程序必须捕获或声明所有编译时异常。
    对于这类异常,如果程序不处理,可能会带来意想不到的结果。

    异常处理机制一:try-catch-finally

    在编写程序时,经常要在可能出现错误的地方加上检测的代码,
    如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。
    过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。

    Java异常处理
    Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,
    使得程序简洁、优雅, 并易于维护。

    java异常处理的方式:
    方式一: try-catch-finally
    方式二: throws + 异常类型

    Java提供的是异常处理的抓抛模型。
    过程一:Java程序的执行过程中如出现异常, 会生成一个异常类对象,该异常对象将被提交给Java运行时系统, 这个过程称为抛出(throw)异常。
    过程二:抓,可以理解为异常的护理方式。

    异常对象的生成
    由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出
    由开发人员手动创建: Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样。
    在这里插入图片描述如果一个方法内抛出异常, 该异常对象会被抛给调用者方法中处理。
    如果异常没有在调用者方法中处理, 它继续被抛给这个调用方法的上层方法。
    这个过程将一直继续下去, 直到异常被处理。这一过程称为捕获(catch)异常。
    如果一个异常回到main()方法, 并且main()也不处理, 则程序运行终止。
    程序员通常只能处理Exception, 而对Error无能为力。
    异常处理是通过try-catch-finally语句实现的。
    在这里插入图片描述try
    捕获异常的第一步是用try{…}语句块选定捕获异常的范围, 将可能出现异常的代码放在try语句块中。在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理,一旦处理完成就跳出当前的try-catch结构(在没有写finally的情况下)。继续执行其后的代码。在try结构中声明的变量,出了try结构之后,就不能被调用。

    catch (Exceptiontype e)
    在catch语句块中是对异常对象进行处理的代码。 每个try语句块可以伴随一个或多个catch语句, 用于处理可能产生的不同类型的异常对象。catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓,如果异常类型满足子父类的关系,则要求子类一定要声明在父类的上面,否则报错。

    finally
    捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
    不论在try代码块中是否发生了异常事件, catch语句是否执行, catch语句是否有异常, catch语句中是否有return,finally块中的语句都会被执行。
    finaly中声明的是一定会被执行的代码,即使catch中出现了异常,try中有return语句,catch中有return语句等情况。
    finally语句和catch语句是任选的。
    像数据库连接,输入输出流,网络编程socket等资源,JVM是不能自动回收的,我们需要手动的进行资源的释放。此时的资源的释放,就需要声明在finally中。

    注意:
    如果明确知道产生的是何种异常, 可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。
    比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException类作为参数, 或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常, 如NullPointerException(catch中的语句将不会执行) 。使用try-cathch-finally处理编译时异常,使得程序在编译时不在报错,但在运行时,仍有可能报错。相当我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
    try-catch-finally结构可以相互嵌套。

    捕获异常的有关信息:
    与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
    getMessage() 获取异常信息,返回字符串
    printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
    在这里插入图片描述不捕获异常时的情况
    前面使用的异常都是RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获, Java自己也能捕获,并且编译通过( 但运行时会发生异常使得程序运行终止 )。

    如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为运行时异常。

    异常处理机制二:throws +异常类型

    声明抛出异常是Java中处理异常的第二种方式
    throws +异常类型,写在方法的声明处,此时此方法执行时, 可能会抛出的类型。
    如果一个方法(中的语句执行时)可能生成某种异常, 但是并不能确定如何处理这种异常, 则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
    在方法声明中用throws语句可以声明抛出异常的列表, throws后面的异常类型可以是方法中产生的异常类型, 也可以是它的父类。
    一旦方法体执行时,出现异常,仍会在异常代码处,生成一个异常类的对象,此对象满足throws后异常类型是,就会被抛出。异常代码后续的代码,就不在执行。
    throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉。
    声明抛出异常举例:
    public void readFile(String file) throws FileNotFoundException {
    ……
    // 读文件的操作可能产生FileNotFoundException类型的异常
    FileInputStream fis = new FileInputStream(file);
    ………
    }

        public void method() {
              try {
                  test3();
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
         }
         
         @Test
         public void test3() throws  FileNotFoundException,IOException {
              File file =new File("heool.txt");
              FileInputStream fis = new FileInputStream(file);
              
              int data = fis.read();
              while(data != -1) {
                  System.out.println((char)data);
                  data = fis.read();
              }
              fis.close();
         }
    }
    

    在这里插入图片描述重写方法声明抛出异常的原则
    重写方法不能抛出比被重写方法范围更大的异常类型。 在多态的情况下,
    对methodA()方法的调用-异常的捕获按父类声明的异常处理。

    
    public class A {
    public void methodA() throws IOException {
    ……
    } }
    public class B1 extends A {
    public void methodA() throws FileNotFoundException {
    ……
    } }
    public class B2 extends A {
    public void methodA() throws Exception { //报错
    ……
    } }
    

    如何选择try-catch和throws
    如果父类中被重写的方法中没有throws方式处理异常,则子类重写的方法中也不能使用throws,意味如果子类重写的方法中有异常,必须使用try-catch。
    执行的方法a中,先后又调用了另外的几个方法,这九个方法是递进关系执行的,我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch

    手动抛出异常
    java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出, 也可根据需要使用人工创建并抛出。
    首先要生成异常类对象, 然后通过throw语句实现抛出操作(提交给Java运行环境)。
    IOException e = new IOException();
    throw e;
    可以抛出的异常必须是Throwable或其子类的实例。 下面的语句在编译时将会产生语法错误:
    throw new String(“want to throw”);

    public class ExceptionTest3 {
         public static void main(String[] args) {
              try {
                  Student stu=new Student();
                  stu.register(-1003);
                  System.out.println(stu);
              } catch (Exception e) {
                  System.out.println(e.getMessage());
              }
         }
    }
    class Student{
         private int id;
         public void register(int id) throws Exception {
              if(id>0) {
                  this.id =id;
              }else {
                  //System.out.println("输入的id不合法");
                  //手动抛出异常
                  //throw new RuntimeException("你输入的数据不合法");
                  throw new Exception("输入的数据不合法");
              }
         }
         @Override
         public String toString() {
              return "Student [id=" + id + "]";
         }
    }
    

    用户自定义异常类
    一般地,用户自定义异常类都是RuntimeException的子类。
    自定义异常类通常需要编写几个重载的构造器。
    自定义异常需要提供全局常量:serialVersionUID
    自定义的异常通过throw抛出。
    自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。

    用户自定义异常类MyException,用于描述数据取值范围错误信息。用户自己的异常类必须继承现有的异常类。
    在这里插入图片描述在这里插入图片描述总结
    在这里插入图片描述一首小悟结束异常处理
    世界上最遥远的距离,是我在if里你在else里,似乎一直相伴又永远分离;
    世界上最痴心的等待,是我当case你是switch,或许永远都选不上自己;
    世界上最真情的相依,是你在try我在catch。无论你发神马脾气,
    我都默默承受,静静处理。到那时,再来期待我们的finally。

    来自bilibili尚硅谷宋红康老师笔记

    展开全文
  • Java异常类处理

    千次阅读 多人点赞 2020-08-15 15:42:03
    一、Java异常类的体系结构   1.程序出现异常时正常现象【就跟人会生病一样】   2.Java异常类Throwable【类】是顶级异常类   3.Throwable【类】有两个子类:     (1)Error【错误】     (2)...

    一、Java中异常类的体系结构

      1.程序出现异常时正常现象【就跟人会生病一样】
      2.Java中异常类Throwable【类】是顶级异常类
      3.Throwable【类】有两个子类:
        (1)Error【错误】
        (2)Exception【异常】

    在这里插入图片描述

    二、Error与Exception的区别

      Error是java程序运行中不可预料的异常情况,这种异常发生后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。【癌症】
      Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。【感冒】

    三、运行时异常与非运行时异常的区别

      非运行时异常—>检查性异常 必须在编写代码时,使用try catch捕获(比如:IOException异常)。
      运行时异常—>非检查性异常 在代码编写时,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。

    四、Java异常处理的原理

      1.问题自己处理不了
      如果出现异常,java会根据问题所描述的异常类,创建一个对象(实例)具体异常对象,然后将该对象抛给上一级【谁调用谁就是上一级】,具体步骤:
    method具体出异常处 —> main主方法 —> jvm虚拟机 —> 将异常出现的位置和原因打印在控制台。

      2.问题可以自己处理掉–通过try-catch代码块处理异常
      将可能出现异常的java代码使用”try{异常可能发生的代码}catch(要抓捕的异常){异常处理方式}”块包裹,如果出现异常try{}就会捕获异常,终止代码的运行,将捕获的异常交给catch(){}去处理异常。

    五、try{}catch(){}finally{}的用法

      异常可以自己处理掉 — 通过try-catch代码块处理异常
    格式:

    try{
    //异常可能发生的代码
    }catch(//要抓捕的异常){
    //异常处理方式
    }finally{
    //最终代码
    }
    

      (1)try —> 捕获可能发生的异常
      (2)catch(//要抓捕的异常){//异常处理方式} —> 处理异常
        catch后面的" ( ) "中专门定义具体异常类型
        catch的 {} —> 具体异常的处理过程
      (3)finally{} —> 有无异常都要执行的动作
    例如:

    package com.wangxing.test1;
    public class TestMain {
    	public static void main(String[] args) {
    		//1.当我们无法判断try{}何种具体异常的时候,
    		//我们可以在catch块的参数中通过定义Exception/Throwable类的对象来代替具体异常类型。
    		/*
    		try {
    			int res=10/0;
    			System.out.println("res=="+res);
    		}catch(Exception e) {
    			System.out.println("处理异常!");
    		}
    		*/
    		//2.一个try{}后面可以跟随多个catch块,多个catch块需要按照具体异常的级别由低到高排列
    		try{
    		int  numbers[]=new int[2];
    		numbers[0]=10;
    		numbers[1]=0;
    		int num1=numbers[0];
    		int num2=numbers[1];
    		int num3=num1/num2;
    		numbers[2]=num3;
    		System.out.println("程序执行完毕!!");
    		}catch(IndexOutOfBoundsException e3) {
    			System.out.println("处理数组下标越界异常!");
    		}catch(ArithmeticException e2) {
    			System.out.println("处理除数为0的异常!");
    		}catch(Exception e1) {
    			System.out.println("exception异常!");
    		}catch(Throwable e) {
    			System.out.println("Throwable异常!");
    		}finally {
    			System.out.println("有无异常都要执行");
    		}
    	}
    }
    
    

    运行结果:

    在这里插入图片描述

      1.当我们无法判断try{}何种具体异常的时候,我们可以在catch块的参数中通过定义Exception/Throwable类的对象来代替具体异常类型。
      2.一个try{}后面可以跟随多个catch块,多个catch块需要按照具体异常的级别由低到高排列
      3.finally{}–有无异常都要执行的动作
      4.当try{}catch(){}finally{}出现在一个有返回值的方法中时,finally{}中的内容实在本方法的return语句之前运行。
    例如:

    package com.wangxing.test2;
    public class TestClass {
    	public  int  testMehtod() {
    		try {
    			int a=10;
    			return a;
    		}catch(Exception e) {
    			//控制台打印具体异常
    			e.printStackTrace();
    		}finally {
    			System.out.println("有无异常都执行");
    		}
    		return 0;
    	}
    }
    
    
    package com.wangxing.test2;
    
    public class TestMain {
    
    	public static void main(String[] args) {
    		TestClass tc=new TestClass();
    		System.out.println(tc.testMehtod());
    	}
    }
    

    运行结果:

    在这里插入图片描述

    六、throws与throw关键字的用法

      throws —> 声明方法抛出异常给上一级【谁调用谁就是上一级】

      格式:访问限制修饰符 返回值类型 方法名称()throws 具体异常类型{ }

      当我们无法判断读具体异常类型的时候使用Exception/Throwable代替
    例如:

    package com.wangxing.test3;
    public class DoClass {
    	public  int  testMethod() throws Exception{
    		int  a=10/0;
    		return a;
    	}
    }
    
    
    package com.wangxing.test3;
    public class TestMain {
    	public static void main(String[] args) throws Exception{
    		DoClass  dc=new DoClass();
    		int res=dc.testMethod();
    		System.out.println("res=="+res);
    	}
    }
    
    

    运行结果:

    在这里插入图片描述

      throw —> 手动引发一个具体异常
      自定义异常 —> 编写一个新类,继承Exception/Throwable,在构造方法中访问父类的构造方法。

    package com.wangxing.test4;
    /**
     * 我自己定义的异常类
     * @author Administrator
     *
     */
    public class MyException extends Exception{
    	public  MyException(String info) {
    		super(info);
    	}
    }
    
    
    package com.wangxing.test4;
    public class Person {
    	private int  age;
    	public  void setAge(int inputage) throws MyException{
    		if(inputage > 0 && inputage <= 200) {
    			age=inputage;
    		}else{
    			throw new MyException("你提供的年龄不符合要求!!");
    		}
    	}
    	public  int  getAge() {
    		return age;
    	}
    }
    
    
    package com.wangxing.test4;
    public class TestMain {
    	public static void main(String[] args) throws MyException{
    		Person  person=new  Person();
    		person.setAge(-10);
    		System.out.println("age=="+person.getAge());
    	}
    }
    

    运行结果:

    在这里插入图片描述

    七、Java中常见运行时异常

      1. NullPointerException - 空指针引用异常
      2. ClassCastException - 类型强制转换异常。
      3. IllegalArgumentException - 传递非法参数异常。
      4. ArithmeticException - 算术运算异常
      5. ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
      6. IndexOutOfBoundsException - 下标越界异常
      7. NegativeArraySizeException - 创建一个大小为负数的数组错误异常
      8. NumberFormatException - 数字格式异常
      9. SecurityException - 安全异常
      10. UnsupportedOperationException - 不支持的操作异常

    总结

    在这里插入图片描述

    展开全文
  • Java异常处理(Java必备知识)

    千次阅读 2021-02-01 01:27:09
    Java提供了异常处理机制来帮助程序员检查可能出现的错误,提高了程序的可读性和可维护性。Java中将异常封装到一个类中,出现错误就会抛出异常。 目录 异常概述 捕捉处理异常 在方法中抛出异常 ......
  • 【Java基础知识 6】Java异常详解

    千次阅读 多人点赞 2021-09-08 22:06:51
    一、try catch简介 大家对trycatch可能并不陌生,也都使用的非常熟练了...然而,有很多常见的异常,比如空指针异常,都属于未检查异常,编译器不会发现这个地方是否会爆空指针异常。 2、简单语法 try{ //代码区 }
  • JAVA异常详解

    万次阅读 多人点赞 2019-06-12 23:50:51
    java中,阻止当前方法或作用域的情况,称之为异常异常的分类 Error:是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual Machine...
  • JAVA异常处理

    千次阅读 2021-03-03 13:27:11
    出bug是写代码的时候非常常见的情况,今天就来讲一下。首先明确一下什么是异常,代码正常运行不会出问题,但是遇到意外...//java.lang.ArithmeticException:/ by zero除以零结果无穷大异常2.String str = null;Syst...
  • Java 异常处理 之 手动抛出异常

    千次阅读 2021-03-05 16:38:58
    一、手动抛出异常 throw在编写程序时,我们必须要考虑程序出现问题的情况。...Java 异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可以根据需要使用人工创建并抛出。在java中,提...
  • java异常处理机制

    千次阅读 2021-01-11 19:42:29
    一、什么是异常 异常机制: 异常机制是指当程序出现错误后,程序如何处理。具体来说就是程序发生异常异常机制提供程序的退出安全通道。 通俗来说:就是为了让程序继续执行下去,不至于中断。 程序错误: 程序...
  • java异常处理机制原理和应用

    千次阅读 多人点赞 2021-11-20 13:50:48
    一、Java异常简介 大家对trycatch可能并不陌生,也都使用的非常熟练了。 当程序运行过程中发生错误时,就会抛出异常,抛出异常总比终止程序来的好的多。 也可以在已知某个错误要发生时,进行trycatch操作,异常时...
  • java.lang.ThrowableError是错误,java.lang.ErrorException是异常java.lang.ExceptionThrowable:有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。...
  • Java异常处理的两种方式

    千次阅读 2021-11-15 12:43:43
    异常处理方式一:在当前方法中直接用try…catch处理 异常处理方式二:在当前方法中不处理,throws 异常抛给调用者处理 一. try-catch的方式就是捕获异常。 try:该代码块中编写可能产生异常的代码。 ...
  • JAVA 异常处理小技巧

    千次阅读 2021-03-01 06:33:12
    1、异常:就是程序运行时出现不正常情况异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述。并封装成对象。其实就是java对不正常情况进行描述后的对象体现。对于问题的划分(两种):一...
  • java异常分类,异常处理,面试中常见异常问题!

    千次阅读 多人点赞 2019-05-14 13:03:06
    Throwable 类是 Java 语言中所有错误或异常的超类。提供了错误堆栈实现等一系列方法。 有两个直接子类:Error & Exception 程序错误一般分为三种: 1.编译错误;2.运行时错误;3.逻辑错误。 (1)编译错误是因为...
  • Java 异常

    千次阅读 多人点赞 2019-04-27 20:18:44
    Java异常 Java异常是Java提供的一种识别及响应错误的一致性机制。 Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答3...
  • java异常的三种打印方法Whenever exception throw then Throwable class provides various methods to provide exception related information like Exception name, Exception description and Stack Trace, etc. ...
  • Java异常面试题

    万次阅读 多人点赞 2019-09-29 11:55:48
    文章目录1. Error 和 Exception 区别是什么?2. 运行时异常和一般异常区别是什么?3.NoClassDefFoundError 和 ClassNotFoundException 区别?4. JVM 是如何处理异常的?...7. Java常见异常有哪些 1. Error 和 Exce...
  • Java异常(超详细!)

    万次阅读 多人点赞 2021-05-03 11:43:14
    1、什么是异常java提供异常处理机制有什么用? 什么是异常:程序执行过程中的不正常情况。 异常的作用:增强程序的 健壮性。 eg. public class ExceptionTest01 { public static void main(String[] args) { ...
  • Java异常处理机制

    千次阅读 2019-04-24 20:19:37
    作者:小符 撰写时间:2018.4.24 学会了异常处理可以帮助...Java异常处理机制为:抛出异常,捕捉异常,处理异常。 语法格式: try…catch 在使用try…catch捕获处理异常时需要注意: **·**不要过度使用异常,不能使...
  • java 异常分类和处理机制

    万次阅读 多人点赞 2018-06-01 15:08:26
    一、背景介绍 程序在运行过程中发生错误或异常情况是不可避免的,如果每一个运行时错误都由...Java语言定义了很多异常类,将运行错误和异常的信息和处理方法封装在了异常类中,帮助程序员检查和控制异常。即J...
  • Java异常类型及处理

    万次阅读 多人点赞 2018-09-21 10:54:18
    Java异常,大家都很熟悉。但是对于具体怎么分类的,JVM对其怎么处理的,代码中怎么处理的,应该怎么使用,底层怎么实现的等等,可能就会有些不是那么清晰。本文基于此详细捋一下异常类型,实现以及使用时应怎么注意...
  • java 异常(Exception)详解

    千次阅读 2021-04-28 14:58:58
    Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理(停止java虚拟机JVM)。 异常的体系 java.lang.Throwable类:是异常和错误的最顶层的...
  • Java异常机制及异常处理建议

    千次阅读 2017-08-26 12:12:51
    1、 Java异常机制 异常指不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。Java通过API中Throwable类的众多子类描述各种不同的异常。...
  • Java异常

    千次阅读 2022-03-07 21:38:46
    目录 Error: Exception: Error和Exception的区别: ...Error类对象由Java...这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。 还有发生在虚拟机试图执行应用是,如类定义错误(NoClassDefFoundError
  • Java异常之InputMismatchException的解决

    万次阅读 多人点赞 2020-12-31 10:04:25
    InputMismatchException异常是输入不匹配异常,即输入的值数据类型与设置的值数据类型不能匹配。 二、异常的出现原因 在使用Scanner进行输入时,报出InputMismatchException异常,其代码如下: public static void ...
  • java异常处理

    万次阅读 2018-02-14 17:54:11
    java异常处理 java异常体系 Java的所有异常继承自Throwable,分为error(错误)和Exception(异常),Exception又分为可查异常和不可查异常,可查异常指的是编译器可以检查处理来,在编写代码的时候需要进行处理...
  • java异常体系结构详解

    千次阅读 2018-03-15 15:51:38
    前几天在参加网易和360公司的在线考试的时候,都出了一道关于java异常类的多项选择题。这几天翻看了相关书籍和网上一些资料,结合自己的理解与思考,将自己的一些收获记录如下:先来看看java异常的体系结构图解...
  • Java异常处理和异常抛出

    千次阅读 多人点赞 2019-02-21 13:11:44
    生活中的异常是指那些有异于常态,和正常情况不一样,有问题的状态,编程世界中的异常是指阻止当前方法或作用域继续执行的状态。比如,程序要打开一个不存的文件、网络连接中断、除零操作、操作数...一.Java异常体系...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,669,038
精华内容 667,615
关键字:

java异常

java 订阅