异常
-
什么是异常:
- 程序运行过程中出现的错误
-
异常总览:
-
Throwable:
- java语言中所有异常或错误的超类
- 两个子类:
- Error:错误类:是指在程序中了严重问题,不改代码,运行不了
- Exception:异常类:程序一般问题,可以通过处理规避掉
-
常见的Exception:
- NullPointerException:空指针异常
- IndexOutOfBoundsException:越界异常
- ClassCastException:类型转换错误
- RuntimeException:运行期错误
- ConcurrentModificationException:并发修改异常
-
常见的Error:
- OutOfMemoryError:内存超限:比如:int[] a = new int[1000000000];
Throwable详解:
- java语言中所有异常或错误的超类
- 所有继承自Throwable的子类(java自带),都默认使用Throwable的方法(子类基本不实现)
- 其包括如下方法:
方法 | 描述 |
---|---|
public Throwable() | 默认构造函数 |
public Throwable(String message) | 指定消息构造函数,后面该message,可通过getMessage获取 |
public Throwable(String message,Throwable cause) | 可以由message+旧的Throwable对象,构造新的Throwable |
public Throwable(Throwable cause) | 由老的构造,生成新的Throwable |
public String getMessage() | 获取message(简易的) |
public String getLocalizedMessage() | 创建本地化描述,如果不做任何子类实现,其等于getMessage |
public Throwable getCause() | 返回cause |
public Throwable initCause(Throwable cause) | 初始化cause,只能初始化一次 |
public String toString() | 详细描述(jdk文档说是简单描述) |
public void printStackTrace() | 将throwable及其追踪输出至标准输出流 |
public void printStackTrace(PrintStream s) | 将此 throwable 及其追踪输出到指定的输出流。 |
public void printStackTrace(PrintWriter s) | 将此 throwable 及其追踪输出到指定的 PrintWriter。 |
JVM对异常的处理:
- 示例,比如除0异常:
public class Test4 {
public static void main(String[] args) {
int a = 1 / 0;
}
}
- 输出:
// 异常的类,以及原因
Exception in thread "main" java.lang.ArithmeticException: / by zero
//出现异常的位置(行)
at Test3.testZero(Test3.java:27)
- 说明:
- 异常也是对象(万物皆对象),JVM发现发生了“除法的除0异常”,将异常封装为java.lang.ArithmeticException:new ArithmeticException(“/ by zero")
- 异常向外抛出,抛到main方法的调用者,即:JVM,此时JVM停止程序,并将异常打印到控制台
异常的详细程度:
- e.printStackTrace() > e.toString() > e.getMessage()
public class Test4 {
public static void main(String[] args) {
try {
div(1, 0);
} catch (Exception e) {
System.out.println("==================== e.getMessage:简要 ====================");
System.out.println(e.getMessage());
System.out.println("==================== e.toString:较详细 ====================");
System.out.println(e.toString());
System.out.println("==================== e.getMessage:非常详细 ====================");
e.printStackTrace();
}
}
private static int div(int a, int b){
return a / b;
}
}
- 输出:
==================== e.getMessage:简要 ====================
/ by zero
==================== e.toString:较详细 ====================
java.lang.ArithmeticException: / by zero
==================== e.getMessage:非常详细 ====================
java.lang.ArithmeticException: / by zero
at Test4.div(Test4.java:16)
at Test4.main(Test4.java:4)
异常的继承:
- 一般继承如下:并且,基本都使用RuntimeException
/**
* 一般继承时,都覆写空构造器、以及带有message的构造器
*/
class YRuntimeException extends RuntimeException {
public YRuntimeException() {
super();
}
public YRuntimeException(String message) {
super(message);
}
}
异常的捕获:
- 多个异常:
private void testExp() {
// 方式1:
try {
method();
} catch (NullPointerException | ArithmeticException e) {
System.out.println(e.toString());
}
// 方式2:
try {
method();
} catch (ArithmeticException e) {
System.out.println(e.toString());
} catch (NullPointerException e) {
System.out.println(e.toString());
}
}
- 如果多个异常,并且这些异常有父子关系,则父类需要写到最后,否则编译失败
class ExceptionParent extends Exception {
}
class ExceptionSon extends ExceptionParent {
}
class TestException {
public static void main(String[] args) {
try {
throw new ExceptionParent();
} catch (ExceptionParent e) {
// 这样写,编译器会报错,因为parent异常已经可以捕获了
} catch (ExceptionSon e) {
}
}
}
Error的捕获
- 因为Error的Exception是两个不同的实现类,故捕获的方式不一样:
class TestY {
public static void main(String[] args) {
try {
int[] a = new int[1000000000];
// 这里会报OutOfMemoryError,只能用Error或者Throwable捕获
} catch (Error e) {
System.out.println("捕获到异常了");
System.out.println(e.toString());
}
}
}
异常抛出:
- throw:方法内部,手动抛出异常
- throws:方法上,声明本方法抛出的异常类型,调用此方法的必须进行处理
- 运行时异常:RuntimeException或者其子类:
- 特点:运行时异常,方法上不需要throws声明,调用者也不需要处理
class XException extends Exception {
}
class YRuntimeException extends RuntimeException {
}
class TestE {
// 只需要抛出XEception即可,RuntimeException及其子类不需要抛出
public void show(int a) throws XException{
if (a == 0) {
throw new XException();
} else {
throw new YRuntimeException();
}
}
}
finally
- finally中代码快一定会执行,格式:
- try catch finally
- try finally
- 正常场景:try catch finally都有返回
class TestFinally1 {
public static void main(String[] args) {
System.out.println(show()); // 输出为3
}
private static int show() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // finally最终返回的是3
}
}
}
* 过程分析:执行到return 1;时,jvm先预留,不直接返回。等到finlly执行完成后,再返回。此时发现finally里有返回,则按照finally返回,故输出为3.
- 正常场景:finally无返回:
class TestFinally1 {
public static void main(String[] args) {
System.out.println(show1()); // 返回为1
}
private static int show1() {
try {
return 1; // 最后返回这里
} catch (Exception e) {
return 2;
} finally {
System.out.println("no return”); // 首先,会打印这里
}
}
}
输出:
no return
1
- 异常场景:无finally,最终按照catch中返回
private static int showException() {
try {
int a = 1 / 0;
return 1;
} catch (Exception e) {
return 2; // 最终方法返回的是这里
}
}
- 异常场景:有finally:则按照finally中返回
private static int showException() {
try {
int a = 1 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // 最终返回的是这里:3
}
}
注意:这里逻辑比较绕,但最好不要在finally中写return
继承关系中方法的异常处理:
- 父类方法没有异常,则子类继承的方法也不能有:
class A {
public void show() {
}
}
class B extends A {
// 正确写法:
@Override
public void show() {
}
// 错误写法:
// @Override
// public void show() throws ExceptionSon{
// }
}
- 父类方法有异常,则子类:
- 可以不写
- 或者声明为该异常的子类(异常的父类会报错)
class AException extends Exception {
}
class BException extends AException {
}
class A {
public void show() throws AException{
}
}
class B extends A {
// 正确写法1:不抛异常
// @Override
// public void show() {
// }
// 正确写法2:抛出为父类异常的子类
@Override
public void show() throws BException{
}
// 错误写法:抛出的异常比父类要高
// @Override
// public void show() throws Exception{
// }
}