异常机制已经成为判断一门编程语言是否成熟的标准,异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。

Java异常机制主要依赖于try、catch、finally、throw、throws五个关键字。

  • 1.try:它里面放置可能引发异常的代码
  • 2.catch:后面对应异常类型和一个代码块,用于表明该catch块用于处理这种类型的代码块,可以有多个catch块。
  • 3.finally:主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件),异常机制总是保证finally块总是被执行。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者

    throw等终止方法的语句,则就不会跳回执行,直接停止。
  • 4.throw:用于抛出一个实际的异常,可以单独作为语句使用,抛出一个具体的异常对象。
  • 5.throws:用在方法签名中,用于声明该方法可能抛出的异常。

Java的异常分为两种,checked异常(编译时异常)和Runtime异常(运行时异常)。

  • java认为checked异常都是可以再编译阶段被处理的异常,所以它强制程序处理所有的checked异常,而Runtime异常无须处理,java程序必须显式处理checked异常,如果程序没有处理,则在编译时会发生错误,无法通过编译。

    -checked异常体现了java设计哲学:没有完善处理的代码根本不会被执行,体现了java的严谨性,

对于构造大型、健壮、可维护的应用系统而言,错误处理是整个应用需要考虑的重要方面。Java异常处理机制,在程序运行出现意外时,系统会生成一个Exception对象,来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性。

如果执行try块里的业务逻辑代码时出现异常,系统会自动生成一个异常对象,该异常对象被提交给运行环境,这个过程被称为抛出(throw)异常。Java环境收到异常对象时,会寻找合适的catch块,如果找不到,java运行环境就会终止,java程序将退出。

不同的catch块,视为了针对不同的异常类,提供不同的处理方法。


对于错误处理机制,主要有如下的两个缺点:

1.无法穷举所有异常情况:因为人类的知识是有限的,异常情况总比可以考虑到的情况多,总有漏网之鱼

2.错误处理代码和业务实现代码混杂严重影响程序的可读性,会增加程序维护的难度。

1.使用try...catch捕获异常

java提出了一种假设,如果程序可以顺利完成,那么一切正常,把系统的业务实现代码放在try块中定义,所有的异常处理逻辑放在catch块中进行处理。

即:try{

//业务实现代码

...

}

catch(Exception e){

输入不合法

}

上面的格式中try块和catch块后的{...}都是不可以省略的!

执行步骤:

1.如果执行try块中的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给java运行环境,这个过程称为抛出(throw)异常。

2.当java运行环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的cathc块并把该异常对象交给catch块处理,那这个过程称为捕获(catch)异常;如果java运行时环境找不到捕获异常的catch块,则运行时环境终止,jav程序也将退出。

注意:

注意1:不管程序代码块是否处于try块中,甚至包括catch块中代码,只要执行该代码时出现了异常,系统都会自动生成一个异常对象,如果程序没有为这段代码定义任何catch块,java运行环境肯定找不到处理该异常的catch块,程序肯定在此退出。

注意2:try块后可以有多个catch块,try块后使用多个catch块是为了针对不同异常类提供的不同的异常处理方式。当系统发生不同意外情况时,系统会生成不同的异常对象,java运行时就会根据该异常对象所属的异常类来决定使用哪个catch块来处理该异常。

注意3:通常情况下,如果try块被执行一次,则try块后只有一个catch块会被执行,绝不可能有多个catch块被执行,除非在循环中使用类continue开始下一次循环,下一次循环又重新运行了try块,这才可能导致多个catch块被执行。

注意4:进行异常捕获时,一定要记住先捕获小的异常,再捕获大的异常。

Java的异常类,以及他们的继承关系:

java把所有非正常情况分成两种:异常(Exception)和错误(Error),都是继承自Throwable父类。

Error错误:一般是指虚拟机相关的问题,如系统崩溃,虚拟机出错误等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常不处理。

Throwable():Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

    1.Error(错误):一般是指java虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误,因此应用程序不应该捕获Error对象,也无须在其throws子句中声明该方法抛出任何Error或其子类。  

   2.Exception:Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件
(1). SQLException:该异常提供关于数据库访问错误或其他错误的信息。
(2). RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
(3).IOException:此类为异常的通用类,它是由失败的或中断的 I/O 操作生成的。

异常对象包含的常用方法:

  1. getMessage();返回该异常的详细描述字符

  2. printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。

  3. printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定的输出流

  4. getStackTrace():返回该异常的跟踪栈信息。

 1 public class TestException
