01、异常概述与异常体系结构
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
异常:
在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
Java程序在执行过程中所发生的异常事件可分为两类:
对于这些错误,一般有两种解决方法:
- 一是遇到错误就终止程序的运行。
- 另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等
异常分类:编译时异常和运行时异常
- 运行时异常
- 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。
- 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
- 编译时异常
- 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
- 对于这类异常,如果程序不处理,可能会带来意想不到的结果。
02、常见异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| import java.io.File; import java.io.FileInputStream; import java.util.Date; import java.util.Scanner; import org.junit.Test;
public class ExceptionTest {
@Test public void test7() {
}
@Test public void test6() { int a = 10; int b = 0; System.out.println(a / b); }
@Test public void test5() { Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); System.out.println(score);
scanner.close(); }
@Test public void test4() { String str = "123"; str = "abc"; int num = Integer.parseInt(str); }
@Test public void test3() { Object obj = new Date(); String str = (String)obj; }
@Test public void test2() {
}
@Test public void test1() {
} }
|
03、异常处理机制一:try-catch-finally
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。
Java异常处理:
Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
方式一:try-catch-finally
方式二:throws + 异常类型
Java异常处理的方式: try-catch-finally
- try
- 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
- catch(Exceptiontypee)
- 在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
- 捕获异常的有关信息:与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
- getMessage() 获取异常信息,返回字符串
- printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
- finally
- 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
- 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
- finally语句和catch语句是任选的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException;
import org.junit.Test;
public class ExceptionTest1 { @Test public void test2(){ try{ File file = new File("hello.txt"); FileInputStream fis = new FileInputStream(file); int data = fis.read(); while(data != -1){ System.out.print((char)data); data = fis.read(); } fis.close(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); } }
@Test public void test1(){ String str = "123"; str = "abc"; try{ int num = Integer.parseInt(str); System.out.println("hello-----1"); }catch(NumberFormatException e){
e.printStackTrace(); }catch(NullPointerException e){ System.out.println("出现空指针异常了,不要着急...."); }catch(Exception e){ System.out.println("出现异常了,不要着急...."); }
System.out.println("hello----2"); } }
|
3.1、finally的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException;
import org.junit.Test;
public class FinallyTest {
@Test public void test2() { FileInputStream fis = null; try { File file = new File("hello1.txt"); fis = new FileInputStream(file);
int data = fis.read(); while (data != -1) { System.out.print((char) data); data = fis.read(); }
} catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
@Test public void testMethod() { int num = method(); System.out.println(num); }
public int method() {
try { int[] arr = new int[10]; System.out.println(arr[10]); return 1; } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return 2; } finally { System.out.println("我一定会被执行"); return 3; } }
@Test public void test1() { try { int a = 10; int b = 0; System.out.println(a / b); } catch (ArithmeticException e) {
int[] arr = new int[10]; System.out.println(arr[10]);
} catch (Exception e) { e.printStackTrace(); } finally { System.out.println("我好慢呀~~~"); } } }
|
04、异常处理机制二:throws
- 声明抛出异常是Java中处理异常的第二种方式
- 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
- 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
public class ExceptionTest2 { public static void main(String[] args){ try { method2(); } catch (IOException e) { e.printStackTrace(); } method3(); } public static void method3(){ try { method2(); } catch (IOException e) { e.printStackTrace(); } } public static void method2() throws IOException{ method1(); }
public static void method1() throws FileNotFoundException,IOException{ File file = new File("hello1.txt"); FileInputStream fis = new FileInputStream(file); int data = fis.read(); while(data != -1){ System.out.print((char)data); data = fis.read(); } fis.close(); System.out.println("hahaha!"); } }
|
4.1、重写方法声明抛出异常的原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import java.io.FileNotFoundException; import java.io.IOException;
public class OverrideTest { public static void main(String[] args) { OverrideTest test = new OverrideTest(); test.display(new SubClass()); } public void display(SuperClass s){ try { s.method(); } catch (IOException e) { e.printStackTrace(); } } } class SuperClass{ public void method() throws IOException{ } } class SubClass extends SuperClass{ public void method()throws FileNotFoundException{ } }
|
05、手动抛出异常
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
- 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)
- 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class StudentTest { public static void main(String[] args) { try { Student s = new Student();
s.regist(-1001); System.out.println(s); } catch (Exception e) {
System.out.println(e.getMessage()); } } } class Student{ private int id; public void regist(int id) throws Exception{ if(id > 0){ this.id = id; }else{
throw new Exception("您输入的数据非法!"); } }
@Override public String toString() { return "Student [id=" + id + "]"; } }
|
06、用户自定义异常类
- 一般地,用户自定义异常类都是RuntimeException的子类。
- 自定义异常类通常需要编写几个重载的构造器。
- 自定义异常需要提供serialVersionUID
- 自定义的异常通过throw抛出。
- 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public class MyException extends RuntimeException{ static final long serialVersionUID = -7034897193246939L; public MyException(){ } public MyException(String msg){ super(msg); } }
|
6.1、练习
练习1——ReturnExceptionDemo类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class ReturnExceptionDemo { static void methodA() { try { System.out.println("进入方法A"); throw new RuntimeException("制造异常"); } finally { System.out.println("用A方法的finally"); } }
static void methodB() { try { System.out.println("进入方法B"); return; } finally { System.out.println("调用B方法的finally"); } }
public static void main(String[] args) { try { methodA(); } catch (Exception e) { System.out.println(e.getMessage()); } methodB(); } }
|
练习2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
public class EcmDef { public static void main(String[] args) { try { int i = Integer.parseInt(args[0]); int j = Integer.parseInt(args[0]); int result = ecm(i,j); System.out.println(result); } catch (NumberFormatException e) { System.out.println("数据类型不一致"); }catch (ArrayIndexOutOfBoundsException e){ System.out.println("缺少命令行参数"); }catch (ArithmeticException e){ System.out.println("除0"); }catch (EcDef e) { System.out.println(e.getMessage()); } } public static int ecm(int i, int j) throws EcDef{ if(i < 0 || j < 0){ throw new EcDef("分子或分母为负数了!"); } return i / j; } }
|
练习2的自定义异常类——EcDef
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class EcDef extends Exception {
static final long serialVersionUID = -33875164229948L;
public EcDef() { }
public EcDef(String msg) { super(msg); } }
|
07、异常总结
总结:异常处理5个关键字