이 글에서는 주로 Java 예외(Exception) 처리 메커니즘에 대한 자세한 설명을 소개합니다. 예외의 정의와 사용법은 주로
을 참조하세요. 1. 예외
의 정의는 "Java 프로그래밍 생각"에서 다음과 같이 정의됩니다. 예외: 현재 메서드나 범위가 계속 실행되지 못하게 하는 문제입니다. Java에는 예외 처리 메커니즘이 있지만 예외를 "정상적인" 태도로 보아서는 안 된다는 점은 분명합니다. 엄밀히 말하면 예외는 어떤 의미에서는 오류이자 문제이며, 이로 인해 프로그램이 실패할 수도 있습니다. Java가 예외 처리 메커니즘을 제안하는 이유는 개발자에게 프로그램에 이상이 있음을 알리기 위한 것입니다.
Java를 처음 배웠을 때 예외가 무엇을 의미하는지, 왜 이런 메커니즘이 있는지는 항상 혼란스러웠습니다. 하지만 지식이 쌓이면서 점차 변칙성에 대해 조금씩 느끼기 시작했습니다. 예외 사용을 설명하는 예를 들어보세요.
public class Calculator { public int devide(int num1, int num2) { //判断除数是否为0 if(num2 == 0) { throw new IllegalArgumentException("除数不能为零"); } return num1/num2; } }
이 클래스에서 나눗셈 연산 방법을 살펴보세요. 초보자라면 매개변수가 올바른지, 적법한지 고려하지 않고 바로 계산 결과를 반환할 수 있습니다. .(물론 용서할 수 있어요, 다들 그렇잖아요). 하지만 크래들에서 프로그램이 실패할 수 있는 "징후"를 최대한 철저하게 제거해야 하므로 매개변수의 적법성을 확인하는 것이 필요합니다. 그 중 매개변수 검사에서 발생한 매개변수 불법 예외는 이 메서드의 비정상적인 상황입니다. 일반적인 상황에서는 계산기를 올바르게 사용하지만 실수로 제수를 0에 할당하는 경우도 배제할 수 없습니다. 이전에 이 상황을 고려하지 않았고 사용자의 수학적 기초가 열악한 경우 문제가 발생한 것입니다. 그러나 이전에 이러한 상황을 고려해 본 적이 있다면 오류가 귀하의 통제 범위 내에 있음이 분명합니다.
2. 비정상적인 문해력 행동
오늘 누군가와 대화를 나누다가 농담을 봤습니다. 세상에서 가장 진정한 의존은 당신이 노력하고 내가 잡을 때입니다. 아무리 화를 내셔도 묵묵히 참고 조용히 처리하겠습니다. 대부분의 초보자는 Java 예외가 try...catch...라고 생각합니다. 예, 이것이 가장 많이 사용되고 가장 실용적입니다. 내 느낌은 다음과 같습니다. Java 예외는 "try...catch..."에서 발생합니다.
먼저 Java의 예외 시스템에 대해 알아봅시다.
Throwable 클래스는 Java 언어의 모든 오류 또는 예외의 상위 클래스입니다(이것이 발생할 수 있는 모든 것입니다). 여기에는 오류와 예외라는 두 가지 하위 클래스가 있습니다.
오류: 합리적인 애플리케이션이 포착하려고 시도해서는 안 되는 심각한 문제를 나타내는 데 사용됩니다. 이 상황은 당신이 감당하기에는 너무 큰 문제이므로 그냥 내버려두고 걱정하지 마십시오. 예를 들어 VirtualMachineError: 이 오류는 JVM(Java Virtual Machine)이 충돌하거나 계속 작동하는 데 필요한 리소스가 부족할 때 발생합니다. 좋습니다. 이러한 예외가 존재하더라도 언제, 어떻게 처리해야 할까요? ? JVM에 맡기세요. 그것보다 더 전문적인 것은 없습니다.
예외: 합리적인 애플리케이션이 포착하려는 조건을 나타냅니다. 예외는 두 가지 범주로 나뉩니다. 하나는 CheckedException이고 다른 하나는 UncheckedException입니다. 이 두 예외의 주요 차이점은 CheckedException은 try...catch...를 사용하여 명시적으로 캡처해야 하지만 UncheckedException은 캡처할 필요가 없다는 것입니다. 일반적으로 UncheckedException은 RuntimeException이라고도 합니다. "Effective Java"는 다음을 지적합니다. 복구 가능한 조건에는 검사된 예외(CheckedException)를 사용하고 프로그램 오류에는 런타임 예외(RuntimeException)를 사용합니다(복구할 수 없으며 큰 실수가 발생했다는 의미).
일반적인 RuntimeExcepitons에는 IllegalArgumentException, IllegalStateException, NullPointerException, IndexOutOfBoundsException 등이 포함됩니다. 언급할 CheckedException이 너무 많습니다. 프로그램을 작성하는 동안 try...catch...에 의해 포착된 예외는 모두 CheckedException입니다. IOException과 io 패키지의 하위 클래스는 CheckedException입니다.
3. 예외 사용
예외 사용 부분은 주로 데모 코드로, 코드를 작성하는 과정에서 접하게 되는 모든 것입니다(물론 작은 부분에 불과합니다). 다른 사람들에게 영감을 주세요!
예제 1. 이 예는 주로 두 메소드를 비교하여 예외 발생 후 코드의 실행 흐름을 보여줍니다.
public static void testException2() { int[] ints = new int[] { 1, 2, 3, 4 }; System.out.println("异常出现前"); System.out.println(ints[4]); System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行 }
首先指出例子中的不足之处,IndexOutofBoundsException是一个非受检异常,所以不用try...catch...显示捕捉,但是我的目的是对同一个异常用不同的处理方式,看它会有什么不同的而结果(这里也就只能用它将就一下了)。异常出现时第一个方法只是跳出了try块,但是它后面的代码会照样执行的。但是第二种就不一样了直接跳出了方法,比较强硬。从第一个方法中我们看到,try...catch...是一种"事务性"的保障,它的目的是保证程序在异常的情况下运行完毕,同时它还会告知程序员程序中出错的详细信息(这种详细信息有时要依赖于程序员设计)。
例2. 重新抛出异常
public class Rethrow { public static void readFile(String file) throws FileNotFoundException { try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理"); //重新抛出异常 throw e; } } public static void printFile(String file) { try { readFile(file); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { printFile("D:/file"); } }
异常的本意是好的,让我们试图修复程序,但是现实中我们修复的几率很小,我们很多时候就是用它来记录出错的信息。如果你厌倦了不停的处理异常,重新抛出异常对你来说可能是一个很好的解脱。原封不动的把这个异常抛给上一级,抛给调用这个方法的人,让他来费脑筋吧。这样看来,java异常(当然指的是受检异常)又给我们平添很多麻烦,尽管它的出发点是好的。
例3. 异常链的使用及异常丢失
定义三个异常类:ExceptionA,ExceptionB,ExceptionC
public class ExceptionA extends Exception { public ExceptionA(String str) { super(); } } public class ExceptionB extends ExceptionA { public ExceptionB(String str) { super(str); } } public class ExceptionC extends ExceptionA { public ExceptionC(String str) { super(str); } }
异常丢失的情况:
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:19) */
为什么只是打印出来了ExceptionC而没有打印出ExceptionB呢?这个还是自己分析一下吧!
上面的情况相当于少了一种异常,这在我们排错的过程中非常的不利。那我们遇到上面的情况应该怎么办呢?这就是异常链的用武之地:保存异常信息,在抛出另外一个异常的同时不丢失原来的异常。
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); //异常连 c.initCause(e); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:21) Caused by: exception.ExceptionB at exception.NeverCaught.f(NeverCaught.java:5) at exception.NeverCaught.g(NeverCaught.java:10) ... 1 more */
这个异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。
例4. 清理工作
清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。异常的出现要求我们必须设计一种机制不论什么情况下,资源都能及时正确的清理。这就是finally。
public void readFile(String file) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream(file))); // do some other work } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
例子非常的简单,是一个读取文件的例子。这样的例子在JDBC操作中也非常的常见。(所以,我觉得对于资源的及时正确清理是一个程序员的基本素质之一。)
Try...finally结构也是保证资源正确关闭的一个手段。如果你不清楚代码执行过程中会发生什么异常情况会导致资源不能得到清理,那么你就用try对这段"可疑"代码进行包装,然后在finally中进行资源的清理。举一个例子:
public void readFile() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work //close reader reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
我们注意一下这个方法和上一个方法的区别,下一个人可能习惯更好一点,及早的关闭reader。但是往往事与愿违,因为在reader.close()以前异常随时可能发生,这样的代码结构不能预防任何异常的出现。因为程序会在异常出现的地方跳出,后面的代码不能执行(这在上面应经用实例证明过)。这时我们就可以用try...finally来改造:
public void readFile() { BufferedReader reader = null; try { try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work // close reader } finally { reader.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
及早的关闭资源是一种良好的行为,因为时间越长你忘记关闭的可能性越大。这样在配合上try...finally就保证万无一失了(不要嫌麻烦,java就是这么中规中矩)。
再说一种情况,假如我想在构造方法中打开一个文件或者创建一个JDBC连接,因为我们要在其他的方法中使用这个资源,所以不能在构造方法中及早的将这个资源关闭。那我们是不是就没辙了呢?答案是否定的。看一下下面的例子:
public class ResourceInConstructor { BufferedReader reader = null; public ResourceInConstructor() { try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(""))); } catch (FileNotFoundException e) { e.printStackTrace(); } } public void readFile() { try { while(reader.readLine()!=null) { //do some work } } catch (IOException e) { e.printStackTrace(); } } public void dispose() { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
这一部分讲的多了一点,但是异常确实是看起来容易用起来难的东西呀,java中还是有好多的东西需要深挖的。
四. 异常的误用
对于异常的误用着实很常见,上一部分中已经列举了几个,大家仔细的看一下。下面再说两个其他的。
例1. 用一个Exception来捕捉所有的异常,颇有"一夫当关万夫莫开"的气魄。不过这也是最傻的行为。
public void readFile(String file) { BufferedReader reader = null; Connection conn = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream(file))); // do some other work conn = DriverManager.getConnection(""); //... } catch (Exception e) { e.printStackTrace(); } finally { try { reader.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
从异常角度来说这样严格的程序确实是万无一失,所有的异常都能捕获。但是站在编程人员的角度,万一这个程序出错了我们该如何分辨是到底是那引起的呢,IO还是JDBC...所以,这种写法很值得当做一个反例。大家不要以为这种做法很幼稚,傻子才会做。我在公司实习时确实看见了类似的情况:只不过是人家没有用Exception而是用了Throwable。
例2. 这里就不举例子了,上面的程序都是反例。异常是程序处理意外情况的机制,当程序发生意外时,我们需要尽可能多的得到意外的信息,包括发生的位置,描述,原因等等。这些都是我们解决问题的线索。但是上面的例子都只是简单的printStackTrace()。如果我们自己写代码,就要尽可能多的对这个异常进行描述。比如说为什么会出现这个异常,什么情况下会发生这个异常。如果传入方法的参数不正确,告知什么样的参数是合法的参数,或者给出一个sample。
例3. 将try block写的简短,不要所有的东西都扔在这里,我们尽可能的分析出到底哪几行程序可能出现异常,只是对可能出现异常的代码进行try。尽量为每一个异常写一个try...catch,避免异常丢失。在IO操作中,一个IOException也具有"一夫当关万夫莫开"的气魄。
五.总结
总结非常简单,不要为了使用异常而使用异常。异常是程序设计的一部分,对它的设计也要考究点。
위 내용은 Java 예외(Exception) 처리 메커니즘 샘플 코드 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!