一、异常引入
代码不能尽善尽美,如用户输入类型格式出错,找不到文件等不能避免异常的产生,影响用户体验,所以需要预处理,对能预料到的异常进行处理
(语法错误和逻辑错误不是异常)
异常事件分为error+exception(狭义的异常),出现error是程序员无法通过修改代码更正的,比如JVM系统内部错误、资源 耗尽等严重情况。一般不编写针对性 的代码进行处理。
对于这些异常,要不程序终止,要不程序员在编译的时候就考虑到这些异常并友好【提醒】处理
二、异常体系
异常exception分为编译器异常和运行期异常
1、编译器异常则设计代码的时候就要处理,不然编译不通过,但并不是一定有异常,但是编译就是不让你过,要不抛到主方法要不trycatch
2、运行期异常在编译的时候不会报错,运行才出现异常,一般不处理,若全处理可能会对 程序的可读性和运行效率产生影响。
public class ExceptionTest { //******************以下是编译时异常*************************** @Test public void test7(){ // 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(); } //******************以下是运行时异常*************************** //ArithmeticException @Test public void test6(){ int a = 10; int b = 0; System.out.println(a / b); } //InputMismatchException @Test public void test5(){ Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); System.out.println(score); scanner.close(); } //NumberFormatException @Test public void test4(){ String str = "123"; str = "abc"; int num = Integer.parseInt(str); } //ClassCastException @Test public void test3(){ Object obj = new Date(); String str = (String)obj; } //IndexOutOfBoundsException @Test public void test2(){ //ArrayIndexOutOfBoundsException // int[] arr = new int[10]; // System.out.println(arr[10]); //StringIndexOutOfBoundsException String str = "abc"; System.out.println(str.charAt(3)); } //NullPointerException @Test public void test1(){ // int[] arr = null; // System.out.println(arr[3]); String str = "abc"; str = null; System.out.println(str.charAt(0)); } }
三、异常处理方式
1、抓抛模型
过程一,抛:【系统自动/我们手动生成】异常对象并抛出,抛出后其后面的代码就不会执行了;
过程二,抓:【try-catch-finally或者throws】
try{ //可能出现异常的代码 } catch(异常类型1,变量名1){ //处理方法1 }catch(异常类型2,变量名2){ //处理方法2 }... finally{ //一定会执行的代码 }
体会:
1、finally是可选的
2、catch匹配了异常类型就不往后走了,选择性,【但是finally里边的代码是一定会执行的,即使catch又出现了异常或者try和catch有return语句】
3、异常类型如果有继承关系,放置catch时候,子类放父类之前,否则报错,如果没有继承关系,先后无所谓
4、catch处理方法一般getMessage(),printStackTrace()
5、try中声明的变量,出了try就不能使用了,技巧:非要使用5的话,可以将变量声明在方法外,并赋予初始值
6、trycatchfinally处理机制,编译没有报错,但是运行不能保证不报错【比如catch本身也会有异常等】,而是把编译期可能出现的异常推迟到运行期,
开发中由于运行时异常比较常见,也不报错,不知道是不是异常了,所以一般不针对运行时异常编写try-catch-finally,针对编译异常就要处理
7、finally一般放一些像输入输出流,数据库连接,网络编程socket等,jvm不能自动回收,需要我们自己去手动操作
8、trycatchfinally可以相互嵌套
过程二处理方法1
public class Test { public static void main(String[] args) { System.out.println(method()); // java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10 at demo01.Test.method(Test.java:11) at demo01.Test.main(Test.java:5) //我一定会被执行 //3 } public static 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; } } }
过程二处理方法2
throws+异常类型或者其父类【因为子类重写了父类的方法】 向上抛,谁调用就抛给谁,可以同时抛出多个类型【如果处理不同,则需要分别异常处理】;最终只能抛到main方法,那么这时候main继续抛由jvm处理,要么就要用try-catch了 throws+异常类型此机制声明在方法处,相比trycatch,其后续代码会终止,而trycatch则是处理了异常,trycatch后面的代码会运行。
public class Demo01Throw {//parseexception是编译器异常,编译器必须改代码,否则编译不通过 public static void main(String[] args) throws ParseException {//方法2=throws+异常类型,谁调用,抛给谁,虚拟机抛出异常,缺点是如果日期不符合pattern,则会终止抛出异常了 System.out.println("=========编译期异常,2种处理方法========="); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf.parse("2015-09-12");//这个方法本身是有异常,2种处理方法,1抛出,2try-catch // Date date = sdf.parse("2015-0912");//这个方法本身是有异常,2种处理方法,1抛出,2try-catch System.out.println(date); method2(); } public static void method2(){//方法1,异常抛出,但是程序可以运行 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try {//放可能异常的代码 System.out.println("hello1"); Date date = sdf.parse("2015-0912"); System.out.println("hello2"); } catch(NullPointerException e){ System.out.println("hello3"); } catch (ParseException e) {//符合了异常类型;
e.printStackTrace();//处理方法 } System.out.println("后续代码可以运行"); } }
public class Test {//运行期异常 public static void main(String[] args) /*throws Exception*/{//main如果也向上抛,那么jvm抛异常就是越界异常,如果trycatch了,那么处理了异常,如果throws和trycatch一起了,那么trycatch会先捕捉并异常,也就不会有异常向上抛给jvm,
//i向上抛的异常必须大于等于method方法里的异常类型,这里的ArrayIndexOutOfBoundsException可以替换成exception
//System.out.println("索引越界异常");
// System.out.println(method());//异常 try{ int[] arr = new int[10]; System.out.println(arr[10]); System.out.println(method()); }catch (ArrayIndexOutOfBoundsException e){//这里的异常类型必须罩住method抛出的异常
// System.out.println(method());//catch自己也报异常,这样就会调升到运行期异常
}
}
public static int method() throws ArrayIndexOutOfBoundsException{//向上抛出异常,谁调用谁处理异常,main方法调用了必须得处理,注意的是此处的
int[] arr = new int[10];
System.out.println(arr[10]);
return 1;
}
}
如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数
比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用 RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。 但不能是与ArithmeticException类无关的异常
* 3. 开发中如何选择使用try-catch-finally 还是使用throws?【父类的异常类型必须大于等于子类的异常类型】 * 3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果 * 子类重写的方法中有异常,必须使用try-catch-finally方式处理。 * 3.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws * 的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。》》最后再一起处理,同时如果第一个方法就trycatch那么第二个方法就不能使用到第一个方法的值了,这不好 * throws和trycatch语法上可以同时使用,但是没有必要,已经处理了为什么还要throws */ 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!"); } }
2、手动生产异常对象throw
public class StudentTest { public static void main(String[] args) { try { Student s = new Student(); s.regist(-1001); System.out.println(s); } catch (Exception e) { // e.printStackTrace(); System.out.println(e.getMessage()); } } } class Student{ private int id; public void regist(int id) throws Exception { if(id > 0){ this.id = id; }else{ // System.out.println("您输入的数据非法!"); //手动抛出异常对象 // throw new RuntimeException("您输入的数据非法!"); // throw new Exception("您输入的数据非法!"); throw new MyException("不能输入负数");
//throw new Runtimeexception("不能输入负数");//不会报错,编译能通过,因为是runtime异常,如果我们自己定义异常对象 //错误的 // throw new String("不能输入负数"); } } @Override public String toString() { return "Student [id=" + id + "]"; } }
/*
* 如何自定义异常类?
* 1. 继承于现有的异常结构:RuntimeException 、Exception
* 2. 提供全局常量:serialVersionUID
* 3. 提供重载的构造器
*
*/
public class MyException extends Exception{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
例子
public class ReturnExceptionDemo { static void methodA() { try { System.out.println("进入方法A");//@1 throw new RuntimeException("制造异常");@3 } finally { System.out.println("用A方法的finally");//@2 } } static void methodB() { try { System.out.println("进入方法B");@4 return; } finally { System.out.println("调用B方法的finally");@5 } } public static void main(String[] args) { try { methodA(); } catch (Exception e) { System.out.println(e.getMessage());@3 } methodB(); } }
/* 练习:接收命令行的2个参数z(main非法的args),不能输入负数,计算2数相除的结果 对数据不一样(numberformatexception),缺少命令行参数(arrayindexoutofboundsexception),除0,以及输入负数(自定义)进行异常处理 */ public class Demo04Throw { public static void main(String[] args) { try { int i =Integer.parseInt(args[0]);//用于输入2个参数通过命令行 int j =Integer.parseInt(args[1]); //因为抛出了非运行期异常@three,必须抛出或者trycatch否则报错 int result = ecm(i,j); System.out.println(result); } catch (EcDef e) {//定义的非负数 System.out.println(e.getMessage()); } catch (NumberFormatException e){//i j数据是非int类型 System.out.println(e.getMessage()); } catch (ArrayIndexOutOfBoundsException e){//缺少i,j 的值 System.out.println(e.getMessage()); } catch (ArithmeticException e){//除0异常 System.out.println(e.getMessage()); } } public static int ecm (int i,int j) throws EcDef{//抛出异常对象@two if(i<0||j<0){ throw new EcDef("分子或分母为负数了");//产生了编译期异常对象@one } return i/j; } } public class EcDef extends Exception{ static final long s=-3487; public EcDef(){ } public EcDef(String msg){ super(msg); } }
自定义的异常属于编译期异常