简介
我们在编写代码的时候,有时候IDE会给出以下提示:
未处理的异常:java.lang.FileNotFoundException.
点击提示后,一般有这两个选项:
1.在方法签名添加exception.
2.使用try/catch包围 .
使用方法1处理后:
public class BasicException {
public static void main(String[] args) throws FileNotFoundException {
ObjectInputStream ois = new ObjectInputStream
(new FileInputStream(new File("f:\\")));
}
}
其实还需要处理IOException.
方法2:
public static void main(String[] args) {
try {
ObjectInputStream ois = new ObjectInputStream
(new FileInputStream(new File("f:\\")));
} catch (IOException e) {
e.printStackTrace();
}
}
直接处理IOException.
以上就是两种异常处理的方式.
还有Error,系统不会提示你处理Error:
public static void main(String[] args) {
int[] arr = new int[Integer.MAX_VALUE];
System.out.println(arr[arr.length - 1]);
}
运行后:
Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at exception.BasicException.main(BasicException.java:5)
"main"线程中出现了java.lang.OutOfMemoryError: 所请求的数组大小超过了虚拟机限制.
异常处理可以让程序变得更加健壮,
Throwable,Error与Exception
上面的FileNotFoundException有一个父类叫IOException,而IOException的父类是Exception.OutOfMemoryError的父类是VirtualMachineError,VirtualMachineError的父类就是Error,而Exception与Error共同的父类叫做Throwable.
Java拥有相当完善的异常处理系统.Throwable作为顶级父类,下面有Exception与Error两个子类,两个子类又有若干子类.这里主要说说Exception.
Error主要是导致系统崩溃的问题,比如内存溢出错误,会直接导致程序停止运行,而且我们不能捕获它们.而Exception往往就是代码的问题,我们可以对其进行处理,比如捕获或者抛出.
异常分为检查型异常和非检查型异常,非检查型异常又叫做运行时异常,有一个单独的类:RuntimeException,这时所有运行时异常的父类.
上面所提到的FileNotFoundException就是一种检查型异常,需要在代码中进行捕获(try/catch)或抛出(throws).而RuntimeException我们不需要在代码中进行处理.运行时异常只有在运行时才出现,比如广大程序员的噩梦:NullPointerException(空指针异常).
使用IntelliJ IDEA寻找Exception的子类,一共找到893个(JDK 1.8),Error共有51个子类,可以说已经把绝大多数的情况包含进去了,但是有的时候我们需要捕获的异常不在这些里面,我们也可以自定义异常,只需继承Exception类就可以.
throw与throws
两个都有抛出的意思,但是应用场景不同.
throws一般在方法定义中,用来通知调用者需要进行处理,比如:
public static void main(String[] args) throws IOException {}
throws也是有讲究的,:你调用的方法throws了什么异常你就需要throws什么异常.而在子类重写父类方法的关系下,情况还更复杂,下面把各种情况都介绍一下:
1.与父类抛出相同的异常:
class Task {
void method () throws FileNotFoundException, InterruptedException {}
}
class Test extends Task{
@Override
void method() throws FileNotFoundException, InterruptedException {}
}
2.抛出部分异常 :
class Task {
void method () throws FileNotFoundException, InterruptedException {}
}
class Test extends Task{
@Override
void method() throws InterruptedException {}
}
3.也可以抛出父类异常的子类:
class Task {
void method () throws IOException {}
}
class Test extends Task{
@Override
void method() throws FileNotFoundException {}
}
4.不可以是父类异常的父类,不能比父类多:
class Task {
void method1 () throws RuntimeException {}
void method2() throws FileNotFoundException {}
}
class Test extends Task{
@Override
void method1() throws Exception {} //不能是父类异常的父类
@Override
void method2() throws FileNotFoundException, EOFException{} //不能比父类异常多
}
而throw则用于方法体中,将异常抛出给调用者:
public static void main(String[] args) throws IOException {
exception();
}
private static void exception() throws IOException {
throw new IOException();
}
也可以创建一个异常对象,直接抛出:
private static void exception() throws IOException {
IOException e = new IOException();
throw e;
}
需要注意的一点是,如果throw的是非检查型异常,则不需要给调用者处理,检查型异常则必须抛出给调用者处理(上面的情况):
public static void main(String[] args) {
exception();
}
private static void exception() {
throw new RuntimeException(); //非检查型异常,不需要抛给调用者.
}
try/catch/finally
在try中处理可能发生检查型异常的代码(如IOException),在catch中进行捕获,并编写处理异常的代码,finally无论是否发生异常都会执行得到,一般用来释放资源,比如关闭流:
public static void main(String[] args){
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream(new File("F:\\")));
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
printStackTrace()与 getStackTrace()是两种常见的处理方式,printStackTrace()是通过System.err输出错误调用栈:
public class BasicException {
public static void main(String[] args) {
C c = new C();
try {
c.catchB();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class A{
void exception() throws IOException {
throw new IOException();
}
}
class B extends A{
void catchA() throws IOException {
exception();
}
}
class C extends B{
void catchB() throws IOException {
catchA();
}
}
运行结果:
java.io.IOException
at exception.A.exception(BasicException.java:18)
at exception.B.catchA(BasicException.java:24)
at exception.C.catchB(BasicException.java:30)
at exception.BasicException.main(BasicException.java:9)
getStackTrace()是获取信息,但是不输出,将上面catch中的方法替换为e.getStackTrace()之后,不会输出任何信息.
catch块可以有多个,异常的细分程度从上到下依次降低,这里只是举一个例子,只要有其中一个异常被捕获,就结束执行try块.
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
try块是必选的,其余两个块至少应该有一种.即拥有如下三种形式:
1.
try {
} catch(Exception e) {
} finally {
}
2.
try {
} catch(Exception e) {
}
3. try {
} finally {
}
自定义异常
如果想要编写我们自定义的异常,只需继承Exception类即可:
public class BasicException {
public static void main(String[] args) throws MyException {
throw new MyException();
}
}
class MyException extends Exception{}
Exception类中有多个构造器,主要是定义抛出异常时显示的消息,在抛出异常对象或捕获的时候,可以利用super(消息)来自定义抛出异常时显示的信息:
public class BasicException {
public static void main(String[] args) throws MyException {
new ThrowException().throwException();
}
}
class MyException extends Exception{
MyException(int number){
super("自定义异常:" + number);
}
}
class ThrowException{
void throwException() throws MyException {
throw new MyException(1);
}
}
运行结果
Exception in thread "main" exception.MyException: 自定义异常:1
......