当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 >
java编程中遇到的异常以及异常的一些处理
时间:2018-06-22作者:华清远见

n 异常的概念

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?

比如除法运算、读写文件操作,都可能发生异常。(当除数为0时;文件路径不存在时)。该如何处理?

C语言的风格:用函数返回值作为执行状态。比如返回一个为0的值表示文件不存在这个状态。缺点是代码比较散乱。

而Java语言提供了一种优秀的解决办法:异常处理机制。java将处理异常的代码放到一个统一的 try-catch-finally结构中去处理,且代码易读。请看下面的例子

Ø 示例一

数学运算之除0异常

import java.util.Scanner;

public class AllDemo {

public static void main(String[] args) {

System.out.println("----欢迎使用命令行除法计算器----");

Scanner scan = new Scanner(System.in);

int num1 = scan.nextInt();

int num2 = scan.nextInt();

int result = a(num1,num2);

System.out.println("result:" + result);

scan.close();

}

private static int a(int num1, int num2) {

return b(num1,num2);

}

private static int b(int num1, int num2) {

return devide(num1,num2);

}

public static int devide(int num1, int num2) {

return num1 / num2;

}

}

执行时输入7和0,会看到控制台结果

----欢迎使用命令行除法计算器----

7

0

Exception in thread "main" java.lang.ArithmeticException: / by zero

at AllDemo.devide(AllDemo.java:24)

at AllDemo.b(AllDemo.java:20)

at AllDemo.a(AllDemo.java:16)

at AllDemo.main(AllDemo.java:9)

分析:当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,于是调用它的函数b也发生了异常,于是调用b的函数a也发生异常,于是调用a的函数main也发生了异常,这样一直向调用栈的栈底回溯,这叫做异常的冒泡。这个例子没有使用异常处理机制,异常最终由main函数抛给java虚拟机,导致程序终止。

Ø 示例二

读写文件异常

import java.io.FileInputStream;

import java.io.IOException;

class ReadFile {

public static void testException() throws IOException

{

//FileInputStream的构造函数会抛出FileNotFoundException

FileInputStream fileIn = new FileInputStream("E:\\a.txt");

int word;

while((word = fileIn.read())!=-1) //read方法会抛出IOException

{

System.out.print((char)word);

}

fileIn.close(); //close方法会抛出IOException

}

public static void main(String arg[]) throws IOException{

ReadFile.testException();

}

}

说明:

1、如果E盘下没有文件a.txt,发生FileNotFoundException。

2、如果a.txt存在,但是被其它进程锁住,有可能发生IOException

3、鉴于1、2,此处为了编译正确,所以只得在testException()加上“throws IOException”,调用它的main()也得加上“throws IOException”。(注:FileNotFoundException是的IOException的子类)

n java异常处理的try-catch-finally结构

我们可以使用异常处理结构改进上面读文件的例子

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

class ReadFile {

public static void testException() {

FileInputStream fileIn = null;

try {

// FileInputStream的构造函数会抛出FileNotFoundException

fileIn = new FileInputStream("E:\\a.txt");

} catch (FileNotFoundException e) {

e.printStackTrace();

System.out.println(e.getMessage());

}

int word;

try {

while ((word = fileIn.read()) != -1) // read方法可抛出IOException

{

System.out.print((char) word);

}

fileIn.close(); // close方法可抛出IOException

} catch (IOException e) {

e.printStackTrace();

} finally {

System.out.println("finally块无论如何都要执行");

}

}

public static void main(String arg[]) {

ReadFile.testException();

}

}

说明

1、如果E盘下没有文件a.txt,发生FileNotFoundException。进入catch结构,打印出一些信息。

2、程序仍然往下能继续运行,在fileIn.read()时发生异常NullPointerException,这是因为第1步进了catch块,这样fileIn变量仍是最初的null值,一旦调函数便发生NullPointerException。

3、问:我们针对NullPointerException为什么不需要写try-catch-finally结构?具体原因请阅读后面的RuntimeException内容

4、这个例子中故意为了演示出NullPointerException,而把try-catch块写成了两个,实际上可以合并。

n try-catch-finally结构说明

1、一个try可以对应多个catch块。

2、如果发生异常,异常被抛给第一个catch 块,如果异常的类型与 catch匹配,它在这里就会被捕获。如果不匹配,它会被传递给第二个 catch 块。如此,直到异常被捕获或者通过所有的 catch 块。

3、finally块始终会被执行。

4、如果try或catch块中存在return语句,那么catch、finally块中的语句也会被执行完了后,才真正return。除非遇到下面的几种情况(1)System.exit(n)可导致立即终止(2)finally块中发生异常(3)程序所在线程死亡(4)关闭CPU。

练习

class Snippet {

public static String t() {

String s = "1";

try {

s = "2";

return s;

//throw new Exception("some");

} catch (Exception e) {

s = "3";

System.out.println("in catch block");

return s;

}finally {

s = "4";

System.out.println("in finally block");

return s;

}

}

public static void main(String[] args) {

String s= t();

System.out.println(s);

}

}