2 {
3
4 public static void main(String[] args)
5 {
6
7 try{
8 FileInputStream fis=new FileInputStream("a.txt");
9 }
10 catch(IOException ioe)
11 {
12 System.out.println(ioe.getMessage());
13 ioe.printStackTrace();
14 }
15
16 }
17
18 }

使用finally回收资源

有时候,程序在try块里面打开了一些物力资源(比如数据库连接,网络连接好磁盘文件等),这些物理资源都必须显式回收。

因为:java的垃圾回收机制不会回收任何的物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。

问题1:那么在哪边回收这些物理资源呢?

答:在finally块中,因为如果try块的某条语句引起一场,该语句后的其他语句通常不会被执行,那将导致位于该语句后的资源回收语句得不到执行;如果在catch块里进行资源回收,但catch块完全有可能得不到执行,这也将导致不能及时回收这些物理资源。所以我们不管try块中的代码是否出现异常,也不管哪个catch块会被执行,finally块总会被执行。

那么:java异常处理的完整语法结构如下:

try
{
//业务实现逻辑
...
}
catch(SubException e)
{
//异常处理快1
...
}
catch(SubException2 e)
{
//异常处理快2
...
}
...
finally
{
//资源回收块
...
}

以上的异常处理语法结构中:

注意:只有try块石必须的,也就是说如果没有try块,则不可能有后面的catch块和finally块;

**注意:**catch块和finally块都是可选的,但catch块和finally块至少出现其中之一,也可以同时出现;

注意:可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;

注意:不能只有try块,既没有catch块,也没有finally块;

注意:多个catch块必须位于try块之后,finally块必须位于所有catch块之后。

 1 import java.io.FileInputStream;
2 import java.io.IOException;
3
4 public class TestException
5 {
6
7 /**
8 * @param args
9 */
10 public static void main(String[] args)
11 {
12 // TODO Auto-generated method stub
13 FileInputStream fis = null;
14 try
15 {
16 fis = new FileInputStream("a.txt");
17 } catch (IOException ioe)
18 {
19 System.out.println(ioe.getMessage());
20 // return语句强制方法返回
21 return;
22 // 使用exit来退出虚拟机
23 // System.exit(1);
24 } finally
25 {
26 // 关闭磁盘文件,回收资源
27 if (fis != null)
28 {
29 try
30 {
31 fis.close();
32 } catch (IOException ioe)
33 {
34 ioe.printStackTrace();
35 }
36 }
37 System.out.println("程序已经执行了finally里德资源回收");
38 }
39 }
40
41 }

运行程序结果: a.txt (系统找不到指定的文件。) 程序已经执行了finally里德资源回收。

如果将catch块中的最后两句注释放入程序,那么结果为:a.txt (系统找不到指定的文件。)

以上两种情况显示:除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),否则不管在try块、catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总是会被执行的。不过,一般情况下,不要再finally块中使用renturn或throw等导致方法终止的语句,因为一旦使用,将会导致try块、catch块中的return、throw语句失效。

 1 public class TestException1
2 {
3
4 public static boolean test()
5 {
6 try
7 {
8 return true;
9 } finally
10 {
11 return false;
12 }
13 }
14
15 public static void main(String[] args)
16 {
17 boolean a = test();
18 System.out.println(a);
19 }
20
21 }

运行结果:false

以上的小程序说明:在finally块中定义了一个renturn false语句,这将导致try块中的return true 失去作用!

总结一下这个小问题:

当程序执行try块,catch块时遇到return语句或者throw语句,这两个语句都会导致该方法立即结束,所以系统并不会立即执行这两个语句,而是去寻找该异常处理流程中的finally块,如果没有finally块,程序立即执行return语句或者throw语句,方法终止。如果有finally块,系统立即开始执行finally块,只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句,如果finally块里也使用了return或throw等导致方法终止的语句,则finally块已经终止了方法,不用再跳回去执行try块、catch块里的任何代码了。

综上:尽量避免在finally块里使用return或throw等导致方法终止的语句,否则可能出现一些很奇怪的情况!


异常处理的嵌套

例如catch块中再次包含了一个完整的异常处理流程,这种在try块,catch块或finally块中包含完整的异常处理流程的情形称为异常处理的嵌套。异常处理流程的代码可以放在任何可执行代码的地方,因此完整的异常处理流程既可放在try块,也可放在catch块,也可放在finally块里。

