Java快速入门(26) - 异常与异常处理

it2023-03-22  71

文章目录

异常与异常处理异常分类检查型异常程序实例 非检查型异常程序实例 错误异常层次异常类中的方法捕获异常程序实例 多个catch语句块程序实例 捕获多种类型的异常throws和throw关键字finally块程序实例注意事项 try-with-resources语法注意事项 用户自定义异常程序实例 常见异常 关注公众号「小白轻松学编程」

异常与异常处理

异常或异常事件指的是程序在执行中出现的问题。当异常发生时程序正常执行流程就会被打断,程序或应用非正常终止。这种情况不是我们想要的,因此,程序中处理异常非常有必要。 异常可以由很多原因引起,下面列出了几种会出现异常的场景:

用户输入了无效的数据打开文件时却找不到文件网络连接断开或JVM内存溢出。

异常分类

异常有些是由用户错误的行为引起的,有些是因为程序员失误,甚至有些是因为物理故障导致的。 基于此,我们可以将异常分为三类,掌握它们可以帮助我们理解Java是如何处理异常的。

检查型异常(Checked exceptions)非检查型异常(Unchecked exceptions)错误(Error)

检查型异常

检查异常指的是编译器在编译代码时提示的异常,其也被称为编译时异常。这类异常不能被简单地忽视,程序员应该在程序编译后处理完这些异常。 举个例子,如果你在程序里使用FileReader类来从一个文件中读取数据时,如果构造器中传入的文件在对应路径中不存在,那么就发生了文件找不到异常(FileNotFoundException),编译器就会提示程序员去处理这个异常。

程序实例

import java.io.File; import java.io.FileReader; public class FilenotFound_Demo { public static void main(String args[]) { File file = new File("E://file.txt"); FileReader fr = new FileReader(file); } }

当我们运行上面的程序时,会提示出现异常:

C:\>javac FilenotFound_Demo.java FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown FileReader fr = new FileReader(file); ^ 1 error

需要注意:由于FileReader类中的read()和close()方法会抛出IOException,因此我们会看到,编译器在抛出FileNotFoundException的同时也会抛出IOException。

非检查型异常

非检查异常指的是在执行程序时出现的异常,也被称为运行时异常。其中包括编程错误,例如逻辑错误或API使用不当。在编译时,运行时异常是被忽略的。 举个例子,如果你在程序中声明了大小为5的数组,并尝试读取数组中第6个元素,那么就会产生一个ArrayIndexOutOfBoundsException异常。

程序实例

public class Unchecked_Demo { public static void main(String args[]) { int num[] = {1, 2, 3, 4}; System.out.println(num[5]); } }

当我们运行上面的程序时,会提示出现异常:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)

错误

错误(Errors)不是异常,而是超出用户或程序员可控范围的问题。错误通常不会在代码中进行处理,因为我们几乎没法对错误做不了什么。比如说,当出现栈溢出的时候,错误就发生了。程序在编译的时候也会忽略错误。

异常层次

所有的异常类都是java.lang.Exception类的子类。而Exception类是Throwable类的子类。除了异常类之外,还有另一个名为Error的子类,它是从Throwable类派生的。 错误是发生严重故障时发生的异常情况,不会由Java程序处理。运行环境产生错误时生成错误信息。例如,JVM内存溢出。一般来说,程序无法从错误中恢复。 Exception类有两个比较重要的子类:IOException类和RuntimeException类。

– 下面是最常见的检查型(checked)和非检查型(unchecked)的Java内置异常列表。

异常类中的方法

下表列出的是Throwable类中比较重要的几个方法。

序号方法&描述1public String getMessage()返回异常的详细信息。2public Throwable getCause()返回异常的异常的起因。3public String toString()返回异常类和getMessage()方法返回的内容。4public void printStackTrace()打印toString()方法的内容以及跟踪到堆栈的错误输出流。5public StackTraceElement [] getStackTrace()返回一个包含堆栈跟踪的每个元素的数组。6public Throwable fillInStackTrace() 用当前堆栈跟踪填充此Throwable对象的堆栈跟踪,并添加到堆栈跟踪中的所有先前信息。

捕获异常

一种捕获异常的方式是结合使用try和catch关键字。try/catch语句将可能产生异常的代码包围起来。try/catch语句中包含的代码可以认为是被保护的代码,使用try/catch语句的语法如下:

