说到异常先写一个demojava
public class Introduce { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入被除数:"); int divisor = scanner.nextInt(); System.out.print("请输入除数:"); int dividend = scanner.nextInt(); System.out.println(div(divisor, dividend)); System.out.println("继续执行----------------"); } public static int div(int divisor, int dividend) { return divisor / dividend; } }
正常状况下没问题,好比程序员
万一手欠把除数输成0,那么执行结果就变成这样数据库
或者:数组
像上面那样在程序运行期间出现了不正常的状况而致使程序没法正常运行就称做为异常,出现异常以后的代码也就不能执行!jvm
那如何解决呢?第一种方案是能够用if-else来解决socket
public class Solution { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入被除数:"); if (scanner.hasNextInt()) { int divisor = scanner.nextInt(); System.out.print("请输入除数:"); if (scanner.hasNextInt()) { int dividend = scanner.nextInt(); if (dividend != 0) { System.out.println(div(divisor, dividend)); } else { System.out.println("除数不能为0"); } } else { System.out.println("录入的不是int数据!"); } } else { System.out.println("录入的不是int数据!"); } System.out.println("继续执行----------------"); } public static int div(int divisor, int dividend) { return divisor / dividend; } }
看完以后是否是感受这个if嵌套代码有些臃肿,业务代码也与处理异常的代码混在一块儿;可读性还不好;最重要的是程序员没法想到全部可能发生异常状况。基于if-else处理机制的缺点,java提供了一套处理异常机制ide
public class Solution2 { public static void main(String[] args) { try { Scanner scanner = new Scanner(System.in); System.out.print("请输入被除数:"); int divisor = scanner.nextInt(); System.out.print("请输入除数:"); int dividend = scanner.nextInt(); System.out.println(div(divisor, dividend)); } catch (Exception e) { e.printStackTrace(); } System.out.println("继续执行----------------"); } public static int div(int divisor, int dividend) { return divisor / dividend; } }
这段代码的try-catch块包围的代码就是用来处理异常的也叫作捕获异常,try块用来处理可能出现异常的代码,catch块用来处理try块中出现的异常3d
语法:try{ 可疑代码,将异常生成对应的异常对象,传递给catch块 }catch(异常){ 异常处理 }
发生状况:try块中出现异常,则异常以后的代码不会执行,直接运行catch块中的语句code
try块中没出现异常,catch块中的语句不会执行对象
catch中的异常类型和你给出的异常类型匹配的话,会执行catch块中的语句
catch中的异常类型和你给出的异常类型不匹配的话,则至关于没进行异常处理,catch块中和以后的代码不会执行
//catch中处理异常 public class Catch { public static void main(String[] args) { try { int[] num = {42, 25, 32, 20, 14, 18}; System.out.println(num[num.length]); } catch (Exception e) { //第一种:空处理,什么都不写 //第二种:输出自定义异常信息 System.out.println("代码出现异常!"); //第三种:打印异常信息 System.out.println(e.toString());//打印异常的全限定名(包名+类名) System.out.println(e.getMessage());// 显示异常描述信息,这段代码提示你数组长度是6 e.printStackTrace();//显示异常的详细信息 //第四种:抛出异常 throw e; } } }
另外try-catch语句还能够进行嵌套,catch语句也能够有多个造成多层catch
public class MultipleCatch { public static void main(String[] args) { try { Scanner scanner = new Scanner(System.in); System.out.print("请输入被除数:"); int divisor = scanner.nextInt(); System.out.print("请输入除数:"); int dividend = scanner.nextInt(); new MultipleCatch().div(divisor, dividend); //多层catch分别匹配InputMismatchException和ArithmeticException异常,匹配到哪个就执行哪个,若是没匹配到至关于没处理异常 } catch (InputMismatchException e) { e.printStackTrace(); } catch (ArithmeticException e) { e.printStackTrace(); } finally { System.out.println("继续执行----------------"); } } public void div(int num1, int num2) { System.out.println(num1 / num2); } }
下面这个InputStream和OutputStream不知道是什么,这不要紧,重点是演示try-catch语句
public class NestedTryCatch { public static void main(String[] args) { //InputStream和OutputStream都是一个流 InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = new FileInputStream(new File("F:\\study")); outputStream = new FileOutputStream(new File("F:\\study")); //多层catch,子类异常必须写在父类异常上面 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch(Exception e) { e.printStackTrace(); } finally { try { //流使用完毕须要关闭,这里就是一个嵌套try-catch语句 inputStream.close(); try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } } }
上面的多层匹配分别匹配FileNotFoundException、IOException和Exception异常,匹配到哪个就执行哪个
值得注意的是这几个异常有继承关系的,FileNotFoundException继承自IOException,它也是接着继承Exception的
再看一下继承体系
编译时异常:就指程序编译时产生的异常,必须处理,不然代码不能经过运行
运行时异常:运行以后才可能出现的异常,能够不作处理,通常是程序的逻辑错误,尽可能避免!
越往上就是父类能处理的异常范围越大,因此就能够想象出为何子类异常必须写在父类,若是同级别的异常就不用关心顺序
异常代码以后可能还有代码语句,但try-catch语句块可能运行完以后后续代码不会执行
throw抛出异常
catch语句块没有捕获成功
try语句块中有return语句
public class Try { public static void main(String[] args) { try { String[] str = {"a", "b", "c", "d"}; //System.out.println(str[str.length]); System.out.println(str.length); return; } catch (Exception e) { //throw e; } System.out.println("后续代码-------------"); } }
但就想异常代码处理以后,不管异常信息是否捕获成功,后续的代码都会运行,能够加finally语句
public class Finally { public static void main(String[] args) { int num = 10; try { System.out.println(num); //执行结果:10 return; } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("后续代码-------------"); num += 100; System.out.println(num); //执行结果:110,由于先执行finally再执行try中的return } } } //finally块通常操做关闭数据库资源,关闭IO流资源,关闭socket资源。
在进行异常捕获的时候,return语句位置的不一样其实可能会形成结果不一样
//一、try中有return,finally中没有return public class Test01 { public static void main(String[] args){ System.out.println(test()); } private static int test(){ int num = 10; try{ System.out.println("try"); return num += 80; }catch(Exception e){ System.out.println("error"); }finally{ if (num > 20){ System.out.println("num>20 : " + num); } System.out.println("finally"); } return num; } } /* 执行结果: try num>20 : 90 finally 90 这里把return num += 80拆分红两个语句了,num+=80和return,看一下class反编译的代码 */ public class Test1 { public TryTest() { } public static void main(String[] args) { System.out.println(test()); } private static int test() { int num = 10; try { System.out.println("try"); num += 80; int var1 = num; return var1; } catch (Exception var5) { System.out.println("error"); } finally { if (num > 20) { System.out.println("num>20 : " + num); } System.out.println("finally"); } return num; } }
//二、try和finally中均有return public class Test2 { public static void main(String[] args) { System.out.println(test()); } private static int test() { int num = 10; try { System.out.println("try"); return num += 80; } catch (Exception e) { System.out.println("error"); } finally { if (num > 20) { System.out.println("num>20 : " + num); } System.out.println("finally"); num = 100; return num; } } } /* 执行结果: try num>20 : 90 finally 100 这里一样把return num += 80拆分红两个语句了,num+=80和return,并且try中的return被省略了,直接执行finally中的return语句,获得返回值。看一下class反编译的代码 */ public class Test2 { public Test2() { } public static void main(String[] args) { System.out.println(test()); } private static int test() { int num = 10; try { System.out.println("try"); num += 80; } catch (Exception var5) { System.out.println("error"); } finally { if (num > 20) { System.out.println("num>20 : " + num); } System.out.println("finally"); int num = 100; return num; } } }
//三、finally中改变返回值num,可是不返回 public class Test3 { public static void main(String[] args){ System.out.println(test()); } private static int test(){ int num = 10; try{ System.out.println("try"); return num; }catch(Exception e){ System.out.println("error"); }finally{ if (num > 20){ System.out.println("num>20 : " + num); } System.out.println("finally"); num = 100; } return num; } } /* 执行结果: try finally 10 finally没有return时,这里把num的值用第三方变量存储起来了,finally执行结束return的是那个第三方变量,仍是看一下class反编译的代码 */ public class Test3 { public Test3() { } public static void main(String[] args) { System.out.println(test()); } private static int test() { byte num = 10; try { System.out.println("try"); byte var1 = num; return var1; } catch (Exception var5) { System.out.println("error"); } finally { if (num > 20) { System.out.println("num>20 : " + num); } System.out.println("finally"); num = 100; } return num; } }
//将num的值包装在Num类中 public class Test4 { public static void main(String[] args){ System.out.println(test().num); } private static Num test(){ Num number = new Num(); try{ System.out.println("try"); return number; }catch(Exception e){ System.out.println("error"); }finally{ if (number.num > 20){ System.out.println("number.num>20 : " + number.num); } System.out.println("finally"); number.num = 100; } return number; } } class Num{ public int num = 10; } /* 执行结果: try finally 100 这里finally也没有return,这里第三方变量存储的是建立对象的那个地址值,finally执行结束建立的对象改变值,仍是看一下class反编译的代码 */ public class Test4 { public Test4() { } public static void main(String[] args) { System.out.println(test().num); } private static Num test() { Num number = new Num(); try { System.out.println("try"); Num var1 = number; return var1; } catch (Exception var5) { System.out.println("error"); } finally { if (number.num > 20) { System.out.println("number.num>20 : " + number.num); } System.out.println("finally"); number.num = 100; } return number; } } class Num { public int num = 10; Num() { } }
java除了提供了try-catch块这种捕获异常的解决方案,还提供了一种声明抛出异常的解决方案throws,即本身不处理这些异常,而是丢给调用方处理,若是整个程序的运行过程当中都没有异常的处理的话,最终异常会抛给jvm,不太友好,通常都要对异常进行处理
public class ThrowsDemo { public static void main(String[] args) throws Exception { new ThrowsDemo().test(); } public void test() throws FileNotFoundException { FileInputStream inputStream=new FileInputStream("F:\\study\\test.txt"); } }
开发人员还能够⾃定义异常,⼀般经过继承Exception的⼦类的⽅式实现,本质上是覆盖原有异常API的信息
public class CustomException extends Exception{ static final long serialVersionUID = -70348971907L; public CustomException(String message) { super(message); } }
//模拟一下余额不足的状况 public class Test { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int money = scanner.nextInt(); new Test().getMoney(money, 2000); } public int getMoney(int money, int price) { if (money < price) { try { throw new CustomException("余额不⾜!"); } catch (CustomException e) { e.printStackTrace(); } } System.out.println("继续执行程序~~~"); return money; } }
自定义异常的步骤:
继承于现的异常结构:RuntimeException 、Exception
提供全局常量:serialVersionUID
编写构造方法,能够传入本身想打印的异常信息
调用的时候经过throw向外抛出异常
若是继承的是运行时异常,那么在使用的时候无需额外处理;若是继承的是检查异常,那么使用的时候须要try-catch捕获或者throws向上抛
throw和throws的区别:
Java里,对于文件操做IO流、数据库链接等开销很是昂贵的资源,用完以后必须及时经过close方法将其关闭,不然资源会一直处于打开状态,可能会致使内存泄露等问题。
关闭资源的经常使用方式就是在finally块里是释放,即调用close方法。好比,咱们常常会写这样的代码:
public static void test() { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); File f = new File("F:\\demo.txt"); FileWriter fw = null; BufferedWriter bw = null; try { fw=new FileWriter(f); bw = new BufferedWriter(fw); String s = br.readLine(); while (!s.equals("exit")) { bw.write(s); bw.newLine();//文件中换行 s = br.readLine(); } } catch (IOException e) { e.printStackTrace(); } //4.关闭流: try { bw.close(); } catch (IOException e) { e.printStackTrace(); } try { br.close(); } catch (IOException e) { e.printStackTrace(); } }
从Java 7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句,改写一下上面的代码,效果以下:
public static void test2(){ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try(FileWriter fw=new FileWriter(new File("F:\\demo.txt")); BufferedWriter bw=new BufferedWriter(fw)){ String s = br.readLine(); while (!s.equals("exit")) { bw.write(s); bw.newLine(); s = br.readLine(); } } catch (IOException e) { e.printStackTrace(); } }
是否是感受简洁了好多,其实这就是一个语法糖,它将在编译时编译成关闭资源的代码。咱们将上述例子中的代码编译成class文件,再反编译回java文件,就能看到以下代码:
public static void test2() { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); File f = new File("F:\\demo.txt"); try { FileWriter fw = new FileWriter(f); Throwable var4 = null; try { BufferedWriter bw = new BufferedWriter(fw); Throwable var6 = null; try { for(String s = br.readLine(); !s.equals("exit"); s = br.readLine()) { bw.write(s); bw.newLine(); } } catch (Throwable var31) { var6 = var31; throw var31; } finally { if (bw != null) { if (var6 != null) { try { bw.close(); } catch (Throwable var30) { var6.addSuppressed(var30); } } else { bw.close(); } } } } catch (Throwable var33) { var4 = var33; throw var33; } finally { if (fw != null) { if (var4 != null) { try { fw.close(); } catch (Throwable var29) { var4.addSuppressed(var29); } } else { fw.close(); } } } } catch (IOException var35) { var35.printStackTrace(); } }
除了异常代码,咱们看到其实关闭流的处理是底层帮咱们处理的