作者dasea (植栽鸡肉饭)
看板ncyu_phyedu
标题[讨论] 例外处理
时间Sat Jan 30 14:58:25 2010
简介
所谓例外(Exception),指的是程式发生不正常的错误,而导致无法继续执行的情形。例外
处理(Exception Handling)顾名思义,就是当例外发生时的处理机制。
C语言里并没有例外处理的机制,使用函数库时,可能会发生无法处理的状况,此时必须由呼
叫者小心检查传回值才行。如果不检查,就会发生无法预期的结果
#include <stdio.h>
main() {
int data;
FILE* f = fopen("sample.txt", "r"); // sample.txt may not exist, fopen
returns NULL
fscanf(f, "%d", &data); // may result segmentation fault;
}
上述例子里,如果档案sample.txt不存在,则无法开启该档案,因此fopen传回NULL。如果不
检查传回值,则会产生segmentation fault。比较好的写法是:
#include <stdio.h>
main() {
int data;
FILE* f = fopen("sample.txt", "r"); // sample.txt may not exist, fopen
returns NULL
if (f == NULL) {
printf("Can't open file sample.txt. Please check if it exists and you
have privilege to access.\n");
return;
}
fscanf(f, "%d", &data); // may result segmentation fault;
}
又例如sqrt()可以用来求平方根,但如果我们给他负数,sqrt该如何处理?根据手册,sqrt遇
到负数参数,会传回NaN(Not a Number,是浮点数里的一个特别数值)。使用sqrt的函数必
须特别检查该值,否则计算出来的东西都变成了NaN。又例如除法运算时分母是0的情况,严
格说起来也是一种错误,某些系统也会产生floating exception (core dumped)。
没有提供例外处理机制的语言,程式的正确性必须靠极端小心的设计者才行。为了减少程
式错误的机会,让软体很强固(robust),Java提供了例外处理的机制。
相关语法
在Java里,Exception是一个Class。Exception extends Throwable, Throwable extends
Object。Exception,Throwable这两个类别均定义於java.lang这个package内。设计者也
可以自订自己的Exception类别。相关的Exception语法如下:
自订Exception类别
public class MyException extends Exception {
}
宣告某method会产生例外
import java.io.*;
public class ExceptionExample {
public void someMethod() throws Exception { // 请注意throws最後面是s
// some code may fail
FileInputStream f;
try {
f = new FileInputStream("abc.txt"); // if abc.txt does not exist,
FileNotFoundException will be caught
} catch(FileNotFoundException fnf) {
System.out.println("File not found. Generate an exception and
throw it");
throw fnf; // 注意throw後面没有s
// throw new Exception(); // or you can throw a new Exception
object
}
}
public static void main(String[] argv) {
ExceptionExample s = new ExceptionExample();
try {
s.someMethod();
} catch(Exception epp) {
System.out.println("An Exception has been caught.");
}
}
}
拦截exception的语法
try {
} catch (TypeOneException e1) {
} catch (TypeTwoException e2) {
} catch (TypeThreeException e3) {
} finally {
}
try {} catch{}类似像if then else if的结构。当try {}里面某一行指令产生Exception
时,try区块会立刻中断执行,然後到第一个catch判断抓到的Exception是否instanceof
TypeOneException,如果是则执行该catch区块;如果不是,则进一步比较instanceof
TypeTwoException。也就是说虽然可以写很多个catch区块,但执行时最多只有一块会执行
到。离开try或catch区块以前,如果有finally区块,则finally区块一定会被执行到。一般
来说finally区块里面的程式码大多用来作资源回收,或清理资料结构的工作,以确保不论
有无发生状况,程式都能继续正常执行。
try {
System.out.println("Opening FileInputStream");
FileInputStream f = new FileInputStream("abc.txt"); // assume this
operation generate FileNotFoundException
System.out.println("File Opened");
} catch (FileNotFoundException e1) {
System.out.println("FileNotFoundException caught");
} catch (Exception e2) {
System.out.println("Exception caught");
} finally {
System.out.println("Execute finally block");
}
上述的范例会印出
Opening FileInputStream
FileNotFoundException caught
Execute finally block
如果把上面例子稍微改一下:
try {
System.out.println("Opening FileInputStream");
FileInputStream f = new FileInputStream("abc.txt"); // assume this
operation generate FileNotFoundException
System.out.println("File Opened");
} catch (Exception e2) {
System.out.println("Exception caught");
} catch (FileNotFoundException e1) {
System.out.println("FileNotFoundException caught");
} finally {
System.out.println("Execute finally block");
}
则聪明一点的Compiler会抱怨FileNotFoundException的区块unreachable(执行不到)。这
是因为FileNotFoundException是Exception的子类别,因此如果产生的例外是instanceof
Exception,则FileNotFoundException就不会执行了;若产生的例外不是instanceof
Exception,那就更不会执行到FileNotFoundException区块了。
是否所有的Exception都要处理?
原则上是的。只要用到的method有宣告throws SomeException,则呼叫该method的地方,就
要使用try {} catch(SomeException)的语法。当然像是try {}
catch(SuperClassOfSomeException)的用法也行。唯一的例外是
java.lang.RuntimeException及其子类别可以不必处理。
哪些属於RuntimeException呢?像是ArrayIndexOutOfBoundException就是其中之一,它发
生在阵列索引不合法的情况下:
public class Test {
public static void main(String[] argv) {
int[] x = new int[10];
x[100] = 0; // will generate ArrayIndexOutOfBoundException
}
}
exception产生时,JVM会由堆叠追踪此错误点的呼叫资讯,并一一向外检查,直到有try
catch区块拦截此exception为止。在上述的例子中,没有任何try catch的宣告,则JVM会终
止该执行绪。
类别Exception的相关方法
抓到例外後,可透过该例外物件,得到有趣的资讯
toString()以简单的字串描述该例外
getMessage()列出细节讯息
printStackTrace()将堆叠资讯印在萤幕上,可帮助设计者快速找到错误点
Error
前面提到Exception是Throwable的子类别。另一个Throwable的子类别是java.lang.Error
。所谓Error指的是严重的错误情况。当Error产生时,其行为和Exceptio类似,但是try
catch区块没有办法拦下它们,最後会由JVM来处理Error,并中断执行绪的执行。像
OutOfMemoryError,StackOverflowError都是Error的子类别。
范例
用Link List实作Stack
public class Stack {
private Node head;
private int size;
class Node {
Object data;
Node next;
}
public void push(Object s) {
Node tmp = new Node();
tmp.next = head;
tmp.data = s;
size++;
head = tmp;
}
public Object pop() throws Exception {
if (head == null) {
throw new Exception();
}
Object tmp = head.data;
head = head.next;
size--;
return tmp;
}
}
public class Example {
public static void main(String[] argv) {
Stack s1 = new Stack();
Stack s2 = new Stack();
s1.push("abc");
s1.push("def");
s2.push("123");
s2.push("456");
try {
s1.pop();
} catch(Exception e) {}
}
}
public class Example2 {
public static void main(String[] argv) throws Exception {
Stack s1 = new Stack();
Stack s2 = new Stack();
s1.push("abc");
s1.push("def");
s2.push("123");
s2.push("456");
s1.pop();
}
}
用Link List实作Queue
public class Queue {
private Node head, tail;
private int size;
class Node {
Object data;
Node next;
}
public void put(Object s) {
Node tmp = new Node();
tmp.data = s;
if (tail != null) {
tail.next = tmp;
} else {
head = tmp;
}
tail = tmp;
size++;
}
public Object get() throws Exception {
if (head == null) {
throw new Exception();
}
Object tmp = head.data;
head = head.next;
if (head == null) {
tail = null;
}
size--;
return tmp;
}
}
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 61.58.22.74