try { //被保护的代码块 } catch (ExceptionName e1) { //catch块 }

容易出异常的代码放在try语句块中,当发生异常时,异常由catch语句块来进行处理。每个try语句块后面应该马上跟上catch语句块或finally语句块。 catch语句块包含声明需要捕获的异常的类型。如果在被保护的代码中发生异常时,程序就会检查catch语句块中指定的异常的类型是否和出现的异常一致。如果一致,那么就会执行catch语句块中的代码,相应的异常对象就和方法中的参数一样被传入catch语句块中。

程序实例

下面程序声明了大小为2的数组,而后面的代码尝试访问数组的第3个元素,这会导致出现异常。

// File Name : ExcepTest.java import java.io.*; public class ExcepTest { public static void main(String args[]) { try { int a[] = new int[2]; System.out.println("Access element three :" + a[3]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Exception thrown :" + e); } System.out.println("Out of the block"); } }

程序输出为:

Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 Out of the block

多个catch语句块

一个try语句后面可以跟多个catch语句块,多个catch语句的语法如下:

try { //被保护的代码块 } catch (ExceptionType1 e1) { //catch块 } catch (ExceptionType2 e2) { //catch块 } catch (ExceptionType3 e3) { //catch块 }

上面语法结构中只使用了3个catch语句块,但实际上我们可以在1个try语句后面跟任意个catch语句。如果try中被保护的代码出现了异常,那么异常首先被抛给第一个catch块。如果异常的数据类型和第一个catch块中定义的异常类型(ExceptionType1)相符合,那么异常就被第一个catch块捕获处理;否则,异常继续抛给第二个catch块判断处理。这个过程会持续到异常被catch语句捕获或者不被处理,即没有catch语句符合异常的数据类型。当异常没有被捕获时,当前方法停止执行并将异常抛给调用该方法的方法。

程序实例

下面代码片段展示了如何使用多个try/catch语句。

try { file = new FileInputStream(fileName); x = (byte) file.read(); } catch (IOException i) { i.printStackTrace(); return -1; } catch (FileNotFoundException f) // Not valid! { f.printStackTrace(); return -1; }

捕获多种类型的异常

从Java 7以后,我们可以使用一个catch语句处理多个类型的异常,这个特性可以简化代码。下面是语法结构:

catch (IOException|FileNotFoundException ex) { logger.log(ex); throw ex;

throws和throw关键字

如果方法里没有处理检查异常,方法就必须使用throws关键字来声明异常。throws关键字放在方法声明的最后面。 我们可以使用throw关键字抛出异常,可以是新实例化的异常,也可以是刚捕获的异常。 来试着理解一下throws和throw关键字的不同之处,throws用于推迟对已检查异常的处理,throw用于显式调用异常。 下面的方法定义时声明了其会抛出一个RemoteException异常。

import java.io.*; public class className { public void deposit(double amount) throws RemoteException { //抛出异常 throw new RemoteException(); } }

方法可以抛出多个异常,只需将多个异常类用逗号分隔即可。例如,下面的方法声明了其会抛出RemoteException和InsufficientFundsException:

import java.io.*; public class className { public void withdraw(double amount) throws RemoteException, InsufficientFundsException { //方法体 } //其它代码 }

finally块

finally代码块跟在try或catch块后面。不管有没有发生异常,finally块中的代码总是会被执行。 无论受保护的代码发生什么情况,我们都可以使用finally块来执行任何清理类型的语句。 在catch语句块后使用finally块的语法如下:

try { //受保护代码 } catch (ExceptionType1 e1) { //catch块 } catch (ExceptionType2 e2) { //catch块 } catch (ExceptionType3 e3) { //catch块 } finally { //finally代码块始终会被执行 }

程序实例

public class ExcepTest { public static void main(String args[]) { int a[] = new int[2]; try { System.out.println("Access element three :" + a[3]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Exception thrown :" + e); }finally { a[0] = 6; System.out.println("First element value: " + a[0]); System.out.println("The finally statement is executed"); } } }

程序输出为:

Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 First element value: 6 The finally statement is executed

注意事项

catch语句不能在没有try语句的时候使用每当出现try/catch块时,不是一定要有finally语句。try语句不能单独出现,必须跟上catch语句或finally语句。try,catch和finally语句块中间不能出现其它代码。

try-with-resources

通常情况下,我们使用流(stream)、连接(connection)等访问资源时,要用finally语句块来将关闭资源。在下面的程序中,我们使用了FileReader类来从一个文件中读取数据,接着在finally块中关闭文件。

import java.io.File; import java.io.FileReader; import java.io.IOException; public class ReadData_Demo { public static void main(String args[]) { FileReader fr = null; try { File file = new File("file.txt"); fr = new FileReader(file); char [] a = new char[50]; fr.read(a); //读取数组内容 for(char c : a) System.out.print(c); //一个个打印字符 } catch (IOException e) { e.printStackTrace(); }finally { try { fr.close(); } catch (IOException ex) { ex.printStackTrace(); } } } }

try-with-resources,也被称为自动资源管理(automatic resource management),是Java 7中引入的一种新的处理异常机制,这个机制能够在使用try/catch语句时自动关闭资源。 使用这种带资源的try语句时,我们只需要简单地在括号中定义资源,创建的资源就会在try语句块结束的时候自动关闭。下面是try-with-resources语句的语法。

语法

try(FileReader fr = new FileReader("file path")) { //使用资源 } catch () { //catch块 } }

下面程序中使用了try-with-resources从文件中读取数据。

import java.io.FileReader; import java.io.IOException; public class Try_withDemo { public static void main(String args[]) { try (FileReader fr = new FileReader("E://file.txt")) { char [] a = new char[50]; fr.read(a); //将内容读取到数组 for(char c : a) System.out.print(c); //一个个打印字符 } catch (IOException e) { e.printStackTrace(); } } }

注意事项

我们在使用try-with-resources语句时,下面几点请牢记在心:

try后面跟的括号里用来创建对象的类必须实现AutoCloseable接口,并且程序会自动调用类中的close()方法。可以在try中声明多个类的对象。当我们在try-with-resources语句的try块中声明多个类的对象时,这些类的对象以和声明相反的顺序关闭。除了try后面括号里声明的资源外,其它部分和普通的try/catch语句一样。try中声明的资源会在程序执行try语句块中的内容之前被初始化。在try括号中声明的资源会被隐式地声明为final类型。

用户自定义异常

我们可以在Java中自行定义异常。在定义自己的异常类时,记住下面几点:

所有的异常类都必须是Throwable类的子类。如果要编写检查型异常,需要继承Exception类。如果要编写运行时异常,需要继承RuntimeException类。

我们可以按下面这个语法格式来定义自己的异常类:

class MyException extends Exception { }

如上面代码所示,我们只需要继承预定义的Exception类就可以创建自己的异常类。这样的话,创建的是检查型异常。例如,下面程序中的InsufficientFundsException类是一个继承了Exception类的用户自定义异常类(检查型异常)。 异常类和其它类一样,都包含有用的变量和方法。

程序实例

//文件名:InsufficientFundsException.java import java.io.*; public class InsufficientFundsException extends Exception { private double amount; public InsufficientFundsException(double amount) { this.amount = amount; } public double getAmount() { return amount; } }

下面代码更详细地阐述用户自定义异常类,其中CheckingAccount类包含的withdraw()方法抛出了一个InsufficientFundsException。

//文件名:CheckingAccount.java import java.io.*; public class CheckingAccount { private double balance; private int number; public CheckingAccount(int number) { this.number = number; } public void deposit(double amount) { balance += amount; } public void withdraw(double amount) throws InsufficientFundsException { if(amount <= balance) { balance -= amount; }else { double needs = amount - balance; throw new InsufficientFundsException(needs); } } public double getBalance() { return balance; } public int getNumber() { return number; } }

下面的BankDemo程序描述了调用CheckingAccount类的desposit()和withdraw()方法。

//文件名:BankDemo.java public class BankDemo { public static void main(String [] args) { CheckingAccount c = new CheckingAccount(101); System.out.println("Depositing $500..."); c.deposit(500.00); try { System.out.println("\nWithdrawing $100..."); c.withdraw(100.00); System.out.println("\nWithdrawing $600..."); c.withdraw(600.00); } catch (InsufficientFundsException e) { System.out.println("Sorry, but you are short $" + e.getAmount()); e.printStackTrace(); } } }

将上面3个代码文件编译后,运行BankDemo,输出如下:

Depositing $500... Withdrawing $100... Withdrawing $600... Sorry, but you are short $200.0 InsufficientFundsException at CheckingAccount.withdraw(CheckingAccount.java:25) at BankDemo.main(BankDemo.java:13)

常见异常

在Java里,可以将异常和错误定义两个类别:

JVM异常 - 这类异常/错误是被JVM专门或逻辑抛出的。例如,NullPointerException,ArrayIndexoutOfBoundsException,ClassCastException。程序异常 - 这一类异常由应用程序或API开发人员明确抛出。例如,IllegalArgumentException,IllegalStateException。

关注公众号「小白轻松学编程」

更多交流,欢迎微信搜索并关注公众号小白轻松学编程

博客里所有教程会第一时间在公众号上更新哟,扫码关注一下吧~

最新回复(0)