Java Basics Part 19/20 - Exceptions
目录
异常(Exception) 是运行期出现错误带来的问题。当程序发生异常的时候,会不正常终止,而这不是程序员想看到,所以就引入了 异常机制,来处理运行期抛出的异常。
异常可以由很多因素导致,下面是一些常见的异常场景:
- 用户输入非法数据
- 需要打开的文件不存在
- 在通信过程中网络断开或者是 JVM 内存溢出了
一些异常由用户错误导致,还有一些有程序员导致,还有一些是物理资源导致。
基于上述原因种类不同,我们可以给异常分类:
Checked exceptions:checked exception 是在编译期间出现的异常,这些异常也叫作编译期异常。它们在编译期不能忽略,程序员应该把这些异常处理掉。
比如,如果使用 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); } } // output FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown FileReader fr = new FileReader(file);
Unchecked exceptions:Unchecked exceptions 是运行期会发生的异常,又叫做运行期异常,这些异常包括程序 bug,比如逻辑错误,API 的错误使用等等这些在编译器会忽略的异常。
public class Unchecked_Demo { public static void main(String args[]){ int num[]={1,2,3,4}; System.out.println(num[5]); } } // output Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
Errors: 这些不是异常,而是一些程序员无法控制的问题。比如,栈溢出。
Exception Hierarchy
所有的 Exception 都是 java.lang.Exception 的子类。Exception 同时也是 Throwable 的子类。除了 Exception 之外,还有 Error 类也是 Throwable 的子类。
Exception 类有两个子类:IOException 和 RuntimeException。
这里有常见的 java 内置 Exception.
Exceptions Methods
下面列出了 Throwable 类的一些很重要的方法。
SN | Methods with Description |
---|---|
1 | public String getMessage():异常发生时返回的错误信息,这个信息在异常类构造器中初始化的。 |
2 | public Throwable getCause():返回异常的原因 |
3 | public String toString():返回类名+getMessage() |
4 | public void printStackTrace():打印 toString() 的结果,输出到 System.err |
5 | public StackTraceElement [] getStackTrace():返回调用栈元素的集合 |
6 | public Throwable fillInStackTrace() |
Catching Exceptions
可以使用 try 和 catch 来捕获异常。语法如下:
try {
//Protected code
}catch(ExceptionName e1) {
//Catch block
}
举例:
// 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");
}
}
// output
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
Multiple catch Blocks
一个 try,后面可以接多个 catch:
try {
//Protected code
}catch(ExceptionType1 e1) {
//Catch block
}catch(ExceptionType2 e2) {
//Catch block
}catch(ExceptionType3 e3) {
//Catch block
}
举例:
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;
}
Catching multiple type of exceptions
从 Java 7 开始,一个 catch 语句块可以捕获多个异常。比如:
catch (IOException|FileNotFoundException ex) {
logger.log(ex);
throw ex;
The throws/throw Keywords
如果方法本身不会处理一个 checked exception,那么方法必须使用 throws 抛出这个异常。throws 关键字总是出现在方法签名的末尾。
方法中也可以使用 throw 关键字抛出一个异常。
throws 和 throw 的区别:
throws 用来推迟 checked exception 的处理,throw 用来显示的触发一个异常。
下面这个例子,抛出了一个 RemoteException:
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
方法也可以一次抛出多个异常。
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// Method implementation
}
//Remainder of class definition
}
The finally block
finally 块紧跟在 try…catch 后面。finally 块中的语句总是会执行,即使有异常发生。
通常使用 finally 语句块来做一些资源清理的工作。
语法如下:
try
{
//Protected code
}catch(ExceptionType1 e1)
{
//Catch block
}catch(ExceptionType2 e2)
{
//Catch block
}catch(ExceptionType3 e3)
{
//Catch block
}finally
{
//The finally block always executes.
}
举例:
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");
}
}
}
// output
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
注意:
- 无 try 不 catch
- finally 不必在
- 无 catch 或 finally 不 try
- try catch finally 之间无代码
The try-with-resources
通常我们会使用一些资源(流,连接等等),使用完成后要关掉这些资源,如下所示,使用 FileReader,然后关闭它:
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); // reads the content to the array
for(char c : a)
System.out.print(c); //prints the characters one by one
}catch(IOException e){
e.printStackTrace();
}
finally{
try{
fr.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
}
}
代码比较多,使用 try-with-resources 句法后(Java 7 引进),try 中打开的资源会自动释放。
语法:
try(FileReader fr=new FileReader("file path")) {
//use the resource
}catch(){
//body of catch
}
}
举例:
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); // reads the contentto the array
for(char c : a)
System.out.print(c); //prints the characters one by one
}catch(IOException e){
e.printStackTrace();
}
}
}
下面这些要点要谨记:
- 使用 try-with-resources 打开的类,必须实现 AutoCloseable接口,运行期会自动调用close()。
- 在 try-with-resources 中可用声明多个类
- try 中声明的多个类会以相反的顺序关闭
- 除了资源的声明之外,其他与 try/catch 无异
- try 中声明的资源在 try 开始之前被初始化。
- try 中声明的资源隐式的被修饰为 final 的
User-Define Exceptions
用户可以创建自己的异常类。自定义的时候注意以下几点:
- 所有的异常都是 Throwable 的子类
- 如果想写一个 checked exception,需要继承 Exception
- 如果想写一个 runtime exception,需要继承 RuntimeException
举例:
// File Name 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;
}
}
使用上述定义的异常:
// File Name 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;
}
}
使用上述定义的类:
// File Name 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();
}
}
}
// output
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)
Common Exceptions
Java 中,可以定义两种 Exceptions 和 Errors:
- JVM Exceptions: 由 JVM 抛出的异常,例如:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException
- Programmatic exceptions: 应用或者是API设计者抛出的异常,例如:IllegalArgumentException,IllegalStateException