异常全家桶来咯,异常捕获,异常抛出,自定义异常

C#开发——如何捕获异常抛出异常 在 C#中,可以通过“catch”块捕获异常,并通过“is”关键字或“as”关键字来判断异常的具体类型。 阅读详情

1.捕获异常:

        在Java中,凡是可能抛出异常的语句,都可以用try...catch捕获。把可能发生异常的语句放在try{...}中,然后使用catch捕获对应的Exception及其子类。

        1.多catch语句

                可以使用多个catch语句,每个catch分别捕获对应的Exception及其子类。JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配。简单地说就是多个catch语句只有一个能被执行。例如:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println(e);
    } catch (NumberFormatException e) {
        System.out.println(e);
    }
}

                存在多个catch的时候,catch的顺序非常重要,子类必须写在前面,例如:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("IO error");
    } catch (UnsupportedEncodingException e) { // 永远捕获不到
        System.out.println("Bad encoding");
    }
}

                对于上面的代码,UnsupportedEncodingException异常是永远捕获不到的,因为它是IOException的子类,当抛出UnsupportedEncodingException异常时,会被catch(IOException e){...}捕获并执行。因此,正确的做法应该是把子类一场放到前面:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    }
}

       

        2.finally语句 

                无论是否有异常发生,我们都希望执行一些语句,例如清理工作,可以把执行语句写若干遍:正常执行的放在try中,每个catch再写一遍,例如:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
        System.out.println("END");
    } catch (IOException e) {
        System.out.println("IO error");
        System.out.println("END");
    }
}

                但是这行代码是我们想必须执行的System.out.println("中国抗击疫情必将胜利!!!");  但是如果抛出前面调用方法如果产生异常的话,就不会执行这一行代码了,那我们可以利用finally语句块保证有无错误都会执行,可改写上述代码为:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

                注意finally的几个特点:

                        1.finally语句不是必须的,可以写可以不写;

                        2.finally总是最后执行的;

               如果没有没有发生异常,就正常执行try{...}语句块,然后执行finally。如果发生了异常,就中断执行try{...}语句块,然后跳转执行匹配的catch语句块,最后执行finally。可见finally是用来保证一些代码必须执行的。

                某些情况下,我们也可以没有catch,只是用try...finally结构的,例如:

void process(String file) throws IOException {
    try {
        ...
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

        3.捕获多种异常:

                如果某些异常的处理逻辑相同,但是异常本身之前又不存在继承关系,那么就需要编写多条catch语句,例如:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("Bad input");
    } catch (NumberFormatException e) {
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                因为处理IOException和NumberFormatException的代码时相同的,所以我们可以把她它两用 | 合并到一起,例如:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                使用try...catch...finally捕获异常小结:

                        1.多个catch语句的匹配顺序非常重要,子类必须放在前面。

                        2.finally语句保证了有无异常都会执行,它时可选的。

                        3.一个catch语句也可以匹配多个非继承关系的异常。

2.抛出异常:

        1.异常的传播路线:

                当某个方法抛出了异常时,如果当前方法没有捕获异常,异常就会被抛到上层调用方法,直到遇到某个try...catch被捕获为止:

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        process2();
    }

    static void process2() {
        Integer.parseInt(null); // 会抛出NumberFormatException
    }
}

                通过printStackTrace()可以打印出方法的调用栈,例如:

java.lang.NumberFormatException: null
    at java.base/java.lang.Integer.parseInt(Integer.java:614)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.process2(Main.java:16)
    at Main.process1(Main.java:12)
    at Main.main(Main.java:5)

                printStackTrace()对于调试错误非常有用,上述信息表示:NumberFormatException是在java.lang.Integer.parseInt方法中被抛出的,从下往上看,调用层依次是:

                        1.main()调用process1();

                        2.process1()调用process2();

                        3.process2()调用Integer.parseInt(String);

                        4.Integer.parseInt(String)调用Integer.parseInt(String,int);

                查看Integer.java源码可知,抛出异常方法的代码如下:

public static int parseInt(String s, int radix) throws NumberFormatException {
    if (s == null) {
        throw new NumberFormatException("null");
    }
    ...
}

        2.抛出异常

                当发生错误时,比如用户输入了 非法的字符,我们就可以抛出异常,如何抛出异常呢?参考上面Integer.parseInt()方法,抛出异常分两步:

                        1.创建某个Exception的实例;

                        2.使用throw语句抛出;

                例如:

void process2(String s) {
    if (s==null) {
        NullPointerException e = new NullPointerException();
        throw e;
        //throw new NullPointerException();
    }
}

                实际上绝大部分抛出异常的代码会合成一行如上述注释掉的那样或者下述这样:

void process2(String s) {
    if (s==null) {
        throw new NullPointerException();
    }
}

                如果一个方法捕获了某个异常后,又在catch自居中抛出新的异常,就相当于把抛出的异常类型转换了:

