异常处理

抛出异常

异常处理使得程序可以处理非预期的情景,并且能够继续正常的操作

在java中,运行时错误会作为异常抛出。异常就是一种对象,表示阻止正常进行程序执行的错误或者情况。

异常是从方法抛出的。方法的调用者可以捕获以及处理该异常。
我们可以先从简单的算术异常错误来了解抛出异常的作用

import java.util.Scanner;
public class Quotient {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        System.out.println("Enter two integers: ");
        int number1=input.nextInt();
        int number2=input.nextInt();
        System.out.println(number1+" / "+number2+" is "+(number1/number2));
    }
}
第二个整数为0时,产生错误

当第二个数字输入的是数字0的时候,就会产生一个运行时错误,因为不能用一个整数除以一个0(但是在java中浮点型除以0将不会抛出异常)
要想解决这个错误,一个简单的方法就是添加一个if语句来测试第二个数字。

import java.util.Scanner;
public class Quotient {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        System.out.println("Enter two integers: ");
        int number1=input.nextInt();
        int number2=input.nextInt();
        if(number2!=0)
        System.out.println(number1+" / "+number2+" is "+(number1/number2));
        else
            System.out.println("Divisor cannot be zero");
    }
}
第二个整数为0时,没有错误了

为了介绍异常处理,重新再写一个方法来计算商。

import java.util.Scanner;
public class QuotienWithMethod {
    public static int quotient(int number1,int number2){
        if(number2==0){
            System.out.println("Divisor cannot be zero");
            System.exit(1);//表示程序非正常退出
        }
        return number1/number2;
    }
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int number1=input.nextInt();
        int number2=input.nextInt();
        
        int result=quotient(number1, number2);
        System.out.println(number1+" / "+number2+" is "+result);
    }
}
没有弹出错误

但是这种方法是强行结束掉整个程序,而且不应该是被调用的方法来结束整个程序,而是应该由调用者来决定是否终止程序。所以要用到异常处理。

import java.util.Scanner;
public class QuotientWithException {
    public static int quotient(int number1,int number2){
        if(number2==0)
            throw new ArithmeticException("Divisor cannot be zero");
        return number1/number2;
    }
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        System.out.println("Enter two integers: ");
        int number1=input.nextInt();
        int number2=input.nextInt();
        try{
            int result=quotient(number1,number2);
            System.out.println(number1+" / "+number2+" is "+result);
        }
        catch(ArithmeticException ex){
            System.out.println("Exception: an integer"+"cannot be divided by zero");
        }
        System.out.println("Execution continues...");
    }
}
抛出异常之后程序未终止

如果number2为0,方法就会通过执行下面的语句来抛出异常

throw new ArithmeticException("Divisor cannot be zero");

在这种情况下,抛出的值为new ArithmeticException("Divisor cannot be zero"),就成为一个异常(exception)。throw语句的执行称为抛出一个异常(throwing an exception)。异常就是一个从异常类创建的对象。在这种情况下,异常类就是java.lang.ArithmeticException.构造方法ArithmeticException(str)被调用来构建一个异常,其中str就是用来描述异常的信息。
当异常被抛出的时候后,正常的执行流程会被中断。异常会从一个地方传递到另一个地方。调用方法的语句包含在一个try块和一个catch块中。try块包含了正常情况之下执行的代码。异常会被catch块捕获。catch块中的语句用来处理异常,然后执行catch块之后的语句(相当于是throw语句调用了catch块,但是catch块执行完毕之后不会返回到throw语句之后,而是直接运行catch块之后的语句)。

catch块的头部:

catch (ArithmeticException ex)

标识符ex的作用很像是方法中的参数。所以,这个参数称为catch块的参数。ex之前的类型(例如,ArithmeticException)指定了catch块可以捕获的异常类型。一旦捕获该异常,就能从catch块体中的参数方访问这个抛出的值。
总之,一个try-throw-catch块的模板可能会如下所示:

try{
    Code to run;
    A statement or a method that may throw an exception;
    More code to run;
}
catch(type ex){
    Code to process the exception;
} 

一个异常可能是通过try块中的throw语句直接抛出,或者调用一个可能会抛出异常的方法二抛出。
什么是抛出异常的优点?

异常处理可以使方法抛出一个异常给它的调用者,并由调用者处理该异常。如果没有这个能力,那么被调用的方法就必须自己处理异常或者终止改程序。被调用的方法通常不知道在出错的情况下该做一些什么,这是库方法的一般情况。库方法可以检测出错误,但是只有调用者才知道出现错误的时候需要做什么。异常处理最根本的优势就是将检测错误(由被调用的方法完成)从处理错误(由调用方法完成)中分离出来。

异常是对象,而对象都采用类来定义。异常的根类是java.lang.Throwable。

