一、异常分类
异常类层次结构图
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。
Error(错误): 是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如 Java 虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java 中,错误通过 Error 的子类描述。
Exception(异常): 是程序本身可以处理的异常。
Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分 别 引 发 运 行 时 异 常 NullPointerException 、ArithmeticException 和 ArrayIndexOutOfBoundException。
注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。
二、抛出异常
- 异常举例
public class Test2 {
public static void CMDCalculate(){
Scanner scan = new Scanner(System.in);
int num1 = scan.nextInt();
int num2 = scan.nextInt();
int result = devide(num1,num2);
System.out.println("result:"+result);
scan.close();
}
public static int devide(int num1,int num2){
return num1/num2;
}
public static void main(String[] args) {
CMDCalculate ();
}
- 编译期间就不通过的异常
public static void fun1() throws IOException {//这里不抛出将会报错
FileInputStream fileIn = new FileInputStream("C:\\1.text");
int word;
while ((word = fileIn.read())!= -1){//read 方法会抛出 IOException
System.out.print((char)word);
}
//close 方法会抛出 IOException
fileIn.close();
}
- 手动抛出异常
public static void main(String[] args) {
//手动抛出异常,可以给异常带有参数
String str = null;
if (str == null){
throw new NullPointerException("str == null");
}
}
- 程序自己抛出异常,然后捕获
public static void main (String[] args) {
try{
int a = 10;
int d = a/0;
System.out.println(d);
}catch (ArithmeticException e){
System.out.println("ArithmeticException异常");
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
int[] array = {1,2,3,4};
System.out.println(array[4]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
e.printStackTrace();
}
}
- try ……catch……finally 捕获多个异常
- 在一个try语句块中可以捕获多个异常类型,并对不同的异常做不同的处理
public static void CMDCalculate(){
//这里已经处理了异常,不需要再声明异常了
//如果加了声明,当前函数就不处理了,调用该函数的一方进行处理。
Scanner scan = new Scanner(System.in);
try{
int num1 = scan.nextInt();
int num2 = scan.nextInt();
int result = devide(num1,num2);
System.out.println("result:"+result);
}catch (ArithmeticException e){
System.out.println("ArithmeticException异常");
e.printStackTrace();
}catch (InputMismatchException e){
System.out.println("InputMismatchException异常");
e.printStackTrace();
}finally {
scan.close();
System.out.println("执行finally块语句");
}
}
三、自定义异常
class SimpleException extends Exception{
//自定义异常类,继承Exception基类
}
public class Test2 {
public static void fun() throws SimpleException{
System.out.println("throws SimpleException");
throw new SimpleException();//可以给参数
}
public static void main(String[] args) {
Test2 test2 = new Test2();
try{
test2.fun();
} catch (SimpleException e) {
System.out.println("SimpleException caught it");
e.printStackTrace();
}
}
}
- 可以写带有参数和不带有参数的
class SimpleException extends Exception {
//自定义异常类,继承Exception基类
public SimpleException() {
}
public SimpleException(String ss) {
super(ss);
}
四、执行顺序
- finally 语句块在 try 语句块中的 return 语句之前执行
public class Test2{
public static void main(String[] args) {
try{
System.out.println("try block");
return;
}finally {
System.out.println("finally block");
}
}
}
- finally 语句块在 catch 语句块中的 return 语句之前执行。
public class Test2{
public static int test(){
int i = 1;
try{
System.out.println("try block");
i = 1/0;
return 1;
}catch (Exception e){
System.out.println("exception block");
return 2;
}finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
System.out.println("return value of test() : " + test());
}
}
- finally 中的 return 会覆盖 try 或者 catch 中的返回值
public static int fun1(){
try{
int a = 5/0;
}catch (Exception e){
return 1;
}finally {
return 2;
}
}
public static void main(String[] args) {
int result = fun1();
System.out.println(result);
}
}
public static int fun2(){
try {
return 1;
}finally {
return 2;
}
}
public static void main(String[] args) {
int result = fun2();
System.out.println(result);
}
}
try…catch…finally…中的return只要能执行,就都执行了,他们共同向一个内存地址写入返回值,后执行的将覆盖先执行的数据,而真正被调用者取的返回值就是最后一次写入的。
- 终极顺序
public class Test2{
public static String test(){
try{
System.out.println("try block");
return test1();
}finally {
System.out.println("finally block");
}
}
public static String test1(){
System.out.println("return statement");
return "after return";
}
public static void main(String[] args) {
System.out.println(test());
}
}
return test1();这条语句等同于 :
String tmp = test1();
return tmp;
public class Test2{
public static int foo() throws Exception {
try {
int a = 5/0;
return 1;
}catch(ArithmeticException amExp) {
throw new Exception("我是Exception异常 ");//ignore
}finally {
return 100;
}
}
//try中的异常被抑制
public static void main(String[] args) {
int result;
try{
result = foo();
System.out.println(result); //输出100
} catch (Exception e){
System.out.println(e.getMessage()); //没有捕获到异常
}
}
}
注意
- 不要在 finally 中使用 return
- 不要在 finally 中抛出异常
- 减轻 finally 的任务,不要在 finally 中做一些其它的事情,finally 块仅仅用来释放资源是最合适的
- 尽量将所有的 return 写在函数的最后面,而不是 try … catch … finally 中