一、概念
先上官方概念:异常就是一个表示阻止执行正常进行的错误或情况。就是说,我们的程序运行不了的情况。异常分为error和exception,
error是系统错误,是java虚拟机抛出的,一旦发生程序就没法运行了,只能尽量优雅的终止程序;
exception是由程序或者外部环境引起的错误,这些错误能被程序捕获和处理,如IOException、ClassNotFoundExcepiton等。
runtime exception 是运行时异常,是由程序涉及错误导致的,比如错误的类型转换、越界访问数组等。运行时异常通常由jvm抛出。
RuntimeExcepiton和Error都是免检异常,其他的是必检异常,编译器强制要求必须对必检异常进行检查并处理。
二、异常怎么用呢?
知道了异常是啥了,来看看怎么用。
java异常的处理有三种操作:声明异常、抛出、捕获
2-1 声明异常
有可能抛出必检异常的方法在定义的时候必须声明他可能抛出的必检异常的类型。
public void myMthod() throws Exception1,Exception2....
注意:如果方法没有在父类中声明异常,那么不能在子类中对其进行覆盖来声明 异常。
2-2 抛出异常
创建一个异常类型,然后抛出。
throw new IOException("ioex");
可以这样理解,声明异常是告诉别人我这个方法里面有异常,抛出异常就是在这个方法里面创建这个异常实例。
注意:声明异常的关键字是throws,抛出异常的关键字是throw
2-3 捕获异常
当一个异常被抛出的时候可以在try-catch块中捕获和处理它。
try{ ... }catch (Exception1 ex1){ ... }catch (Exception1 ex2){ ...}
如果执行try过程中出现相应异常,由相应的catch块内的异常处理器对异常进行处理。寻找处理器的过程叫做捕获异常。
如果异常没有在当前的方法中被捕获,就被传给该方法的调用者,这个过程一直重复,直到异常被捕获或被传给main方法。
注意:catch块中异常被指定的顺序非常重要,父类的catch块出现在子类的catch块之前,会导致编译错误。
异常使方法抛出一个异常给他的调用者,调用者可以处理异常。一般情况下,被调用的方法不知道在出错的情况下做些什么,只有调用者知道如何处理。异常处理的根本优势其实是提供了一种机制,就是将检测错误(由被调用的方法完成)和处理错误(由于调用方法完成)分离。
2-4 finally子句
不论异常是否出现或者是否被捕获,fianlly块中的代码都会执行,即使在到达finally块之前有return子句,也会执行,所以可以在这里处理例如文件关闭等操作。
三、异常的一个使用案例--程序中关闭文件的最佳实践
在一次实践中,遇到一个问题,在程序中调用 file.delete() 删除文件总是失败。查了些资料后发现是由于jvm占用文件导致文件不能删除,debug过程中尝试手动删除文件会提示“文件已在java platform se binary”中打开。
一般来说 java file.delete失败 有以下几个原因
1.看看是否被别的进程引用,手工删除试试(删除不了就是被别的进程占用)
2.file是文件夹 并且不为空,有别的文件夹或文件,
3.极有可能有可能自己前面没有关闭此文件的流
转载自:https://www.cnblogs.com/stono/p/6736767.html
但是添加了文件流关闭的语句之后依然没有解决我的问题,推测可能是流关闭的方式有问题,才想到应该在finally中关闭流才靠谱,去查书发现书里的写法是直接在finally语句中关闭文件。
try{ …… }catch (Excepiton e){ …… }finally{ if(output != nll) output.close(); }
但是这样的方式也不是很好,因为close方法也是会抛出异常的,于是查到了在try中关闭文件文件流的正确姿势。(感谢:https://blog.csdn.net/qq_27093465/article/details/52439754)
流在try外面声明,在try里面初始化,然后在finally里面给close,还记得处理异常e,finally里面close的时候也得再次try catch 一下。
/** * 测试正确关闭文件流 */ private static void testCloseFileStream() { final Logger LOG = LoggerFactory.getLogger(Cmshome.class); String fileName = ""; InputStream inputStream = null;//声明个引用,因为这个new对象的时候也是会异常的 try { //这里就会异常,如果文件名不存在的话。 inputStream = new FileInputStream(fileName); } catch (IOException e) { //这个主要是把出现的异常给人看见,不然就算异常了,看不到就找不到问题所在。 LOG.debug("loadProperties IOException:" + e.getMessage()); } finally { if (inputStream != null) { try { inputStream.close(); // 关闭流 } catch (IOException e) { LOG.debug("inputStream close IOException:" + e.getMessage()); } } } } //错误的关闭文件的方式的解释: Properties properties = new Properties(); try { //这要是异常,直接就到catch语句,下面的close就不会执行啦,关闭就没用啦 InputStream wrongWay = new FileInputStream(fileName); properties.load(wrongWay); wrongWay.close(); // 关闭流 } catch (IOException e) { e.printStackTrace(); } //下面是new文件流和关闭文件流的源码,有抛异常动作。 public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } //这个是抽象类(abstract class)里面的方法,所以没有具体实现过程。 public void close() throws IOException {}
最终我关闭文件后删除文件使用的方式:
…… }finally { if(failEmbedReader != null) { try { failEmbedReader.close(); } catch (IOException e) { System.out.println(e); } Logger.getGlobal().info("删除失败文件"); recoverFile.delete(); } }