嵌套的深度没有很明确的限制,通常没有必要写层次太深的嵌套异常处理,会导致程序可读性降低。


Checked异常和Runtime异常体系

Java异常被分为两大类:Checked异常和Runtime异常(运行时异常)。

所有RuntimeException类及其子类的实例被称为Runtime异常,不是RuntimeException类及其子类的异常实例则被称为Checked异常。

只有java语言提供了Checked异常,其他语言都没有提供,java认为Checked异常都是可以被处理(修复)的异常,所以java程序无须显式的处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误,无法通过编译。

Checked异常的处理方式:

  • ①:当方法明确知道如何处理异常,程序应该使用try…catch块来捕获该异常,然后在对应的catch块中修补该异常。
  • ②:当方法不知道如何处理异常,应该在定义该方法时声明抛出该异常。

Runtime异常无须显式声明抛出,如果程序需要捕捉Runtime异常,也可以使用try…catch块来捕获Runtime异常。

问题是:大部分的方法总是不能明确知道如何处理异常,这就只能声明抛出异常了。


使用throws抛出异常

使用throws抛出异常的思路是:当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理,如果main方法也不知道应该如何处理这种类型的异常,也可以使用使用throws声明抛出异常,该异常将交给JVM来处理。

JVM对异常的处理方法:打印异常跟踪栈的信息,并终止程序运行,所以有很多程序遇到异常后自动结束。

使用throws抛出异常的格式:

throws声明的抛出的语法格式紧跟在方法之后,可以声明多个异常类,多个异常类之间以逗号隔开。一旦使用了throws语句声明抛出异常,就不用再使用try…catch来捕获异常了。

如:throws ExceptionClass1,ExceptionClass2…

注意点1:如果某段代码调用了一个带throws声明的方法,该方法声明抛出了Checked异常,这表明该方法希望它的调用者来处理该异常。那么这段代码要么放在try块中显示捕获该异常,要么这段代码处于另一个带throws声明抛出的方法中。

举例如下:

 1 //方法一:
2
3 import java.io.FileInputStream;
4 import java.io.IOException;
5
6 public class TestException2
7 {
8
9 // test() 方法抛出了异常,那么test()方法的调用者要么放在try块中显示捕获该异常,要么这段代码处于另一个带throws声明抛出的方法中。
10
11 // 以下为后者的处理方法
12
13 public static void test() throws IOException
14 {
15 FileInputStream fis = new FileInputStream("a.txt");
16 }
17
18 public static void main(String[] args) throws Exception
19 {
20 test();
21 }
22
23 }
 1 //方法二:
2
3 import java.io.FileInputStream;
4 import java.io.IOException;
5
6 public class TestException2
7 {
8
9 public static void test() throws IOException
10 {
11 FileInputStream fis = new FileInputStream("a.txt");
12 }
13
14 public static void main(String[] args)
15 {
16 try
17 {
18 test();
19 } catch (IOException e)
20 {
21 // TODO Auto-generated catch block
22 e.printStackTrace();
23 }
24 }
25
26 }

使用throws声明抛出异常时有一个限制:就是方法重写时的“两小”中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或或相等,子类方法中不允许比父类方法声明抛出更多异常。即如果子类抛出的异常是父类抛出的异常的父类,那么程序无法通过编译。

因为Checked异常存在一些不便之处,大部分情况,可以使用Runtime异常,如果程序需要在合适的地方捕获异常,并对异常进行处理,程序一样可以用try…catch捕获Runtime异常。


使用throw抛出异常

当程序出现错误时,系统会自动抛出异常,另外,java也允许程序自行抛出异常,自行抛出异常使用throw语句完成!

抛出异常:

如果需要在程序中自行抛出异常,应使用throw语句,throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。throw语句的格式如下:throw ExceptionInstance;

throw语句抛出异常的两种情况:

  • 1.当throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把异常交给方法的调用者处理。
  • 2.当throw语句抛出的异常是Runtime异常,则该语句无须放在try块内,也无须放在带throws声明抛出的方法中,程序既可以显式使用try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给方法的调用者处理。

举例如下:

 1 public class TestException3