void process1(String s) {
    try {
        process2();
    } catch (NullPointerException e) {
        throw new IllegalArgumentException();
    }
}

void process2(String s) {
    if (s==null) {
        throw new NullPointerException();
    }
}

                当process2()抛出NullPointerException后,被process1()捕获,然后抛出IllegalArgumentException()。如果在main()中捕获IllegalArgumentException,wine吧打印异常栈看看,如下:

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException();
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

                打印出的异常栈类似于:

java.lang.IllegalArgumentException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)

                这说明新的异常丢失了原始异常或者说根本异常的信息,我们已经看不到原始异常NullPointerException的信息了。为了能够追踪到完整的异常栈信息,在构造异常的时候,把原始的Exception实例传进去,新的Exception就可以持有原始Exception信息了,对上述代码修改如下:

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(e);
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

                运行上述代码,打印出的异常栈类似于,如下:

java.lang.IllegalArgumentException: java.lang.NullPointerException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
    at Main.process2(Main.java:20)
    at Main.process1(Main.java:13)

                注意到:Caused by:Xxx,说明捕获的IllegalArgumentException并不是造成问题的根本原因,根本原因在于NullPointerException,时在Main.process2()方法中抛出的。在代码中获取原始异常信息可以使用Throwable.getCause()方法。如果返回null,则说明该异常已经是产生问题根本异常了。有了完整的异常栈信息,我们才可以快速的定位并修改代码解决问题。

捕获异常并再次抛出时,一定要保留原始异常,以便于更快的发现第一案发现场!!!

                如果我们在try或者catch语句块中抛出异常,finally语句块是否还会执行呢?例如:

public class Main {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        } catch (Exception e) {
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
            System.out.println("finally");
        }
    }
}

                上述代码执行结果如下:

catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"
    at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"
    at ...

                第一行打印了catchd,说明进入了catch语句块,第二行打印了finally,说明执行了finally语句块,因此我们可以得知,在catch中抛出异常时,不会影响finally的执行,JVM会先执行finally,然后再抛出了异常。

        3.异常屏蔽:

                如果在执行finally语句时抛出异常,那么catch语句块还能否继续抛出呢? 例如:

public class Main {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        } catch (Exception e) {
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
            System.out.println("finally");
            throw new IllegalArgumentException();
        }
    }
}

                执行上述代码发现异常信息如下:

catched
finally
Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)

                控制台的输出说明finally抛出异常后,原来在catch中准备抛出的异常就"消失"了,因为只能抛出一个异常。没有被抛出的异常称之为"被屏蔽"的异常(Suppressed Exception)。在极少数情况下,我们需要获知所有的异常,那该如何保存所有的异常信息呢?方法是先用变量保存原始一场,然后调用Throwable.addSuppressed(),把原始一场添加进来,然后在finally中抛出,如下:

public class Main {
    public static void main(String[] args) throws Exception {
        Exception origin = null;
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (Exception e) {
            origin = e;
            throw e;
        } finally {
            Exception e = new IllegalArgumentException();
            if (origin != null) {
                e.addSuppressed(origin);
            }
            throw e;
        }
    }
}

                当catch和finally都抛出了异常时,虽然catch的异常被屏蔽了,但是finally抛出的异常仍然包含了它:

Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.base/java.lang.Integer.parseInt(Integer.java:652)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.main(Main.java:6)

                通过Throwable.getSuppressed()可以获取所有的Suppressed Exception。绝大多数情况下,在finally中不要抛出异常。因此,我们通常不关心Suppressed Exception。

                

                抛出异常小结:

                        1.调用printStackTrace()可以打印异常的传播栈,对于调试程序非常有效;

                        2.捕获异常并再次抛出新的异常时,应该持有原始异常信息;

                        3.通常不要在finally中抛出异常。如果在finally中抛出异常,应该将原始异常加入到哦原有异常中,调用方可通过Throwable.getSuppressed()获取所有添加的Suppressed Exception。

3.自定义异常:

                当我们在代码中需要抛出异常时,尽量使用JDK已经定义的异常。例如:参数类型不合法,我们应该抛出IllegalArgumentException:

static void process1(int age) {
    if (age <= 0) {
        throw new IllegalArgumentException();
    }
}

                当然在大型项目中,可以自定义新的异常类型,但是,保持一个合理的一场继承体系是非常重要的,通常的做法是自定义一个异常作为"根异常",然后,派生出各种业务类型的异常。根异常需要从一个适合的Exception派生,通常建议从RuntimeException派生:

public class BaseException extends RuntimeException {
}

                其他异常类型都可以从根异常派生:

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