//无论如何,都要进到finally块执行,如果finally{}里有return 那么返回的肯定是finally里的

Java异常类图

java异常

从上面的示例代码可以看出,java把异常也看成是对象。进而设计了下面所示的异常方面的类结构体系。最上层类叫做Throwable。可以把这些分成两大种类

Ø 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求程序员必须处理这些异常。在运行阶段,倘若发生Error则虚拟机几乎崩溃,倘若发生RuntimeException若程序员没处理它则一直回溯向上抛给java虚拟机处理。当然,如果程序员愿意的话,也可以编写代码处理(使用try…catch…finally)这样的异常(但是通常情况下不会这样做。需要这样做的情况是比如搞数学运算的这个专业领域要处理ArithmeticException)。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。这种异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

Ø 检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

n 常见异常类型说明

异常类型说明

Exception 异常层次结构的父类

ArithmeticException算术错误情形,如以零作除数

ArrayIndexOutOfBoundsException数组下标越界

NullPointerException尝试访问 null 对象成员

ClassNotFoundException不能加载所需的类

IllegalArgumentException方法接收到非法参数

ClassCastException对象强制类型转换出错

NumberFormatException数字格式转换异常,如把"abc"转换成数字

n throws关键字

声明本方法不处理异常,让调用者处理。

在函数签名中使用throws 声明交给函数调用者caller去解决。

import java.util.Scanner;

public class HelloWorld {

public static void main(String[] args) {

try {

divide();//调用的此方法抛出了异常

}

catch (Exception e) {

System.out.println("错误:被除数和除数必须是整数,且除数不能为零。");

e.printStackTrace();

e.getMessage();//暂时打印不出任何内容

}

finally{

System.out.println("感谢使用本程序");

}

}

public static void divide() throws Exception{//声明异常

Scanner scanner = new Scanner(System.in);

System.out.println("请输入被除数");

int num1 = scanner.nextInt();//有可能异常InputMismatchException

System.out.println("请输入除数");

int num2 = scanner.nextInt();//有可能异常InputMismatchException

System.out.println(num1 / num2);//有可能异常除数等于0:ArithmeticException

}

}

n throw关键字

主动抛出异常

如果对于具体的一些处理逻辑,程序员也可以主动的抛出异常让 外层处理。(此异常可能是程序员自己定义的)。

class Person {

private String name;

private String sex = "男";

public void setSex(String sex) throws Exception{

if("男".equals(sex) || "女".equals(sex)){

this.sex = sex;

}

else{

//主动抛出异常。也可做成自定义异常并抛出

throw new Exception("性别输入错误,必须是男或女");

}

}

public void print() {

System.out.println(this.name + this.sex);

}

}

class Test {

public static void main(String[] args) {

Person p = new Person();

try {

p.setSex("male");

p.print();

} catch (Exception e) {

e.printStackTrace();

}

}

}

n 异常类的两个打印异常信息的好办法

一般,我们需要打印出异常的相关信息。在Exception类中,定义了下面两个方法,

e.printStackTrace();//打印调用堆栈信息 并把e.getMessage()的信息也打出来了

e.getMessage();//打印异常的相关信息

与IOException类相似,我们自己定义的异常类往往也是Exception的子类,我们可以(1)覆盖e.getMessage()方法; 也可以(2)构造的时候传入具体的字符串信息,因为e.getMessage()就是获取这个信息

示例

public class Test {

public static void main(String[] args) {

try {

Exception e = new Exception("哈哈,我是异常");

throw e;

} catch (Exception e) {

e.printStackTrace();

System.out.println("message="+ e.getMessage());

}

}

}

n 建议

(1)多重catch块:Catch块的排列顺序必须是从子类到父类。最后一个一般是Exception。

(2)不要在fianlly中使用return。

不要在finally中抛出异常。

减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。

将尽量将所有的return写在函数的最后面,而不是try … catch … finally中。

n 练习

import java.util.InputMismatchException;

import java.util.Scanner;

public class HelloWorld {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

int num1=1 , num2=1;

try {

System.out.println("请输入被除数");

num1 = scanner.nextInt();//有可能异常InputMismatchException

System.out.println("请输入除数");

num2 = scanner.nextInt();//有可能异常InputMismatchException

System.out.println(String.format("%d / %d = %d", num1, num2, num1 / num2));//有可能异常除数等于0:ArithmeticException

}

catch(InputMismatchException e)

{

System.err.println("被除数和除数必须是整数");

return;

}

catch(ArithmeticException e)

{

System.err.println("除数不能为零");

//return;

System.exit(0);//这个是立即终止

}

catch (Exception e) {

System.out.println("错误:被除数和除数必须是整数,且除数不能为零。");

e.printStackTrace();

e.getMessage();//暂时打印不出任何内容

}

finally{

System.out.println("感谢使用本程序");

}

}

}


发表评论

全国咨询电话:400-611-6270,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(3337544669), 徐老师QQ(1462495461), 刘老师 QQ(3108687497)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2018 华清远见教育集团 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号