Throwable类是所有异常类的根。所有的java异常类都直接或者间接地继承自Throwable类。我们可以通过继承Exception或者Exception的子类来创建自己的异常类。
异常类可以分为三种类型:系统错误、 异常、 运行时异常

  • 系统错误(system error):是由java虚拟机抛出的,用error类表示。Error类描述的是系统内部错误。
  • 异常(Exception):是用Exception类表示的,他描述的是由程序和外部环境所引起的错误,这些错误可以能被程序捕获和处理。
  • 运行时错误(runtime exception)是用RuntimeException类表示的,它描述的是程序设计错误。
    RuntimeException、Error以及它们的子类都称为免检异常(unchecked exception),而其他所有异常都称为必检异常(checked exception)因为免检异常可能出现在程序的任意一个地方,为了避免过多地使用try-catch块,所以免检异常不作强制要求。

Java的异常处理模基于三种操作:声明一个异常、抛出一个异常、捕获一个异常。

声明异常:在Java中,当前执行的语句必属于某个方法。Java解释器调用main方法开始执行一个程序。每个方法都必须声明它可能抛出的必检异常类型。

public void myMethod() throws IOException

抛出异常:检测到错误的程序可以创建一个合适的异常类型的实例并抛出它,这就成为抛出一个异常。

IllegalArgumentException ex=new IllegalArgumentException("Wrong Argument");
throw ex;

或者用下面这种方法

throw new IllegalArgumentException("Wrong Argument");

每个异常类至少有两个构造方法:一个无参构造方法和一个带可描述这个异常的String参数的构造函数。这个String参数称为异常消息,可以用getMessage();获取。
声明异常的关键字是throws,抛出异常的关键字是throw。
捕获异常:当抛出一个异常时,可以在try-catch块中捕获和处理它。

try{
    statements;
}
catch (Exception ex){
    handler for exception;
}

如果在执行try块中代码的过程中没有出现异常,则会跳过catch语句。如果try块中的某条语句抛出一个异常,java会跳过try块中剩下的语句。
从一个通用的父类可以派生出各种异常类。如果一个catch块可以捕获一个父类的异常对象,它就能捕获那个父类的所有子类的异常对象。而且如果父类的catch块出现在子类的catch块漆面,会导致编译错误。
Java要求程序员必须处理必检异常。如果方法声明了一个必检异常,就必须在try-catch块中调用它,或在调用方法中声明要抛出异常。
要是需要一个catch块捕获多个异常可以使用"|"来将每个异常类型隔开。
从异常中获取信息


public class TestException {
    public static void main(String[] args) {
        try{
            System.out.println(sum(new int[]{1,2,3,4,5}));
        }
            catch (Exception ex){
                ex.printStackTrace();
                System.out.println("\n"+ex.getMessage());
                System.out.println("\n"+ex.toString());
                
                System.out.println("\nTrace Info Obtained from getStackTrace");
                StackTraceElement[] traceElements=ex.getStackTrace();
                for(int i=0;i<traceElements.length;i++){
                    System.out.println("method"+traceElements[i].getMethodName());
                    System.out.println("("+traceElements[i].getClassName()+":");
                    System.out.println(traceElements[i].getLineNumber()+")");
                }
            }
        }
    private static int sum(int [] list){
        int result=0;
        for(int i=0;i<=list.length;i++)
            result+=list[i];
            return result;
    }
}

在代码中使用了printStackTrace();、getMessage();、toString();三种方法来显示栈跟踪、异常信息、异常对象和信息。

运行结果:

printStackTrace();显示栈跟踪
getMessage();显示异常信息
toString();显示异常对象和信息
getStackTrace();显示调用的方法

finally子句

有时候,不论异常是否会出现或者被捕获,都希望执行某一些代码,这个时候可以使用finally子句来达到这个目的。

try{
    statements;
}
catch(TheException ex){
    handling ex;
}
finally{
    finalStatements;
}

注意:使用finally子句时可以省略掉catch块。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代...
    Executing阅读 1,313评论 0 6
  • 初识异常(Exception) 比如我们在取数组里面的某个值得时候,经常会出现定义的取值范围超过了数组的大小,那么...
    iDaniel阅读 1,863评论 1 2
  • Java异常类型 所有异常类型都是Throwable的子类,Throwable把异常分成两个不同分支的子类Erro...
    予别她阅读 915评论 0 2
  • __________观音山有感 观音山,一直没看,单看名字,不太喜欢。 昨天不经意间点开了,画面开始,范冰冰在化妆...
    米酒黑布林阅读 392评论 1 0
  • 木本植物是什么呢? 这里元宝枫小编来解释一下,哦,对了,元宝枫也是一种木本植物哦! 木本植物是是指根和茎因增粗生长...
    九尾猫猫阅读 3,977评论 0 1