最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
jsp 不能抛出checked异常时 处理方法
时间:2010-05-06 编辑:简简单单 来源:一聚教程网
因此必须预先处理异常,另外,可以将异常转换为运行时异常,或者绕过它而不处理它。但是,应该这样做吗,这其中是否隐藏着错误?
问题
只要看一个例子,问题就清楚了。假设有一个File对象的List,需要按它们的标准路径以字典顺序排序。所谓标准路径,是指在解析别名、符号链接和/../及/./之后得到的完整绝对路径。本地方法使用一个比较器,如清单1所示:
1.清单1.按标准路径比较两个文件
2.importjava.io.File;
3.importjava.io.IOException;
4.importjava.util.ArrayList;
5.importjava.util.Collections;
6.importjava.util.Comparator;
7.
8.publicclassFileComparatorimplementsComparator
9.
10.publicintcompare(Filef1,Filef2){
11.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());
12.}
13.
14.publicstaticvoidmain(String[]args){
15.ArrayList
16.for(Stringarg:args){
17.files.add(newFile(arg));
18.}
19.Collections.sort(files,newFileComparator());
20.for(Filef:files){
21.System.out.println(f);
22.}
23.}
24.
25.}
不幸的是,该代码不能通过编译。问题在于,getCanonicalPath()方法抛出一个IOException,因为它需要访问文件系统。通常,当使用checked异常时,可以使用以下两种方法之一:
1.将出错的代码包装在一个try块中,并捕捉抛出的异常。
2.声明包装方法(本例为compare())也抛出IOException。
通常,至于选择何种方法,取决于是否能在抛出异常时合理地处理异常。如果能,那么使用try-catch块。如果不能,那么声明包装方法本身抛出异常。不幸的是,这两种技巧对于本例都不管用。在compare()方法中无法合理地处理IOException。从技术上讲,似乎可以做到-即返回0、1或-1,如清单2所示:
26.清单2.抛出异常时返回一个默认值
27.publicintcompare(Filef1,Filef2){
28.try{
29.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());
30.}
31.catch(IOExceptionex){
32.return-1;
33.}
34.}
然而,这违反了compare()方法的约定,因为它不是一个稳定的结果。对于相同的对象,前后两次调用可能产生不同的结果。如果使用这个比较器来排序,那么意味着最终列表没有被正确排序。所以现在试试第2个选项-声明compare()抛出IOException:
35.publicintcompare(Filef1,Filef2)throwsIOException{
36.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());
37.}
这也不能通过编译。因为checked异常是方法签名的一部分,在覆盖方法时,不能增加checked异常,就像不能改变return类型一样。那么最后还剩下一个折中选项:在compare()中捕捉异常,将它转换成运行时异常,然后抛出运行时异常,如清单3所示:
38.清单3.将checked异常转换成运行时异常
39.publicintcompare(Filef1,Filef2){
40.try{
41.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());
42.}
43.catch(IOExceptionex){
44.thrownewRuntimeException(ex);
45.}
46.}
不幸的是,虽然这样可以通过编译,但是这种方法也不管用,其原因较为微妙。Comparator接口定义一个合约(请参阅参考资料)。这个合约不允许该方法抛出运行时异常(防止因违反泛型类型安全而成为调用代码中的bug)。使用这个比较器的方法合理地依靠它来比较两个文件,而不抛出任何异常。它们没有准备好处理compare()中意外出现的异常。
正是由于这个微妙的原因,让运行时异常成为代码要处理的外部状况是一个坏主意。这样只是逃避问题,并没有真正处理问题。不处理异常所带来的不良后果仍然存在,包括毁坏数据和得到不正确的结果。
第一种办法是将问题一分为二。比较本身不会导致异常。比较的只是字符串而已。通过标准路径将文件转换成字符串才会导致异常。如果将可能抛出异常的操作与不会抛出异常的操作分开,那么问题就更容易处理了。也就是说,首先将所有文件对象转换为字符串,然后通过字符串比较器(甚至可以通过java.lang.String的自然排序)对字符串排序,最后使用排序后的字符串列表对原始的文件列表排序。这种方法不太直接,但是优点是在列表被改变之前就抛出IOException。如果出现异常,它只会出现在预先设计好的地方,不会造成损害,调用代码可以指定如何处理异常。清单4对此作了演示:
1.清单4.先读取,然后排序
2.importjava.io.File;
3.importjava.io.IOException;
4.importjava.util.ArrayList;
5.importjava.util.Collections;
6.importjava.util.HashMap;
7.
8.publicclassFileComparator{
9.
10.privatestaticArrayList
11.throwsIOException{
12.ArrayList
13.for(Filefile:files)paths.add(file.getCanonicalPath());
14.returnpaths;
15.}
16.
17.publicstaticvoidmain(String[]args)throwsIOException{
18.ArrayList
19.for(Stringarg:args){
20.files.add(newFile(arg));
21.}
22.
23.ArrayList
24.
25.//tomaintaintheoriginalmapping
26.HashMap
27.inti=0;
28.for(Stringpath:paths){
29.map.put(path,files.get(i));
30.i++;
31.}
32.
33.Collections.sort(paths);
34.files.clear();
35.for(Stringpath:paths){
36.files.add(map.get(path));
37.}
38.}
39.
40.}
清单4并没有消除出现I/O错误的可能性。这一点无法做到,因为这里的代码无力提供这样的功能。但是,可以将这个问题交给更合适的地方来处理。
避免问题
前面提到的方法有点复杂,所以我建议另一种方法:不使用内置的compare()函数或Collections.sort()。使用这样的函数也许比较方便,但是不适合当前情况。Comparable和Comparator是为确定的、可预测的比较操作而设计的。一旦I/O不再符合这种情况,很可能常用的算法和接口变得不适用。即使勉强可以使用,其效率也极其例如,假设不是按标准路径来比较文件,而是按内容来比较文件。对于所比较的两个文件,每个比较操作都需要读文件的内容-甚至可能是完整的内容。这样一来,高效的算法会想要尽量减少读的次数,并且可能会想缓存每次读的结果-或者,如果文件较大,则可能缓存每个文件的hashcode-而不是每次比较时重新读每个文件。同样,您会想到首先填充一个比较键列表,然后进行排序,而不是进行内联排序。可以想象定义一个单独的、并行的IOComparator接口,该接口抛出必要的异常,如清单5所示:
41.清单5.独立的IOComparator接口
42.importjava.io.IOException;
43.publicinterfaceIOComparator
44.intcompare(To1,To2)throwsIOException;
45.
47.清单6.用冒泡算法对文件排序
48.importjava.io.IOException;
49.importjava.util.ArrayList;
50.importjava.util.List;
51.
52.publicclassIOSorter{
53.
54.publicstatic
55.throwsIOException{
56.List
57.temp.addAll(list);
58.
59.bubblesort(temp,comparator);
60.
61.//copybacktooriginallistnowthatnoexceptionshavebeenthrown
62.list.clear();
63.list.addAll(temp);
64.}
65.
66.//ofcourseyoucanreplacethiswithabetteralgorithmsuchasquicksort
67.privatestatic
68.throwsIOException{
69.for(inti=1;i 70.for(intj=0;j 71.if(comparator.compare(list.get(j),list.get(j+1))>0){ 72.swap(list,j); 73.} 74.} 75.} 76.} 77. 78.privatestatic 79.Ttemp=list.get(j); 80.list.set(j,list.get(j+1)); 81.list.set(j+1,temp); 82.} 83. 84.} 这不是唯一的方法。为了清晰,清单6有意模仿已有的Collections.sort()方法;但是,也许更有效的方法是返回一个新的列表,而不是直接修改旧列表,以防在修改列表时抛出异常所带来的损害。 最终,您实际上承认并着手处理可能出现的I/O错误,而不是逃避它,您甚至可以做更高级的错误修正。例如,IOComparator也许不会被一次I/O错误难倒-因为很多I/O问题是暂时的-可以重试几次,如清单7所示: 1.清单7.如果一开始不成功,再试几次(但是别试太多次) 2.importjava.io.File; 3.importjava.io.IOException; 4. 5.publicclassCanonicalPathComparatorimplementsIOComparator 6. 7.@Override 8.publicintcompare(Filef1,Filef2)throwsIOException{ 9.for(inti=0;i<3;i++){ 10.try{ 11.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath()); 12.} 13.catch(IOExceptionex){ 14.continue; 15.} 16.} 17.//lastchance 18.returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath()); 19.} 20. 21.} 这种技巧不能解决常规的Comparator的问题,因为必须重试无数次才能避免抛出异常,而且很多I/O问题并不是暂时性的。 checked异常是坏主意吗? 如果java.io.IOException是运行时异常,而不是checked异常,问题是不是有所改观?答案是否定的。如果IOException扩展RuntimeException而不是java.lang.Exception,那么更容易编写出有bug的、不正确的代码,这种代码忽略了真正可能发生的I/O错误,而在运行时出人意料地失败。 然而,编写正确的、有准备并且能够处理I/O错误的代码并不会更容易。是的,相对于不会出现意外I/O错误,不需要为此做准备的情况,这种方法更加复杂。但是,从Java语言中消除checked异常无助于我们实现那样的理想情况。I/O错误和其他环境问题是常态,积极准备比视而不见要好得多。
-
上一个: jsp 在线人数统计程序
-
下一个: jsp 多线程开发与Java并发模型框架
相关文章
- SpringBoot测试配置属性与web启动环境解析 10-24
- vue中将el-switch值true、false改为number类型的1和0解析 10-24
- Vue中的路由配置项meta使用解读 10-24
- SpringBoot自定义bean绑定解析 10-24
- SpringBoot常用计量与bean属性校验和进制数据转换规则解析 10-24
- 工厂方法在Spring框架中的运用介绍 10-24