七、异常
1. Throwable的子类包含哪两类?简述Java Error类与Exception类的区别。
Throwable的子类包含Error类和Exception类。
Error: 致命异常。标识系统发生了不可控的错误。程序无法处理,只能人工介入。例如, 虚拟机产生的错误StackOverflowError、OutOfMemoryError。
Exception: 非致命异常。程序可处理。分为受编译器检测的checked异常(受检异常)和不受编译器检测的unchecked异常(非受检异常)。
2. Exception又分为checked异常和unchecked异常,请分别举例说明。
派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。
区别1:
checked exception 必须被显式的抛出或者捕获,比如FileNotFoundException,是一个必须处理的异常。
unchecked exception 不用被显式的抛出或者捕获,比如NullPointerException。
区别2:
checked exception继承Exception类
unchecked exception 继承RuntimeException类
3.请查阅资料,简述StackOverflowError和OutOfMemoryError两类错误的发生情形和原因。
1、线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常。
递归可能造成StackOverflowError;不断创建线程可能造成StackOverflowError
2、栈的深度(大小类似于弹夹深度)可以自动扩展,扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
3、虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError 和OutOfMemoryError
异常。
4、如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。
5、当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。
6、当常量池(方法区的一部分)无法再申请到内存时会抛出OutOfMemoryError 异常。
7、各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。
4.简述异常处理的两种方式,并举例说明区别。
声明抛出处理:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
程序捕获处理:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
public class Test {
public static void main(String[] args) { // doSome()可能抛出编译时异常,需要预先处理,2种处理方式
// 第一种:方法声明位置上继续上抛,添加throws ClassNotFoundException
// 第二种:使用try..catch进行捕捉
try {
doSome();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// ClassNotFoundException属于编译时异常,直接父类是Exception,没有通过RuntimeException父类
private static void doSome() throws ClassNotFoundException { //假设这个方法执行过程中会出现ClassNotFoundException异常
System.out.println("doSome!!!");
}
}
5.选取RuntimeException类的五个子类,编写抛出并捕获上述子类异常的程序。(例如算术异常, 空指针异常,类转换异常,数组越界异常等)
1.
EmptyStackException类:
抛出:
package test;
import java.util.*;
public class test1 {
public static void main(String[] args) throws EmptyStackException{
Stack st=new Stack();
Object ob=st.pop();
}
}
/*
Exception in thread "main" java.util.EmptyStackException
at java.util.Stack.peek(Stack.java:102)
at java.util.Stack.pop(Stack.java:84)
at test.test1.main(test1.java:8)
*/
捕获:
package test;
import java.util.*;
public class test1 {
public static void main(String[] args) {
try {
Stack st=new Stack();
Object ob=st.pop();
}
catch(EmptyStackException e) {
System.out.println("空栈异常");
}
}
}
//空栈异常
2.
ArithmeticException类:
抛出:
package test;
public class test1 {
public static void main(String[] args) {
int a,b,c;
a=1;
b=0;
c=a/b;
System.out.println(c);
}
}
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at test.test1.main(test1.java:8)
*/
捕获:
package test;
public class test1 {
public static void main(String[] args) {
try{
int a,b,c;
a=1;
b=0;
c=a/b;
System.out.println(c);
}
catch(ArithmeticException e) {
System.out.println("除数为0");
}
}
}
//除数为0
3.
NullPointerException类:
抛出:
package test;
public class test1 {
private static int[] x;
public static void main(String[] args) {
System.out.println(x[0]);
}
}
/*
Exception in thread "main" java.lang.NullPointerException
at test.test1.main(test1.java:6)
*/
捕获:
package test;
public class test1 {
private static int[] x;
public static void main(String[] args) {
try {
System.out.println(x[0]);
}
catch(NullPointerException e) {
System.out.println("空指针异常");
}
}
}
//空指针异常
4.
ArrayIndexOutOfBoundsException类:
抛出:
package test;
public class test1 {
public static void main(String[] args) {
String foo=args[1];
System.out.println("foo="+foo);
}
}
/*
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at test.test1.main(test1.java:5)
*/
捕获:
package test;
public class test1 {
public static void main(String[] args) {
try{
String foo=args[1];
System.out.println("foo="+foo);
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界");
}
}
}
//数组下标越界
5.
NegativeArraySizeException类:
抛出:
package test;
public class test1 {
public static void main(String[] args) {
int[] a;
a=new int[-1];
}
}
/*
Exception in thread "main" java.lang.NegativeArraySizeException
at test.test1.main(test1.java:6)
*/
捕获:
package test;
public class test1 {
public static void main(String[] args) {
try{
int[] a;
a=new int[-1];
}
catch(NegativeArraySizeException e) {
System.out.println("负数组长度异常");
}
}
}
//负数组长度异常
6.根据某业务场景自定义一个异常类,并在某场景下抛出该异常对象。
package test;
public class test1 extends Exception{
public test1(String msg) {
super(msg);
}
static void throwOne() throws test1{
boolean exception=true;
if(exception) {
throw new test1("异常");
}
}
public static void main(String[] args) {
try {
throwOne();
}
catch(test1 e) {
e.printStackTrace();
}
}
}
/*
test.test1: 异常
at test.test1.throwOne(test1.java:11)
at test.test1.main(test1.java:17)
*/
7.异常中的throws声明与throw语句的区别是什么?请举例说明。
用户程序自定义的异常和应用程序特定的异常,必须借助于throws和throw语句来定义抛出异常。
1、throw是语句抛出一个异常。
语法:throw (异常对象);
throw e;
2、throws是方法可能抛出异常的声明,用在声明方法时,表示该方法可能要抛出异常。
语法:[(修饰符)](返回值类型)(方法名)([参数列表])[throws(异常类)]{......}
public void doA(int a) throws Exception1, Exception2{……}
举例:
throws E1,E2,E3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常E1,E2,E3可能是该函数体产生的。throw则是明确了这个地方要抛出这个异常。
如:
public void doA(int a) throws IOException {
try {
……
} catch (Exception1 e) {
throw e;
} catch (Exception2 e) {
System.out.println("出错了!");
}
if (a != b)
throw new Exception3("自定义异常");
}
(1)throw语句用在方法体内,表示抛出异常,由方法体内的语句处理;throws语句用在方法声明后面,表示再抛出异常,由该方法的调用者来处理。
(2)throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常;throw是具体向外抛异常的动作,所以它是抛出一个异常实例。
(3)throws出现在方法函数头;而throw出现在函数体。
(4)throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。
(5)两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
8. finally子句的作用是什么?