java程序员面试笔试宝典-4.6异常处理

4.6.1 finally块中的代码什么时候被执行?

问题:try{}里面有return语句,那么紧跟在try后面的finally{}中的代码是否会被执行?如果会的话什么时候被执行,在return之前还是之后?
任何语句要执行,都必须在return之前,除非碰到exit函数
如果try-finally或者catch-finally中都有return语句,那么finally块中的return语句将会覆盖别处的return语句,最终返回调用者的是finally中的return.

package cn.itcast.demo;

public class Test1 {
	public static int testFinally(){
		try {
			return 1;//在执行return语句前,确实执行了finally块中的代码
		} catch (Exception e) {
			return 0;
		}finally {
			System.out.println("execute finally");
		}
	}
	public static void main(String[] args) {
		int result = testFinally();
		System.out.println(result);
	}
}

在这里插入图片描述

package cn.itcast.demo;

public class Test2 {
	public static int testFinally(){
		try {
			return 1;//在执行return语句前,确实执行了finally块中的代码
		} catch (Exception e) {
			return 0;
		}finally {
			System.out.println("execute finally");
			return 3;//当finally块中有return语句时,将会覆盖函数中其他return语句
		}
	}
	public static void main(String[] args) {
		int result = testFinally();
		System.out.println(result);
	}
}

在这里插入图片描述
此外:由于在一个方法内部定义的变量存储在栈中,当这个函数结束后,其对应的栈就被回收,方法体中的变量不存在了。
因此return在返回时不是直接返回变量的值,而是复制一个然后返回。
对于基本数据类型的变量没有影响,引用类型的变量有影响

package cn.itcast.demo;
/*
 *  此外:由于在一个方法内部定义的变量存储在栈中,当这个函数结束后,其对应的栈就被回收,方法体中的变量不存在了。
	因此return在返回时不是直接返回变量的值,而是复制一个然后返回。
	对于基本数据类型的变量没有影响,引用类型的变量有影响
 */
public class Test2 {
	//对于基本数据类型的变量没有影响
	public static int testFinally1(){
		//由于在一个方法内部定义的变量存储在栈中
		//当这个函数结束后,其对应的栈就被回收,方法体中的变量不存在了
		//因此return在返回时不是直接返回变量的值,而是复制一个然后返回
		int result = 1;
		try {
			result = 2;
			return result;//在执行return语句前,确实执行了finally块中的代码
		} catch (Exception e) {
			return 0;
		}finally {
			result = 3;
			System.out.println("execute finally1");
		}
	}
	//引用类型的变量有影响
	public static StringBuffer testFinally2(){
		StringBuffer s = new StringBuffer("Hello");
		try {
			return s;
		} catch (Exception e) {
			return null;
		}finally {
			s.append(" World");
			System.out.println("execute finally2");
		}
	}
	public static void main(String[] args) {
		int resultVal = testFinally1();
		System.out.println(resultVal);
		StringBuffer resultRef = testFinally2();
		System.out.println(resultRef);
	}
}

在这里插入图片描述

说明:

程序在执行到return时,会首先将返回值存储在一个指定的位置,其次区执行finally块,最后再返回。
testFinally1调用return前,先把result=2存储在一个指定的位置,然后再执行finally中的代码,此时修改程序的值不会影响到程序的返回结果。
testFinally2调用return前,首先把s存储到一个指定的位置,由于s为引用类型,因此在finally中修改s将会修改返回的结果。

引申:出现在Java程序中的finally块是不是一定会被执行?

不一定

1)当程序在进入try语句块之前就出现异常,会直接结束,不会执行finally块中的代码

package cn.itcast.demo;
//1)当程序在进入try语句块之前就出现异常,会直接结束,不会执行finally块中的代码
public class Test3 {

	    public static void testFinally(){
	        int i = 5/0;
	        try {
	            System.out.println("try block");
	        } catch (Exception e) {
	            System.out.println("catch block");
	        } finally{
	            System.out.println("finally block");
	        }
	    }

	    public static void main(String[] args) {
	        testFinally();
	    }
	}

}

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at cn.itcast.demo.Test3.testFinally(Test3.java:6)
	at cn.itcast.demo.Test3.main(Test3.java:17)

2)当程序在try块中强制退出时也不会执行finally块中的代码

下例在try块中通过调用System.exit(0);强制退出了程序,导致finally块中的代码没有被执行

package cn.itcast.demo;
//2)当程序在try块中强制退出时也不会执行finally块中的代码
public class Test3 {

	    public static void testFinally(){
	        try {
	            System.out.println("try block");
	            System.exit(0);
	        } catch (Exception e) {
	            System.out.println("catch block");
	        } finally{
	            System.out.println("finally block");
	        }
	    }

	    public static void main(String[] args) {
	        testFinally();
	    }
	}

在这里插入图片描述
在这里插入图片描述

4.6.2 异常处理的原理是什么?

