本文共 5781 字,大约阅读时间需要 19 分钟。
对于异常情况,例如,可能造成程序崩溃的错误输入,Java是通过捕获机制来处理异常错误。当程序出错时,我们不可能总是及时和用户沟通,所以希望记录出现的问题,以备日后进行分析。
当一个用户在运行程序期间,由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了。为了避免这类事情发生,我们应该注意以下几点。
所有Java异常对象都是由Throwable继承而来。如果Java内置的异常类不满足我们的需求,我们可以创建自己的异常类。下面是我画的Java异常结构的一个简化示意图。
Error | 是描述Java运行时系统内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样内部错误,除了告知客户,并尽力使用程序安全地终止之外,别无他法,不过这样的情况很少出现。 |
Exception | 是描述Java应用程序抛出和处理的非严重型错误,程序错误导致的异常就属于RuntimeException;而程序本身没有问题,但由于I/O错误这类问题导致的异常属于其他异常。 |
派生于RuntimeException的异常包含下面几种情况:
不是派生于RuntimeException的异常(其他异常):
如果存在RuntimeException异常,那么就一定是你的问题这是一条非常有道理的规则,不知道大家认不认可,我们应该在变量使用前检测是否为null可以防止出现NullPointerException(空指针异常);通过检测数组下标是否越界可以避免ArrayIndexOutOfBoundsException异常。
当我们需要在一个方法中抛出一个异常时,我们使用throw后加某异常类的实例,程序会在此向客户端程序(调用这段代码的程序)抛出对应异常并在此退出(相当于return)。另外需要注意的是,我们必须在定义该方法的时候指明异常类型,比如下面这段代码会抛出testException异常。
public void demo() throws testException,AException,BException{ throw new testException(); }
不同的异常类之间用逗号隔开即可,在这种情况下我们不必须throw每个异常类的实例(),但是客户端代码必须要catch到每个异常类:
public class MyException { public static void main(String[] args){ MyException e = new MyException(); try { e.demo(); } catch (testException e1) { e1.printStackTrace(); } catch (BException e1) { e1.printStackTrace(); } catch (AException e1) { e1.printStackTrace(); } } public void a() throws testException,AException,BException{ throw new testException(); }} class testException extends Exception {};class AException extends Exception{};class BException extends Exception{};
try { //code more code}catch (Exception e){ //handler for this type}
注:捕获多个异常不仅让你代码看起来更简单,还会更加高效。
try { ...}catch (FileNotFoundException e){ ...}catch (UnknownHostException e){ ...}catch (IOException e){ ...}
异常对象可能包含与异常本身有关的信息。要想获取对象更多的信息,可以尝试使用e.getMessage(),得到详细的错误信息(如果有的话),或者使用e.getClass().getName()得到异常对象的实际类型。
在catch子句中可以抛出一个异常,这样做是为了改变异常的类型。如果我们开发了一个供其他程序使用的子系统,那么表示子系统故障的异常类型可能会产生很多种解释。ServletException就是如此,执行servlet代码可能不想知道发生错误的细节,但希望明确知道servlet是否有问题。
try { //连接database}catch (SQLException e){ throw new ServeletException("database error:"+e.getMessage());//throw抛异常}
这里,ServeletException用带有异常信息文本的构造器来构造。不过可以有一种更好的处理方法,并且将原始异常设置为新异常的原因:
try { //连接database}catch (SQLException e){ Throwable se = new ServeletException("database error"); se.initCause(e); throw se;}
当捕获到异常时,就可以用下面这条语句重新得到原始异常:
Throwable e = se.getCause();
强烈建议使用这种包装技术。可以让用户抛出子系统的高级异常,而不会丢失原始异常的细节。
小提示:如果在一个方法中发生异常,而又不允许抛出它,那么包装技术就十分重要。我们可以捕获这个异常,并将它包装成一个运行时异常。比如:有时候你可能只想记录一个异常,再将它重新抛出,而不做任何的改变,代码示例如下:
try { //连接database}catch (Exception e){ logger.log(level,message,e);//日志 throw e;}
不管是否有异常被捕获,finally子句中的代码总是会被执行。当finally包含return时,将会出现意想不到的结果,假设从try中退出return。在方法返回前,finally中的内容将会被执行。如果finally中也有return,这个返回值会覆盖原始的返回值,例子如下:
public static int(int n){ try{ int r = n*n; return r; } finally{ if(n==2) return 0; }}
如果在finally中关闭资源,最好加上处理,防止异常,这是最传统的做法。下面第五点将给大家介绍jdk7之后资源关闭的方式。
finally{ try{ in.close(); }catch(Exception e){ throw e; }}
如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。
当try退出时,会自动调用close方法,去读取单词中的所有单词,示例如下:
try(Scanner in = new Scanner(New FileInputStream("/user/share/dict/words")),"UTF-8"){ while(in.hasNext()){ System.out.println(in.next()); }}
如果try抛出一个异常,而且close方法也抛出一个异常,这样就会带来一个难题。到资源的try可以很好地处理。原来的close方法抛出的异常被抑制。这些异常将会自动捕获,并由addSuperssed方法增加到原来的异常。如果对这些异常感兴趣,可以调用getSupperssed方法,它会得到从close抛出并抑制的异常列表。
有时候Java内置异常不满足于我们的需求,我们就需要用到自定义异常。比如:系统中有些错误是符合Java语法的,但不符合我们项目的业务逻辑;企业项目是分模块或者分功能开发的 ,使用自定义异常类就统一了对外异常展示的方式等。
所有异常都必须是 Throwable 的子类。 |
如果希望写一个检查性异常类,则需要继承 Exception 类。 |
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。 |
public class DatamartAssertException extends Exception { private static final long serialVersionUID = 7358625131054183395L; public DatamartAssertException() { } public DatamartAssertException(String message) { super(message); } }
public static void main(String[] args) throws DatamartAssertException{ CloseableHttpClient httpClient = null; try{ //创建httpClient实例 httpClient = HttpClients.createDefault(); //地址url String url = "www.baidu.coms"; //创建HttpPost实例 HttpPost method = new HttpPost(url); //设置请求读取超时时间 60秒 RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(60000).setConnectionRequestTimeout(60000) .setSocketTimeout(60000).build(); method.setConfig(requestConfig); //执行请求 try{ HttpResponse result = httpClient.execute(method); /**请求发送成功,并得到响应**/ if (result.getStatusLine().getStatusCode() == 200) { String str = ""; /**读取服务器返回过来的json字符串数据**/ str = EntityUtils.toString(result.getEntity()); } }catch (Exception e){ e.printStackTrace(); } }catch(ConnectTimeoutException e){ // 捕获超时异常 并反馈给调用者 throw new DatamartAssertException("请求超时!"); }catch(ConnectException e) { throw new DatamartAssertException("请求超时!"); }catch (Exception e){ e.printStackTrace(); } finally{ /**关闭连接,释放资源**/ try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } }
总结:不要过分的细化异常,不要压制异常;早抛出,晚捕获。
--------------如果大家喜欢我的博客,可以点击左上角的关注哦。