...

                自定义的根异常应该是需要提供多个构造方法的(最好是跟RuntimeException对应):

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

                在上述构造方法参考RuntimeException原生实现。这样,抛出异常的时候,就可以选择合适的构造方法。

                自定义异常小结:

                        1.抛出异常时,尽可能复用JDK已经定义好的异常类型;

                        2.自定义异常体系时,建议从RuntimeException类派生根异常,然后从根异常在派生出其他类型的异常。

                        3.自定义异常时,尽量模仿RuntimeException类,提供多种构造方法。

【第七天】C++异常抛出捕获以及标准异常 异常:是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要 读取的文件不存在,空指针,内存不足,访问非法内存等等)。(异常是一个类。)异常处理就是处理程序中的错误。 阅读详情

相关推荐

gtsam 学习十(ISAM2 理论)

翻译自:iSAM2: Incremental Smoothing and Mapping Using the Bayes Tree 摘要 提出了一种新型的贝斯树处理稀疏矩阵,在转化为因子图,可以更好的求解平方根信息和映射问题。 文中提出了三个概念 使用概率密度项进行矩阵分解 介绍如何将矩阵分解转换为贝叶斯数和条件概率密度 基于贝叶斯树提供了一种新的系数非线性优化:ISAM2(进行重新排序和重新线性化,避免了批量更新) 问题描述 文章聚焦与如何用增量实时解决非线性问题, 利用增量即新的测量值实时更新估

weixin_37781153的博客 8368

Java 异常体系(美团面试)

Java把异常作为一种类,当做对象来处理。所有异常类的基类是Throwable类,两大子类分别是Error和Exception。   系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。   异常(Exception)又分为RuntimeExcep...

551

Python数据爬取超详细讲解(零基础入门,老年人都看的懂)

Python数据爬取超详细讲解(零基础入门,老年人都看的懂)

python03011的博客 3699

java异常处理机制

来源:http://www.importnew.com/26613.html简单介绍:Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常异常分类:Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。Throwable又派生...

m0_37914211的博客 638

JAVA基础,集合框架(容器)

文章目录集合什么是JAVA集合Collection接口List集合ArrayList 类LinkedList类ArrayList 类和 LinkedList 类的区别Set集合HashSet 类TreeSet 类Map集合Map集合遍历Collections类排序(正向和逆向)查找、替换操作复制泛型泛型集合泛型类泛型方法泛型的高级用法1. 限制泛型可用类型2. 使用类型通配符3. 继承泛型类和实现泛型接口枚举(enum)声明枚举枚举类为枚举添加方法EnumMap 与 EnumSetEnumMap 类Enum

ghb741649543的博客 451

多个catch()异常处理顺序验证

try--catch理解: 当需要处理catch()处理异常时,没有继承关系的两个异常处理顺序随意,具有继承关系的两个异常先处理子异常,在处理父异常,任意一个catch()执行后,同级catch()均不在执行。 此时代码块 1 2可以互换,但代码块3必须在代码块2后面,此时路径有问题,报异常文件不存在; package com.ExceptionTest; import java.io.FileInputStream; import java.io.FileNotFoundException; i

m0_49665279的博客 2656

一个try可以跟进多个catch语句,用于处理不同情况,当一个try只能匹配一个catch

在Java和其他支持try-catch结构的编程语言中,一个try可以跟随多个catch语句,用于处理try块中抛出的不同异常。当try块中的代码抛出一个异常时,程序会立即跳转到与该异常类型相匹配catch块中。在这个例子中,如果try块中的代码抛出IOException或SQLException,那么对应的catch块将处理这些异常。我们可以写多个catch语句,但是不能将父类型的exception的位置写在子类型的excepiton之前,因为这样父类型肯定先于子类型被匹配,所有子类型就成为废话。

博客域名:borter.blog.csdn.net 1万+

Python异常捕获抛出-对方不想和你说话并向你抛出了一个异常

一文掌握Pyhton的异常捕获抛出,包括Python内置异常类型、自定义异常类等。

有一分热,发一分光。 7445

抛出异常捕获异常的区别

抛出异常:如果程序中出现了异常,没有办法将具体的异常打印出来,不做任何处理 捕获异常:如果程序出现了异常,就能够详细的打印是什么原因导致了异常并且能够做出相应的处理,能够显示详细的Log 如果异常出现的话,会立刻终止程序,所以我们得处理异常: 1. 该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。 2. 在方法中使用try-catch语句块来处理异常。 ...

qq_29090169的博客 1万+