异常是指程序运行时(非编译时)所发生的非正常情况或错误,当程序违反了语义规则时,JVM就会将出现的错误表示为一个异常并抛出。
这个异常可以在catch块中进行捕获,然后进行处理。
而异常处理的目的是为了提高程序的安全性和鲁棒性。
Java语言把异常当作对象,并定义了一个基类(java.lang.Throwable)作为所有异常的父类。
在Java API中,已经定义了许多异常类,这些异常类分为Error(错误)和Exception(异常)两大类。

违反语义规则包括两种情况:

1)java类库内置的语义检查,例如数组下标越界:IndexOutOfBoundsException,当访问Null时,NullPointException;

2)Java允许开发人员扩展这种语义检查,创建自己的异常类,并选择在何时用throw关键字抛出异常。

4.6.3 运行时异常和普通异常有什么区别?

Error表示程序在运行期间发生了非常严重的错误,这种错误不可恢复,属于JVM层次错误,导致程序终止执行。
此外编译器不会检查Error是否被处理,不推荐捕获Error类型异常
因为这种异常多源于逻辑错误,是可以解决的,即一个正确的程序中不应该存在Error的。
例如:OutOfMemoryError,ThreadDeath

Exception表示可恢复的异常,是编译器可以捕捉的。分为检查异常和运行时异常。

1)检查异常:所有继承自Exception并且不是运行时异常的异常都是检查异常。

如IO异常和SQL异常
这种异常都发生在编译阶段,java编译器强制程序区捕获此类的异常,即把可能会出现这些异常的代码放在try块中,把对异常的处理放在catch块中。
这种异常一般在如下情况下使用:
a.异常的发生并不会导致程序的出错,进行处理后可以继续执行后续的操作。例如:当连接数据库失败后,可以重新连接后进行后续操作。
b.程序依赖于不可靠的外部条件,例如系统IO.

2)运行时异常:编译器没有强制对其进行捕获并处理。如果不进行处理,当出现此异常时,会有JVM来处理。

例如:NullPointException(空指针异常)
ClassCastException(类型转换异常)
ArrayIndexOutOfBoundsException(数组越界异常)
ArrayStoreException(数组存储异常)
BufferOverflowException(缓冲区 溢出异常)
ArithmeticException(算术异常)等。

出现运行时异常后,系统会把异常一直往上层抛,直到遇到处理代码为止。若没有处理代码则抛到最上层。
多线程就用Thread.run()方法抛出,单线程就用main()方法抛出
抛出之后,如果是线程,那么 这个线程也就退出了,如果是主程序抛出的异常,那么整个程序也就退出了。
所以,如果不对运行时的异常进行处理,后果非常严重,一旦发生,要么线程中止,要么主程序中止。

注意:

1)Java异常处理用到了多态的概念,如果在异常 处理过程中,先捕获了基类,然后再捕获子类,那么捕获的子类代码块将永远不会被执行。
因此,再进行异常捕获时,先捕获子类,再不会基类的异常信息。

正确的写法:
        try {
            //
        } catch (SQLException e) {
            //
        } catch (Exception e2) {
            //
        }

错误的写法:
        try {
            //
        } catch (Exception e) {
            //
        } catch (SQLException e2) {
            //
        }
  1. 尽早抛出异常,并对捕获的异常进行处理,对于运行时异常,实际上根本不需要进行处理。
    3)可以根据实际的需求自定义异常类,这些自定义的异常类只要继承自Exception类即可。
    4)异常能处理就处理,不能处理就抛出
    例1:下面程序能否编译通过,如果把ArithmeticException换成IOException呢
public class ExceptionTypeTest {

    public void dosomething() throws ArithmeticException{
        System.out.println("123");
    }

    public static void main(String[] args) {
        ExceptionTypeTest et = new ExceptionTypeTest();
        et.dosomething();
    }
}

能,ArithmeticException属于运行时异常,编译器没有强制必须捕获并处理
换为IOException不行,因为为检查异常,编译器强制捕获此类异常,否则会报错。

笔1:异常包含下列哪些内容?C

A:程序中的语法错误
B:程序中的编译错误
C:程序执行过程中遇到的事先没有预料到的情况
D:程序事先定义好的可能出现的意外情况。

笔2:下面关于异常的说法正确的是?D

A:一旦出现异常,程序运行就中止了
B:如果一个方法声明将抛出某个异常,它就必须真的抛出那个异常。
C:再Catch子句中匹配异常是一种精确匹配。
D:可能抛出系统异常的方法是不需要声明异常的。
Java 语言中所有错误或异常都是通过Throwable进行描述,而异常又分为两种,一种是可处理的也就是Exception,一种是不可处理的也就是Error.
Error:是一种无法处理的错误,比如运行一个系统中没有的一个class文件,这是无法处理的
而Exception又分成两种:一种是运行时异常(RuntimeException),一种是编译时运行(Exception)。(在写代码时发尽量发生在编译时)
RuntimeException:是需要调用者处理的,而这个异常程序员是无法处理的
Exception:是调用者无法处理的,因此需要程序员进行抛出或者进行try catch处理

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值