- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章老生常谈Java异常处理和设计(推荐)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在程序设计中,进行异常处理是非常关键和重要的一部分。一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度。试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻找出错的根源?但是如果一个项目异常处理设计地过多,又会严重影响到代码质量以及程序的性能。因此,如何高效简洁地设计异常处理是一门艺术,本文下面先讲述java异常机制最基础的知识,然后给出在进行java异常处理设计时的几个建议.
若有不正之处,请多多谅解和指正,不胜感激.
以下是本文的目录大纲:
1、什么是异常 。
2、java中如何处理异常 。
3、深刻理解try,catch,finally,throws,throw五个关键字 。
4、在类继承的时候,方法覆盖时如何进行异常抛出声明 。
5、异常处理和设计的几个建议 。
1、什么是异常 。
异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图。错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的。假若程序在运行期间出现了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这不是我们希望看到的结果。因此,如何对运行期间出现的错误进行处理和补救呢?java提供了异常机制来进行处理,通过异常机制来处理程序运行期间出现的错误。通过异常机制,我们可以更好地提升程序的健壮性.
在java中异常被当做对象来处理,根类是java.lang.throwable类,在java中定义了很多异常类(如outofmemoryerror、nullpointerexception、indexoutofboundsexception等),这些异常类分为两大类:error和exception.
error是无法处理的异常,比如outofmemoryerror,一般发生这种异常,jvm会选择终止程序。因此我们编写程序时不需要关心这类异常.
exception,也就是我们经常见到的一些异常情况,比如nullpointerexception、indexoutofboundsexception,这些异常是我们可以处理的异常.
exception类的异常包括checked exception和unchecked exception(unchecked exception也称运行时异常runtimeexception,当然这里的运行时异常并不是前面我所说的运行期间的异常,只是java中用运行时异常这个术语来表示,exception类的异常都是在运行期间发生的).
unchecked exception(非检查异常),也称运行时异常(runtimeexception),比如常见的nullpointerexception、indexoutofboundsexception。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定.
checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的ioexeption和sqlexception。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过.
在java中,异常类的结构层次图如下图所示:
在java中,所有异常类的父类是throwable类,error类是error类型异常的父类,exception类是exception类型异常的父类,runtimeexception类是所有运行时异常的父类,runtimeexception以外的并且继承exception的类是非运行时异常.
典型的runtimeexception包括nullpointerexception、indexoutofboundsexception、illegalargumentexception等.
典型的非runtimeexception包括ioexception、sqlexception等.
2、java中如何处理异常 。
在java中如果需要处理异常,必须先对异常进行捕获,然后再对异常情况进行处理。如何对可能发生异常的代码进行异常捕获和处理呢?使用try和catch关键字即可,如下面一段代码所示:
1
2
3
4
5
6
7
|
try
{
file file =
new
file(
"d:/a.txt"
);
if
(!file.exists())
file.createnewfile();
}
catch
(ioexception e) {
// todo: handle exception
}
|
。
被try块包围的代码说明这段代码可能会发生异常,一旦发生异常,异常便会被catch捕获到,然后需要在catch块中进行异常处理.
这是一种处理异常的方式。在java中还提供了另一种异常处理方式即抛出异常,顾名思义,也就是说一旦发生异常,我把这个异常抛出去,让调用者去进行处理,自己不进行具体的处理,此时需要用到throw和throws关键字。 。
下面看一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
main {
public
static
void
main(string[] args) {
try
{
createfile();
}
catch
(exception e) {
// todo: handle exception
}
}
public
static
void
createfile()
throws
ioexception{
file file =
new
file(
"d:/a.txt"
);
if
(!file.exists())
file.createnewfile();
}
}
|
这段代码和上面一段代码的区别是,在实际的createfile方法中并没有捕获异常,而是用throws关键字声明抛出异常,即告知这个方法的调用者此方法可能会抛出ioexception。那么在main方法中调用createfile方法的时候,采用try...catch块进行了异常捕获处理.
当然还可以采用throw关键字手动来抛出异常对象。下面看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
main {
public
static
void
main(string[] args) {
try
{
int
[] data =
new
int
[]{
1
,
2
,
3
};
system.out.println(getdatabyindex(-
1
,data));
}
catch
(exception e) {
system.out.println(e.getmessage());
}
}
public
static
int
getdatabyindex(
int
index,
int
[] data) {
if
(index<
0
||index>=data.length)
throw
new
arrayindexoutofboundsexception(
"数组下标越界"
);
return
data[index];
}
}
|
然后在catch块中进行捕获.
也就说在java中进行异常处理的话,对于可能会发生异常的代码,可以选择三种方法来进行异常处理:
1)对代码块用try..catch进行异常捕获处理; 。
2)在 该代码的方法体外用throws进行抛出声明,告知此方法的调用者这段代码可能会出现这些异常,你需要谨慎处理。此时有两种情况:
如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常.
如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理.
3)在代码块用throw手动抛出一个异常对象,此时也有两种情况,跟2)中的类似:
如果抛出的异常对象是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常.
如果抛出的异常对象是运行时异常,此方法的调用者可以选择地进行异常捕获处理.
(如果最终将异常抛给main方法,则相当于交给jvm自动处理,此时jvm会简单地打印异常信息) 。
3、深刻理解try,catch,finally,throws,throw五个关键字 。
下面我们来看一下异常机制中五个关键字的用法以及需要注意的地方.
1.try,catch,finally 。
try关键字用来包围可能会出现异常的逻辑代码,它单独无法使用,必须配合catch或者finally使用。java编译器允许的组合使用形式只有以下三种形式:
try...catch...; try....finally......; try....catch...finally... 。
当然catch块可以有多个,注意try块只能有一个,finally块是可选的(但是最多只能有一个finally块).
三个块执行的顺序为try—>catch—>finally.
当然如果没有发生异常,则catch块不会执行。但是finally块无论在什么情况下都是会执行的(这点要非常注意,因此部分情况下,都会将释放资源的操作放在finally块中进行).
在有多个catch块的时候,是按照catch块的先后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配.
在使用try..catch..finally块的时候,注意千万不要在finally块中使用return,因为finally中的return会覆盖已有的返回值。下面看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import
java.io.fileinputstream;
import
java.io.filenotfoundexception;
import
java.io.ioexception;
public
class
main {
public
static
void
main(string[] args) {
string str =
new
main().openfile();
system.out.println(str);
}
public
string openfile() {
try
{
fileinputstream inputstream =
new
fileinputstream(
"d:/a.txt"
);
int
ch = inputstream.read();
system.out.println(
"aaa"
);
return
"step1"
;
}
catch
(filenotfoundexception e) {
system.out.println(
"file not found"
);
return
"step2"
;
}
catch
(ioexception e) {
system.out.println(
"io exception"
);
return
"step3"
;
}
finally
{
system.out.println(
"finally block"
);
//return "finally";
}
}
}
|
这段程序的输出结果为:
可以看出,在try块中发生filenotfoundexception之后,就跳到第一个catch块,打印"file not found"信息,并将"step2"赋值给返回值,然后执行finally块,最后将返回值返回.
从这个例子说明,无论try块或者catch块中是否包含return语句,都会执行finally块.
如果将这个程序稍微修改一下,将finally块中的return语句注释去掉,运行结果是:
最后打印出的是"finally",返回值被重新覆盖了.
因此如果方法有返回值,切忌不要再finally中使用return,这样会使得程序结构变得混乱.
2.throws和thow关键字 。
1)throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,允许throws后面跟着多个异常类型; 。
2)一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出现在方法体中,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw出去。throw关键字的一个非常重要的作用就是 异常类型的转换(会在后面阐述道).
throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由方法去处理异常,真正的处理异常由此方法的上层调用处理.
四.在类继承的时候,方法覆盖时如何进行异常抛出声明 。
本小节讨论子类重写父类方法的时候,如何确定异常抛出声明的类型。下面是三点原则:
1)父类的方法没有声明异常,子类在重写该方法的时候不能声明异常; 。
2)如果父类的方法声明一个异常exception1,则子类在重写该方法的时候声明的异常不能是exception1的父类; 。
3)如果父类的方法声明的异常类型只有非运行时异常(运行时异常),则子类在重写该方法的时候声明的异常也只能有非运行时异常(运行时异常),不能含有运行时异常(非运行时异常).
5、异常处理和设计的几个建议 。
以下是根据前人总结的一些异常处理的建议:
1.只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程 。
谨慎地使用异常,异常捕获的代价非常高昂,异常使用过多会严重影响程序的性能。如果在程序中能够用if语句和boolean变量来进行逻辑判断,那么尽量减少异常的使用,从而避免不必要的异常捕获和处理。比如下面这段经典的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
void
useexceptionsforflowcontrol() {
try
{
while
(
true
) {
increasecount();
}
}
catch
(maximumcountreachedexception ex) {
}
//continue execution
}
public
void
increasecount()
throws
maximumcountreachedexception {
if
(count >=
5000
)
throw
new
maximumcountreachedexception();
}
|
上边的useexceptionsforflowcontrol()用一个无限循环来增加count直到抛出异常,这种做法并没有说让代码不易读,而是使得程序执行效率降低.
2.切忌使用空catch块 。
在捕获了异常之后什么都不做,相当于忽略了这个异常。千万不要使用空的catch块,空的catch块意味着你在程序中隐藏了错误和异常,并且很可能导致程序出现不可控的执行结果。如果你非常肯定捕获到的异常不会以任何方式对程序造成影响,最好用log日志将该异常进行记录,以便日后方便更新和维护.
3.检查异常和非检查异常的选择 。
一旦你决定抛出异常,你就要决定抛出什么异常。这里面的主要问题就是抛出检查异常还是非检查异常.
检查异常导致了太多的try…catch代码,可能有很多检查异常对开发人员来说是无法合理地进行处理的,比如sqlexception,而开发人员却不得不去进行try…catch,这样就会导致经常出现这样一种情况:逻辑代码只有很少的几行,而进行异常捕获和处理的代码却有很多行。这样不仅导致逻辑代码阅读起来晦涩难懂,而且降低了程序的性能.
我个人建议尽量避免检查异常的使用,如果确实该异常情况的出现很普遍,需要提醒调用者注意处理的话,就使用检查异常;否则使用非检查异常.
因此,在一般情况下,我觉得尽量将检查异常转变为非检查异常交给上层处理.
4.注意catch块的顺序 。
不要把上层类的异常放在最前面的catch块。比如下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
try
{
fileinputstream inputstream =
new
fileinputstream(
"d:/a.txt"
);
int
ch = inputstream.read();
system.out.println(
"aaa"
);
return
"step1"
;
}
catch
(ioexception e) {
system.out.println(
"io exception"
);
return
"step2"
;
}
catch
(filenotfoundexception e) {
system.out.println(
"file not found"
);
return
"step3"
;
}
finally
{
system.out.println(
"finally block"
);
//return "finally";
}
|
第二个catch的filenotfoundexception将永远不会被捕获到,因为filenotfoundexception是ioexception的子类.
5.不要将提供给用户看的信息放在异常信息里 。
比如下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
main {
public
static
void
main(string[] args) {
try
{
string user =
null
;
string pwd =
null
;
login(user,pwd);
}
catch
(exception e) {
system.out.println(e.getmessage());
}
}
public
static
void
login(string user,string pwd) {
if
(user==
null
||pwd==
null
)
throw
new
nullpointerexception(
"用户名或者密码为空"
);
//...
}
}
|
展示给用户错误提示信息最好不要跟程序混淆一起,比较好的方式是将所有错误提示信息放在一个配置文件中统一管理.
6.避免多次在日志信息中记录同一个异常 。
只在异常最开始发生的地方进行日志信息记录。很多情况下异常都是层层向上跑出的,如果在每次向上抛出的时候,都log到日志系统中,则会导致无从查找异常发生的根源.
7. 异常处理尽量放在高层进行 。
尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂.
8. 在finally中释放资源 。
如果有使用文件读取、网络操作以及数据库操作等,记得在finally中释放资源。这样不仅会使得程序占用更少的资源,也会避免不必要的由于资源未释放而发生的异常情况.
以上这篇老生常谈java异常处理和设计(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我.
最后此篇关于老生常谈Java异常处理和设计(推荐)的文章就讲到这里了,如果你想了解更多关于老生常谈Java异常处理和设计(推荐)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!