python异常(概念、捕获、传递、抛出

01. 异常的概念 程序在运行时,如果Python 解释器遇到到一个错误,会停止程序的执行,并且提示一些错误信息,这就是异常 程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出(raise)异常 程序开发时,很难将所有的特殊情况都处理的面面俱到,通过异常捕获可以针对突发事件做集中的处理,从而保证程序的稳定性和健壮性 02. 捕获异常 2.1 简单的捕获异常语法 在程序开发中,...

python学习者的博客 1万+

异常】Java抛出捕获异常以及自定义异常的步骤

Java中异常部分:什么是异常,如何捕获抛出异常以及如何自定义异常

程序员好冰的博客 4188

java 捕获异常还是抛出异常

今天在看hadoop源码时,想想自己最近在做的那个系统,发现很多异常处理的方式不对,还是按照传统的异常处理方式(即:采用返回值来标识程序出现的异常情况)。而hadoop中很多方法的声明是有异常抛出的,而我的系统中的很多方法的声明都没有抛出异常。只是判断了异常情况,并输出了错误提示,但是并没有抛出异常。 org.apache.hadoop.hdfs.protocol包下的Block类的readFi...

M_Jack的博客 4154

【C++ 语言】异常 ( 抛出字符串异常 | 抛出异常对象 | 抛出任意对象 | 抛出自定义异常 )

异常处理 字符串异常 异常对象 抛出任意类型对象 自定义异常

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 7201

24.c++异常(异常抛出捕获异常的重新抛出抛出异常对象、抛出派生类对象、异常规范)

Start()return ret;

Q2691463021的博客 2133

异常捕获抛出自定义异常

catch (Throwable e) {//catch (想要捕获异常类型)捕获异常。}catch (Throwable e) {//catch (想要捕获异常类型)捕获异常。//finLLY 可以不要finally ,假设IO,资源,关闭!//finLLY 可以不要finally ,假设IO,资源,关闭!//假设这个方法中,处理不了这个异常,主动抛出异常。//toString() 异常的打印信息。//假设要捕获多个异常,从小到大。//假设要捕获多个异常,从小到大。

Anyway_01的博客 823

Scala系列20:Scala中异常捕获抛出异常

0.异常处理 来看看下面一段代码 def main(args: Array[String]): Unit = { val i = 10 / 0 println("你好!") } Exception in thread "main" java.lang.ArithmeticException: / by zero at ForDemo$.main(ForDemo.scala:3) at ForDemo.main(ForDemo.scala) 执行程序,可以看到

涤生大数据 5949

异常一定要捕获或继续向上抛出

文章目录先给结论方法签名throws Exception方法体中throw new Exception调用不捕获调用捕获启示 先给结论 异常一定要捕获或继续向上抛出吗? 答: 一,语法层面,不一定。方法签名的Exception一定要捕获或继续向上抛出,否则编译即出错(Java语法),但方法体中throw new Exception则不一定要捕获抛出。 二,良好的代码习惯 & 代码鲁棒性层面,是的,一定要捕获或继续向上抛出。 方法签名throws Exception 显然,如果一个方法签名上有异常

ProLayman的博客 4281

Java异常捕获处理和异常抛出自定义异常

目录标题一、捕获和处理异常二、异常抛出1. throw2. throws三、自定义异常1. 自定义异常概念2. 自定义异常类3. 示例 一、捕获和处理异常 try{} 语句块中放要检查的代码,有异常就会自动抛出 catch(异常类型){} 语句块接收到 try{} 中锁抛出异常对象,会寻找嫩处理这一异常catch块来进行处理(可以有多个catch块,由异常类型范围小到大排列) finally{} 不管系统有没有抛出异常语句块都会执行,一般用来解放资源。除了在之前执行了system.exit(0) 异常

one goto one 的博客 4296

c#入门-抛出异常捕获异常

一个程序可能因为各种各样的问题无法正常运行。重点是,我们要知道程序是因为什么问题而无法运行的。然后再根据问题来排除问题。一种常见的方式是给出一串错误码,然后开发人员查对照表。而在c#中,允许在程序内就解决问题。那么就不需要以字符串的形式给开发者看了。只需要直接使用类进行交互就行了。异常类继承自Exception类。如果不使用异常语法使用,那么他就是一个正常的类。可以new构造,可以赋值,可以取值。

zms9110750的博客 5145

关于抛出异常捕获异常的一些区别

小总结 抛出异常: 创建异常对象,封装异常信息然后通过throw将异常对象传递给调用者。 不对异常进行处理只对异常进行抛出是非常不负责任的表现可以称为渣男。 但是可以通过主动抛出异常对一些jvm虚拟机识别不出来的异常进行抛出。 手动抛出异常举例 public static void main(String[] args) throws Exception { int age = 0...

理直气壮鹅的博客 3738

应用74LS160实现24进制计数器

利用2片74LS160实现24进制计数器,DMU

上一篇: InputMisMatchException(输入类型不匹配异常)
下一篇: 进攻LeedCode653 . 两数之和IV-输入BST
是小陈呀~
博客等级 码龄6年 565粉丝 · 61原创
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小陈呀~

您的鼓励是我最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值