2 {
3
4 public static void throwChecked(int a) throws Exception
5 {
6 if (a < 0)
7 {
8 /**
9 * 自行抛出Exception异常 改代码必须处于try块里,或处于带throws声明的方法中
10 */
11 throw new Exception("a的值大于0,不符合要求");
12 }
13 }
14
15 public static void throwRuntime(int a)
16 {
17 if (a < 0)
18 {
19 /**
20 * 自行抛出RuntimeException异常,既可以显式捕获该异常 也可以完全不用理会该异常,把该异常交给方法的调用者处理
21 */
22 throw new RuntimeException("a的值大于0,不符合要求");
23 } else
24 {
25 System.out.println("a的值为:" + a);
26 }
27 }
28
29 public static void main(String[] args)
30 {
31 try
32 {
33 /**
34 * 此处调用了带throws声明的方法,必须显示捕获该异常(使用try...catch) 否则,要在main方法中再次声明抛出
35 */
36 throwChecked(-3);
37 } catch (Exception e)
38 {
39 System.out.println(e.getMessage());
40 }
41 throwRuntime(3);
42 }
43
44 }

由上面的代码显式:自行抛出Runtime异常比自行抛出Checked异常的灵活性更好。


自定义异常类

用户自定义异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。

应以异常类通常需要提供两种构造器:一个是无参数的构造器,另一个是带一个字符串的构造器,这个字符串将作为该异常对象的详细说明(也就是异常对象的getMessage方法的返回值)。

通常情况下,程序会很少自行抛出系统异常,因为异常的类名通常包含了该异常的有用信息,所以在选择抛出什么异常时,应该选择合适的异常类,从而可以明确地描述异常情况,这样程序常常需要定义异常类。

用户定义异常类,需要基础Exception基类,如果希望定义RuntimeException基类,就应该继承该基类,定义异常类时通常需要提供两种构造器:1,无参的构造器,****2,带字符串的构造器,这个字符串作为该异常对象的详细说明,(也就是异常对象的getMessage方法返回值),调用super将字符串参数传给异常对象的message属性,message属性就是异常对象的详细描述信息。

例子如下:

 1 public class TestException4 extends Exception
2 {
3
4 public TestException4()
5 {
6
7 }
8
9 public TestException4(String msg)
10 {
11 super(msg);
12 }
13
14 }

catch和throw同时使用

前面已有两种异常处理方法:

  • 1.在异常出现的方法内捕获并处理,方法的调用者将不能再次捕获该异常。
  • 2.该方法签名中声明抛出该异常,将该异常完全交给方法调用者处理。

但是在实际应用中往往需要更复杂的处理方式,即异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应该再次抛出异常,可以让该方法的调用者也能捕获到异常。

为了实现这种靠多个方法协作处理同一个异常的情形,可以通过catch块中结合throw来完成。

举例catch和throw同时使用的例子:

 1 public class TestException4
2 {
3 // 以下AuctionException这个异常是自定义的异常类
4 private double initPrice = 30.0;
5
6 public void bid(String bidPrice) throws AuctionException
7 {
8 double d = 0.0;
9 try
10 {
11 d = Double.parseDouble(bidPrice);
12 } catch (Exception e)
13 {
14 e.printStackTrace();
15 throw new AuctionException("竞拍价必须是数值,不能包含其他字符!");
16 }
17 if (initPrice > d)
18 {
19 throw new AuctionException("竞拍价比起拍价低,不允许竞拍!");
20 }
21 initPrice = d;
22 }
23
24 public static void main(String[] args)
25 {
26 TestException4 ta = new TestException4();
27 try
28 {
29 ta.bid("df");
30 } catch (AuctionException ae)
31 {
32 // TODO: handle exception
33 System.err.println(ae.getMessage());
34 }
35 }
36 }

catch和throw同时使用来处理异常的方法是在大型企业中比较常用的。


java的异常跟踪栈

异常对象的printStackTrace方法用于打印异常的跟踪栈信息,根据printStackTrace方法的输出结果,我们可以找到异常的源头,并跟踪到异常一路触发的过程。

虽然printStackTrace()方法可以很方便地追踪异常的发生状况,可以用它来调试,但是在最后发布的程序中,应该避免使用它。而应该对捕获的异常进行适当的处理,而不是简单的将信息打印出来。


原文地址:

http://blog.csdn.net/qq_37267015/article/details/76999764?ref=myread

