【Java】理解异常 异常处理

异常处理

Error 和Exception

ExceptionError, ⼆者都是Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。均继承自Throwable类。

Error表⽰系统级的错误, 是java 运⾏环境内部错误或者硬件问题, 不能指望程序来处理这样的问题, 除了退出运⾏外别⽆选择, 它是Java 虚拟机抛出的。

Exception 表⽰程序需要捕捉、需要处理的常, 是由与程序设计的不完善⽽出现的问题, 程序必须处理的问题。

异常类型

Java 中的异常, 主要可以分为两⼤类, 即受检异常(checked exception)非受检异常( unchecked exception)

受检异常

对于受检异常来说, 如果⼀个⽅法在声明的过程中证明了其要有受检异常抛出:

public void test() throws new Exception{ }

那么,当我们在程序中调⽤他的时候, ⼀定要对该异常进⾏处理( 捕获或者向上抛出),否则是⽆法编译通过的。这是⼀种强制规范。

这种异常在IO操作中⽐较多。⽐如FileNotFoundException, 当我们使⽤IO流处理⼀个⽂件的时候, 有⼀种特殊情况, 就是⽂件不存在。 所以 在⽂件处理时他会显⽰抛出FileNotFoundException, ⽬的就是告诉这个⽅法的调⽤者,这个⽅法不保证⼀定可以成功, 是有可能找不到对应的⽂件的,你要明确的对这种情况做特殊处理。

当我们希望我们的⽅法调⽤者, 明确的处理⼀些特殊情况的时候, 就应该使⽤受检异常。

非受检异常

对于⾮受检异常来说, ⼀般是运⾏时异常, 继承⾃RuntimeException。在编写代码的时候, 不需要显⽰的捕获,但是如果不捕获, 在运⾏期如果发⽣异常就会中断程序的执⾏。

这种异常⼀般可以理解为是代码原因导致的。⽐如发⽣空指针、数组越界等。所以,只要代码写的没问题, 这些异常都是可以避免的。也就不需要我们显⽰的进⾏处理。试想⼀下, 如果你要对所有可能发⽣空指针的地⽅做异常处理的话, 那相当于你的所有代码都需要做这件事。

异常相关关键字

try⽤来指定⼀块预防所有异常的程序;

catch⼦句紧跟在try 块后⾯, ⽤来指定你想要捕获的异常的类型;

finally 为确保⼀段代码不管发⽣什么异常状况都要被执⾏;

throw 语句⽤来明确地抛出⼀个异常;

throws⽤来声明⼀个⽅法可能抛出的各种异常;

正确处理异常

异常的处理⽅式有两种:

1、⾃⼰处理(捕获)。

2、向上抛,交给调⽤者处理。

异常,千万不能捕获了之后什么也不做,或者只是使⽤e.printStacktrace。

处理异常的原则:自己明确知道如何处理的,就处理掉;不知道如何处理的,就交给调⽤者处理。

自定义异常

⾃定义异常就是开发人员⾃⼰定义的异常, ⼀般通过继承Exception 的⼦类的⽅式实现。

编写⾃定义异常类实际上是继承⼀个API 标准异常类,⽤新定义的异常处理信息覆盖原有信息的过程。这种⽤法在Web 开发中也⽐较常见,一般可以⽤来⾃定义业务异常。如余额不⾜、重复提交等。这种⾃定义异常有业务含义,更容易让上层理解和处理

异常链

“异常链”是Java 中⾮常流⾏的异常处理概念, 是指在进⾏⼀个异常处理时抛出了另外⼀个异常, 由此产⽣了⼀个异常链条。

该技术⼤多⽤于将“ 受检查异常” ( checked exception) 封装成为“⾮受检查异常”( unchecked exception)或者RuntimeException。

并且要注意的是,如果因为异常你决定抛出⼀个新的异常,新的异常⼀定要包含原有的异常,这样, 处理程序才可以通过getCause()initCause()⽅法来访问异常最终的根源。

从Java 1.4 版本开始,几乎所有的异常都支持异常链。

以下是Throwable 中支持异常链的方法和构造函数。

Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)

initCauseThrowable 构造函数的Throwable 参数是导致当前异常的异常。getCause 返回导致当前异常的异常,initCause 设置当前异常的原因。

以下示例显示如何使用异常链:

try {
    //...
} catch (IOException e) {
	throw new SampleException("Other IOException", e);
}

上面的示例中,当捕获到IOException 时,将创建一个新的SampleException 异常,并附加原始的异常原因,并将异常链抛出到下一个更高级别的异常处理程序。

try-with-resources

Java 里,对于文件操作IO 流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close 方法将其关闭,否则资源会一直处于打开状态,这样可能会导致内存泄露等问题。

关闭资源的常用方式就是在finally块里是释放,即调用close方法。比如,我们经常
会写这样的代码:

public static void main(String[] args) {
	BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\123.txt"));
        while ((line = br.readLine()) != null) {
        	System.out.println(line);
    	}
	} catch (IOException e) {
		// handle exception
	} finally {
        try {
            if (br != null) {
                br.close();
        	}
		} catch (IOException ex) {
        // handle exception
        }
    }
}

从Java 7 开始,jdk 提供了一种更好的方式关闭资源,使用try-with-resources语句,改写一下上面的代码,效果如下:

public static void main(String... args) {
    try (BufferedReader br = new BufferedReader(new FileReader("d:\\123.txt"))) {
    	String line;
        while ((line = br.readLine()) != null) {
        	System.out.println(line);
        }
    } catch (IOException e) {
    	// handle exception
    }
}

finally 和return 的执行顺序

try()⾥⾯有⼀个return语句, 那么后⾯的finally{}⾥⾯的code 会不会被执⾏, 什么时候执⾏, 是在return 前还是return 后?

如果try中有return语句, 那么finally中的代码还是会执⾏。因为return表⽰的是
整个⽅法体返回, 所以,finally 中的语句会在return 之前执⾏。

但是return 前执行的finally 块内,对数据的修改效果对于引用类型和值类型会不同:

//测试修改值类型
static int f() {
    int ret = 0;
    try {
    	return ret; // 返回0,finally 内的修改效果不起作用
    } finally {
        ret++;
        System.out.println("finally 执行");
    }
} 
// 测试修改引用类型
static int[] f2(){
    int[] ret = new int[]{0};
    try {
    	return ret; // 返回[1],finally 内的修改效果起了作用
    } finally {
        ret[0]++;
        System.out.println("finally 执行");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值