南昌建站软件,黄金网站app下载免费,福州设计公司,抖音小程序商城文章目录 一、异常处理概述二、方式1#xff1a;捕获异常#xff08;try-catch-finally#xff09;#xff08;1#xff09;抓抛模型#xff08;2#xff09;try-catch-finally基本格式1、基本语法2、整体执行过程3、try和catch3.1 try3.2 catch (Exceptiontype e) 捕获异常try-catch-finally1抓抛模型2try-catch-finally基本格式1、基本语法2、整体执行过程3、try和catch3.1 try3.2 catch (Exceptiontype e) 3catch中异常处理的方式1、处理方式2、案例13、案例24、案例35、案例46、整体代码 4 finally使用及举例1、finally介绍2、举例3、笔试题3.1 题13.2 题23.3 题33.4 题4 4、面试题5、对finally的理解 5异常处理的体会 一、异常处理概述
在编写程序时经常要在可能出现错误的地方加上检测的代码如进行x/y运算时要检测分母为0数据为空输入的不是数据而是字符等。
过多的if-else分支会导致程序的代码加长、臃肿可读性差程序员需要花很大的精力“堵漏洞”。
因此采用异常处理机制。
【Java异常处理】
Java采用的异常处理机制是将异常处理的程序代码集中在一起与正常的程序代码分开使得程序简洁、优雅并易于维护。 异常处理中的“异常”是Exception因为Error不编写针对性的代码进行处理。 Java异常处理的方式
方式一try-catch-finally
方式二throws 异常类型
二、方式1捕获异常try-catch-finally
1抓抛模型
Java提供了异常处理的抓抛模型。
前面提到Java程序的执行过程中如出现异常会生成一个异常类对象该异常对象将被提交给Java运行时系统这个过程称为抛出(throw)异常。如果一个方法内抛出异常该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去直到异常被处理。这一过程称为捕获(catch)异常。如果一个异常回到main()方法并且main()也不处理则程序运行终止。
方式一抓抛模型try-catch-finally
过程1“抛”-- 产生异常的对象 程序在执行的过程当中一旦出现异常就会在出现异常的代码处生成对应异常类的对象并将此对象抛出。目前先看自动创建异常的对象并抛出一旦抛出此程序就不执行其后的代码了。 过程2“抓”-- 捕获处理 针对于过程1中抛出的异常对象进行捕获处理。此捕获处理的过程就称为抓。一旦将异常进行了处理代码就可以继续执行。
2try-catch-finally基本格式
1、基本语法
捕获异常语法如下
try{...... //可能产生异常的代码
}
catch( 异常类型1 e ){...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){...... //当产生异常类型2型异常时的处置措施
}
finally{...... //无论是否发生异常都无条件执行的语句
}try后面有一对大括号大括号里面包裹的就是可能产生异常的代码可能有多行。
若是此代码在执行的过程当中没有出现异常那么就不会执行下面catch代码。先不管finally
若是此代码在执行的过程当中出现了异常那么就会“抛”出异常。
比如现在try里面有4行代码执行在第2行出现了异常那么第2行就会抛出一个对象第3行和第4行就不执行了。
接下来需要捕获对象它会根据这个对象的类型依次匹配下面的多个catch。若第一个catch不匹配就继续与下面的catch匹配若成功匹配就执行catch里面的代码catch里面是对异常处理的代码。执行结束下面的catch就不执行了。
try{...... //可能产生异常的代码
}
catch( 异常类型1 e ){ //不匹配...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){ //匹配执行...... //当产生异常类型2型异常时的处置措施
}
... //异常处理结束这里的代码可以继续执行try里面有4行代码第3行和第4行没有机会执行了。finally是try-catch里面可以放的结构里面放的代码比较特别。就是不管前面什么情况这里面的代码一定执行。暂时不说它。 2、整体执行过程
当某段代码可能发生异常不管这个异常是编译时异常受检异常还是运行时异常非受检异常我们都可以使用try块将它括起来并在try块下面编写catch分支尝试捕获对应的异常对象。
如果在程序运行时try块中的代码没有发生异常那么catch所有的分支都不执行。如果在程序运行时try块中的代码发生了异常根据异常对象的类型将从上到下选择第一个匹配的catch分支执行。此时try中发生异常的语句下面的代码将不执行而整个try…catch之后的代码可以继续运行。如果在程序运行时try块中的代码发生了异常但是所有catch分支都无法匹配捕获这个异常那么JVM将会终止当前方法的执行并把异常对象**“抛”给调用者**。如果调用者不处理程序就挂了。 【使用细节】
将可能出现异常的代码声明在try语句中。一旦代码出现异常就会自动生成一个对应异常类的对象。并将此对象抛出。针对于try中抛出的异常类的对象使用之后的catch语句进行匹配。一旦匹配上就进入catch语句块进行处理。一旦处理结束代码就可继续向下执行。如果声明了多个catch结构不同的异常类型在不存在子父类关系的情况下谁声明在上面谁声明在下面都可以。如果多个异常类型满足子父类的关系则必须将子类声明在父类结构的上面。否则报错。
3、try和catch
3.1 try
捕获异常的第一步是用try{…}语句块选定捕获异常的范围将可能出现异常的业务逻辑代码放在try语句块中。
3.2 catch (Exceptiontype e)
catch分支分为两个部分catch()中编写异常类型和异常参数名{}中编写如果发生了这个异常要做什么处理的代码。如果明确知道产生的是何种异常可以用该异常类作为catch的参数也可以用其父类作为catch的参数。 比如可以用ArithmeticException类作为参数的地方就可以用RuntimeException类作为参数或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常如NullPointerExceptioncatch中的语句将不会执行。每个try语句块可以伴随一个或多个catch语句用于处理可能产生的不同类型的异常对象。如果有多个catch分支并且多个异常类型有父子类关系必须保证小的子异常类型在上大的父异常类型在下。否则报错。catch中常用异常处理的方式 public String getMessage()获取异常的描述信息返回字符串public void printStackTrace()打印异常的跟踪栈信息并输出到控制台。包含了异常的类型、异常的原因、还包括异常出现的位置在开发和调试阶段都得使用printStackTrace()。 3catch中异常处理的方式
1、处理方式
java.lang.Throwable 类是Java程序执行过程中发生的异常事件对应的类的根父类。
Throwable类是异常的根父类它继承于Object类。
Throwable中的常用方法
public void printStackTrace()打印异常的详细信息。 包含了异常的类型、异常的原因、异常出现的位置、在开发和调试阶段都得使用printStackTrace。public String getMessage()获取发生异常的原因。 返回值是字符串。
【catch中异常处理的方式】
① 自己编写输出的语句。
② printStackTrace()打印异常的详细信息。 推荐
③ getMessage()获取发生异常的原因。 try中声明的变量出了try结构之后就不可以进行调用了。 try-catch结构是可以嵌套使用的。 2、案例1
处理方式自己编写输出的语句。
以如下代码为例
public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}
}接下来将可能出现异常的代码用try包裹假如这几行都可能有异常都包裹起来如下
public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致try{Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}}
}然后将可能出现的异常写在catch里面这里可能出现的异常是InputMismatchException如下
public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致try{Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}catch (InputMismatchException e){}}
}比如随便写一句话
package yuyi02;import org.junit.Test;import java.util.InputMismatchException;
import java.util.Scanner;public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致try{Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}catch (InputMismatchException e){System.out.println(出现了InputMismatchException的异常);}System.out.println(异常处理结束代码继续执行);}
}运行
①无异常
运行看一下比如输入了10 此时意味着在try里面输出了10catch就不执行。然后将try-catch后面的代码继续执行了。
②有异常
再次运行输入abc 此时在try里面第2行出现了InputMismatchException的异常它会自动创建这个类型的对象这个对象一抛出会被相匹配的catch语句捕获然后进行异常处理。最后将try-catch后面的代码继续执行。try里面第3行代码没有机会执行了因为第2行抛出了异常 ☕补充
①
当然我们也可以有多个catch比如
public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致try{Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}catch (InputMismatchException e){System.out.println(出现了InputMismatchException的异常);}catch (NullPointerException e){ //空指针异常System.out.println(出现了NullPointerException的异常);}catch (RuntimeException e){ //运行时异常System.out.println(出现了RuntimeException的异常);}System.out.println(异常处理结束代码继续执行);}
}比如在代码执行过程中可能会有上面几种异常若是第1个catch没有进去就是没有匹配成功那么就会继续匹配后面的catch。
若是都没有进去那就会报异常。相当于和没有处理一样。
②
InputMismatchException和NullPointerException是两个并列的类没有继承关系可以颠倒。如下 RuntimeException是InputMismatchException和NullPointerException的父类应当写在下面。
若写在上面就会报错因为后面两个就没有机会执行会说该异常已经被处理了如下 ③
InputMismatchException e中InputMismatchException是变量类型e是变量名。因为上面抛出来的异常对象没有名字。
在catch里面可以使用e这个变量名。
有效范围是大括号内所以各个catch都能用e来当变量名。
3、案例2
处理方式printStackTrace()打印异常的详细信息。 推荐
以下面代码为例
package yuyi02;import org.junit.Test;public class ExceptionHandleTest {Testpublic void test2(){//NumberFormatException 数据格式化异常String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}
}假如这四行代码可能出现异常将它们用try包裹起来如下
Test
public void test2(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}}然后写上可能出现的异常
Test
public void test2(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){e.printStackTrace(); //打印堆栈信息}System.out.println(程序结束);
}输出 此时程序正常结束了异常也处理了这里只不过是输出了异常信息而已。
把相应的哪个方法里面调用哪个了情况都给罗列出来了。
☕注意
有人就问了这里和没有处理的显示一样那么有啥意义呢
若是没有写try-catch语句此时程序出现了异常如下 其实真正在开发当中像这种异常可以考虑不去处理。
因为处理、不处理报出来的结果一样。这个处理其实就是预防万一出问题就要按照指定的方式去执行。
但是归根结底这个代码还是有问题的。
所以对于这种运行时异常平时就是不处理。若是出现了异常那就直接去改代码。
给try-catch的目的是提前预知了可能有问题的代码并给一种方案。不至于让程序挂掉。
比如支付某个订单的时候出现了错误界面不会出现一堆乱码而是出现一个友好的提示这其实就是一种呈现异常的解决方式。应用程序还可以正常运行只是支付功能没有实现而已。最终还是要改代码啦。
上面的例子其实是编译时异常对于运行时异常没有必要处处加上try-catch只要出现了就回去改代码。 【开发体会】 对于运行时异常 开发中通常就不进行显示的处理了。一旦在程序执行中出现了运行时异常那么就根据异常的提示信息修改代码即可。 对于编译时异常try-catch主要处理的是编译时异常 一定要进行处理。否则编译不通过。
4、案例3
处理方式getMessage()获取发生异常的原因。
以下面代码为例
package yuyi02;import org.junit.Test;public class ExceptionHandleTest {Testpublic void test2(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){System.out.println(e.getMessage());}System.out.println(程序结束);}
}这里返回字符串所以需要主动输出一下。
输出 这里表达的意思是因为写了“abc”不满足要求所以出现异常。
☕注意
在这个地方其实不能打印str了如下 这是因为在try里面声明的变量作用域只在try里面出了try就看不到了。 try中声明的变量出了try结构之后就不可以进行调用了。 try-catch结构是可以嵌套使用的。 5、案例4
以下面代码为例
Test
public void test3() {File filenew File(D:\\hello.txt);//可能报FileNotFoundException 文件找不到异常FileInputStream fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}//资源关闭fis.close(); //可能报IOException 输入输出异常
}其实这个文件是存在的不会出现错误。但此时编译就是不让过如下 此时必须要处理一下。
将可能出现异常的代码用try包裹起来如下
Test
public void test3() {try {File filenew File(D:\\hello.txt);//可能报FileNotFoundException 文件找不到异常FileInputStream fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}//资源关闭fis.close(); //可能报IOException 输入输出异常}}然后针对可能出现的异常写catch如下
Test
public void test3() {try {File filenew File(D:\\hello.txt);//可能报FileNotFoundException 文件找不到异常FileInputStream fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}//资源关闭fis.close(); //可能报IOException 输入输出异常}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}}将光标放在FileNotFoundException上按Ctrlh可以看到继承关系如下 可以看到FileNotFoundException继承于IOException这里不是继承于RuntimeException所以是编译时异常。
因为它们是子父类的关系所以不能颠倒写的顺序。
☕注意
这里我们写的比较细两种可能出现的异常都写出来了处理方案都一样。
这种情况下若将上面一个删了也是可以的如下 即使这里抛的是一个FileNotFoundException这里也能够捕获。
其次IOException是FileNotFoundException的父类它们的printStackTrace()方法的方法体不一样是多态。
所以完全可以替换。
输出
这个例子在上一篇文章讲过文件里面的内容就是如下显示的内容 在整个程序运行过程中没有出现任何异常也没有捕获过。 若此时找不到文件比如将路径更改一下 再次运行 出现的异常是在FileInputStream fisnew FileInputStream(file);这个位置产生了一个FileNotFoundException的对象这个对象直接出了try的花括号try里面剩下的语句都无法执行了。
然后就到了catch里面找到相匹配的处理打印相应的信息。最后执行try-catch后面的语句即可。
如下 可以调试看一下过程 编译时异常虽然处理了但是在运行的时候还可能会出现。
可以理解为把一个编译时异常延后到运行时出现了。在运行的时候报出来。 编译时异常必须要处理若是不处理就不能运行。 【开发体会】 对于运行时异常 开发中通常就不进行显示的处理了。一旦在程序执行中出现了运行时异常那么就根据异常的提示信息修改代码即可。 对于编译时异常try-catch主要处理的是编译时异常 一定要进行处理。否则编译不通过。
所以平常我们写代码写完一行发现出红线了就看一下什么原因既然是编译时异常那么就将它用try-catch解决一下即可。
6、整体代码
代码
package yuyi02;import org.junit.Test;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;/*** ClassName: ExceptionHandleTest* Package: yuyi02* Description:** Author 雨翼轻尘* Create 2024/1/10 0010 9:39*/
public class ExceptionHandleTest {Testpublic void test1(){//InputMismatchException 输入类型不一致try{Scanner scannernew Scanner(System.in);int num scanner.nextInt(); //若输入的不是int类型则会报错System.out.println(num);}catch (NullPointerException e){ //空指针异常System.out.println(出现了NullPointerException的异常);}catch (InputMismatchException e){System.out.println(出现了InputMismatchException的异常);}catch (RuntimeException e){ //运行时异常System.out.println(出现了RuntimeException的异常);}System.out.println(异常处理结束代码继续执行...);}Testpublic void test2(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){System.out.println(e.getMessage());}System.out.println(程序结束...);}Testpublic void test3() {try {File filenew File(D:\\hello1.txt);//可能报FileNotFoundException 文件找不到异常FileInputStream fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}//资源关闭fis.close(); //可能报IOException 输入输出异常}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}System.out.println(读取数据结束...);}
}输出结果 4 finally使用及举例
1、finally介绍
捕获异常语法如下
try{...... //可能产生异常的代码
}
catch( 异常类型1 e ){...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){...... //当产生异常类型2型异常时的处置措施
}
finally{...... //无论是否发生异常都无条件执行的语句
}两种情况 ☕注意
因为异常会引发程序跳转从而会导致有些语句执行不到。而程序中有一些特定的代码无论异常是否发生都需要执行。例如数据库连接、输入流输出流、Socket连接、Lock锁的关闭等这样的代码通常就会放到finally块中。所以我们通常将一定要被执行的代码声明在finally中。 唯一的例外使用 System.exit(0) 来终止当前正在运行的 Java 虚拟机。这个操作会让程序强行结束。也就是说除非让当前Java虚拟机运行的进程强行结束除此之外finally中的代码一定会被执行 不论在try代码块中是否发生了异常事件catch语句是否执行catch语句是否有异常catch语句中是否有returnfinally块中的语句都会被执行。finally语句和catch语句是可选的但finally不能单独使用。 之前说switch-case的时候default是可选的包括if-else中的else是可选的可选的意思就是可写可不写。
【try-catch】
try{...... //可能产生异常的代码
}
catch( 异常类型1 e ){...... //当产生异常类型1型异常时的处置措施
}//没有finally【try-finally】
对于运行时异常通常不做显示的处理就根据异常提示信息修改代码即可。
对于编译时异常一定要处理否则编译不通过。
若是某一些代码可能会有运行时异常不用显示处理所以不要catch了但是这个代码万一出现了运行时异常怎么办呢在finally里面写上一定要被执行的代码即可。
try{}finally{}//没有catch可以没有catch仅有finally可以没有finally仅有catch。 但是不能仅仅只有一个try。 2、举例
以如下代码为例
package yuyi02;import org.junit.Test;public class FinallyTest {Testpublic void test1(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){e.printStackTrace();}System.out.println(程序结束...);}
}此时代码表达的意思如下 再看一下输出 可以看到此时确实出现异常了而且这个异常在catch里面被捕获了。
try-catch下面的代码可以继续执行。
那么写在try-catch下面的代码一定会执行为啥要刻意写一个finally呢
看一下下面两种写法 此时感觉不到两者的区别因为执行结果都一致。
现在改一下代码在catch中出现了异常而此时没有地方给它处理。
public class FinallyTest {Testpublic void test1(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){e.printStackTrace();System.out.println(10/0); //在catch中存在异常}System.out.println(程序结束...);}Testpublic void test2(){//NumberFormatException 数据格式化异常try {String str123;strabc;int iInteger.parseInt(str);System.out.println(i);}catch (NumberFormatException e){e.printStackTrace();System.out.println(10/0); //在catch中存在异常}finally {System.out.println(程序结束...);}}
}️分析
在test1中执行过程如下 看一下输出结果 此时没有打印程序结束…。
再看一下test2打印了程序结束…如下 所以如果有一定要执行的代码就放到finally里面。 3、笔试题
3.1 题1
下面代码输出结果是
代码
package yuyi02.interview;/*** ClassName: FinallyTest1* Package: yuyi02.interview* Description:** Author 雨翼轻尘* Create 2024/1/11 0011 9:41*/
public class FinallyTest1 {public static void main(String[] args) {int result test(12);System.out.println(result);}public static int test(String str){try{Integer.parseInt(str);return 1;}catch(NumberFormatException e){return -1;}finally{System.out.println(test结束);}}
}输出结果 分析 即使有returnfinally也一定会执行。 若是没有finally最后一行代码还无法存在如下 因为此时在try和catch中都有return最后一行输出语句没有机会执行但要是写在finally里面就不一样啦。
3.2 题2
下面代码输出结果是
代码
package yuyi02.interview;/*** ClassName: FinallyTest2* Package: yuyi02.interview* Description:** Author 雨翼轻尘* Create 2024/1/11 0011 9:42*/
public class FinallyTest2 {public static void main(String[] args) {int result test(a);System.out.println(result);}public static int test(String str) {try {Integer.parseInt(str);return 1;} catch (NumberFormatException e) {return -1;} finally {System.out.println(test结束);}}
}输出结果 分析 不管try和catch里面有没有执行语句finally一定会被执行。 3.3 题3
下面代码输出结果是
代码
package yuyi02.interview;/*** ClassName: FinallyTest3* Package: yuyi02.interview* Description:** Author 雨翼轻尘* Create 2024/1/11 0011 9:43*/
public class FinallyTest3 {public static void main(String[] args) {int result test(a);System.out.println(result);}public static int test(String str) {try {Integer.parseInt(str);return 1;} catch (NumberFormatException e) {return -1;} finally {System.out.println(test结束);return 0;}}
}输出结果 分析 3.4 题4
下面代码输出结果是
代码1
package yuyi02.interview;/*** ClassName: FinallyTest4* Package: yuyi02.interview* Description:** Author 雨翼轻尘* Create 2024/1/11 0011 9:43*/
public class FinallyTest4 {public static void main(String[] args) {int result test(10);System.out.println(result);}public static int test(int num) {try {return num;} catch (NumberFormatException e) {return num--;} finally {System.out.println(test结束);return num;}}
}输出结果 分析 代码2
package yuyi02.interview;/*** ClassName: FinallyTest4* Package: yuyi02.interview* Description:** Author 雨翼轻尘* Create 2024/1/11 0011 9:43*/
public class FinallyTest4 {public static void main(String[] args) {int result test(10);System.out.println(result);}public static int test(int num) {try {return num;} catch (NumberFormatException e) {return num--;} finally {System.out.println(test结束);num;}}
}输出结果 分析 方法都是一个栈帧num是一个局部变量局部变量存在于栈帧里面。
栈帧又分为好几个部分其中最重要的有局部变量表数组用来存放局部变量、操作数栈存放临时运算的数据。
画个图看一下 4、面试题
final 、 finally 、finalize 的区别
final最终的
finally一定会被执行的代码
finalize方法名
5、对finally的理解
【finally的理解】
我们将一定要被执行的代码声明在finally结构中。更深刻的理解无论try中或catch中是否存在仍未被处理的异常无论try中或catch中是否存在return语句等finally中声明的语句都一定要被执行。finally语句和catch语句是可选的但finally不能单独使用。
️什么样的代码我们一定要声明在finally中呢
我们在开发中有一些资源比如输入流、输出流数据库连接、Socket连接等资源在使用完以后必须显式地进行关闭操作否则GC垃圾回收器不会自动地回收这些资源进而导致内存的泄漏。
为了保证这些资源在使用完以后不管是否出现了未被处理的异常的情况下这些资源能被关闭。我们必须将这些操作声明在finally中 【举例】
以如下代码为例
//实际开发中finally的使用
Test
public void test3() {try {File filenew File(D:\\hello.txt);//可能报FileNotFoundException 文件找不到异常FileInputStream fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}//资源关闭fis.close(); //可能报IOException 输入输出异常}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}System.out.println(读取数据结束...);
}这里面涉及资源的关闭fis.close();但是处理的不太好。
如下 ☕注意
既然test3是一个方法执行的时候它的栈帧进入栈空间代码走完栈帧不是弹出去了吗栈帧弹出去了那么栈帧里面定义的fis也因该销毁了啊
fis指向堆空间造的一个流对象既然指针没有了那GC为啥不能回收呢
注意test3方法执行完栈帧确实弹出了变量也没有了。GC去回收它的时候由于现在是个流流需要跟物理磁盘上的文件打交道包括网络资源也是一样。它还有其他的引用指向。
所以GC在回收的时候他发现还有其他的指针指向所以没有办法回收。
实际上此时没有变量名指向它也用不了。
从我们的角度来看是垃圾但是GC发现还有别的引用指向它就没有回收。 此时必须调用fis.close()将其他连接断掉这时候GC才能够回收否则就可能有泄露问题。
既然fis.close()的操作必须要被执行而若是有其他风险的话可能执行不了就需要将它放入finally里面。
如下 此时发现fis报红这是因为在try里面声明的变量出了try就看不见了。
所以就需要将相关代码也一起拿出去但是此时可能报的异常怎么办如下 这时候需要将处理异常的部分还放在try里面。既然变量会看不见那就不在try里面声明。
在外面声明在try里面赋值即可。如下 此时既在try里面处理了异常又可以在finally里面看见了。 现在还有一个问题close()也可能有异常所以也需要处理一下。
如下 可以看到try-catch里面是可以嵌套使用的。
现在的程序就可以解决刚才的泄露问题 其实还可以让程序健壮性更好若fisnew FileInputStream(file); 这里出现异常流并没有创建成功处理异常之后还要执行finallyfis.close();就会出现空指针。
所以可以在finally里面可以先加一个if判断。如下 代码
//实际开发中finally的使用
Test
public void test3() {FileInputStream fisnull; //在try外面声明让finally里面也可以使用try {File filenew File(D:\\hello.txt);//可能报FileNotFoundException 文件找不到异常fisnew FileInputStream(file); //流直接操作文件//把文件内容直接读到内存中输出int datafis.read(); //可能报IOException 输入输出异常while(data!-1){ //data为-1的时候退出就是读完了System.out.print((char)data);datafis.read(); //可能报IOException 输入输出异常}}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}finally {//重点将流资源的关闭操作放在finally当中。try {if (fis!null){ //预防流没有创建成功会出现空指针的问题//资源关闭 防止读文件的时候出异常导致资源无法关闭所以写在finally里面必须执行fis.close(); //可能报IOException 输入输出异常}}catch (IOException e){e.printStackTrace();}}System.out.println(读取数据结束...);
}看一下前后对比 重点将流资源的关闭操作放在finally当中。 5异常处理的体会
前面使用的异常都是RuntimeException类或是它的子类这些类的异常的特点是即使没有使用try和catch捕获Java自己也能捕获并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。所以对于这类异常可以不作处理因为这类异常很普遍若全处理可能会对程序的可读性和运行效率产生影响。如果抛出的异常是IOException等类型的非运行时异常则必须捕获否则编译错误。也就是说我们必须处理编译时异常将异常进行捕捉转化为运行时异常。