Java中的异常处理机制《》的更多相关文章

  1. Java 中的异常处理机制

    生活中的异常:  不能够完整而顺利的完成一些工作 根据不同的异常进行相应的处理,而不会就此终端我们的生活 引出:  异常处理: 方式:  1.选择结构(逻辑判断)避免 demo:if逻辑处理异常 im ...

  2. Java中的异常处理机制的简单原理和应用?

    程序运行过程中可能出现各种"非预期"情况,这些非预期情况可能导致程序非正常结束. 为了提高程序的健壮性,Java提供了异常处理机制: try { s1... s2... s3... ...

  3. 16、java中的异常处理机制

    异常:就是程序在运行时出现不正常情况.异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述.并封装成对象. 其实就是java对不正常情况进行描述后的对象体现. 对于问题的划 ...

  4. java中的异常处理机制

    java异常处理机制 1)在java语言中,通常将可能出现异常的语句放入try{}语句中,将出现错误后需要执行的语句放入到catch{}语句中,将无论是否发生异常都要执行的语句放在finally{}语 ...

  5. Java中的异常处理机制的简单原理和应用

    异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间.地点.人物.情节等信息,可以用一个对象来表示,Java使用面向对象的方式 ...

  6. java中的异常处理机制_函数覆盖时的异常特点

    /*注意:异常声明在函数上 异常在子父类覆盖时的体现1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者异常的子类2.如果父类方法抛出多个异常,那么子类在覆盖该方法 ...

  7. 【Java面试题】21 Java中的异常处理机制的简单原理和应用。

    异常指Java程序运行时(非编译)所发生的非正常情况或错误. java对异常进行了分类,不同类型的异常使用了不同的java类,所有异常的根类为java.lang.Throwable.Throwable ...

  8. java中的异常处理机制_finally的使用

    finally总结: finally代码块:定义一定执行的代码 通常用于关闭资源或者某些一定执行的代码 实例1:finally功能演示 class FuShuException extends Exc ...

  9. 43 java中的异常处理机制的简单原理和应用

随机推荐

  1. python的二维数组操作--坑

    用到python list的二维数组,发现有一些需要注意的地方. 第一种赋值方法: list0 = [[0]*3]*4 list0[0][1] = 1 print(list0) 输出结果为: [[0, ...

  2. 各开源协议BSD、Apache Licence 2.0、GPL

    以下是上述协议的简单介绍:BSD开源协议BSD开源协议是一个给于使用者很大自由的协议.基本上使用者可以"为所欲为",可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有 ...

  3. Java通过jni调用动态链接库

    (1)JNI简介 JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++).从Java1.1开始,JNI标准成为java ...

  4. Spring Boot -- 外部配置的属性使用

    Spring Boot允许使用propertities文件.yaml文件或者命令行参数作为外部配置. 命令行参数配置 Spring Boot可以基于jar包运行,打成jar包的程序可以直接通过下面的命 ...

  5. 第1章 什么是JavaScript

    目录 1. JavaScript实现 1.1 ECMAScript 1.2 DOM 1.3 BOM 1995年JavaScript问世时主要用途时代替Perl等服务器段语言处理输入验证 1. Java ...

  6. 你一定需要知道的高阶JAVA枚举特性!

    JAVA枚举,比你想象中还要有用! 我经常发现自己在Java中使用枚举来表示某个对象的一组潜在值. 在编译时确定类型可以具有什么值的能力是一种强大的能力,它为代码提供了结构和意义. 当我第一次了解枚举 ...

  7. IntelliJ IDEA启动界面的秘密:当编程遇到艺术

    细心的同学会发现Intellij IDEA每次发版本的时候都会有不同的启动界面背景,都很比较抽象的艺术图像. JetBrains的其它产品也有自己独特的设计. 但是这背后是怎么实现的.有什么寓意却很少 ...

  8. 一文搞懂MySQL前缀索引

    引入 通常在开发中我们需要定义字符串类型的字段,例如用户名或者用户邮箱等. 假设我们在维护一个用户登录系统,用户表的定义: create table User( ID bigint unsigned ...

  9. 无限重置IDE过期时间插件 亲测可以使用

    相信破解过IDEA的小伙伴,都知道jetbrains-agent这个工具,没错,就是那个直接拖入到开发工具界面,一键搞定,so easy的破解工具!这个工具目前已经停止更新了,尽管还有很多小伙伴在使用 ...

  10. 温故而知新--day2

    温故而知新--day2 类 类与对象 类是一个抽象的概念,是指对现实生活中一类具有共同特征的事物的抽象.其实列化后称为对象.类里面由类属性组成,类属性可以分为数据属性和函数属性(函数属性又称为类方法) ...