Bootstrap

Java第7章作业:异常

第一题:简述Java Error类与Exception类的区别。

Error类:Error类是程序本身无法处理的错误,一般与虚拟机有关,如系统崩溃,虚拟机错误,内存空间不足等错误,出现这种错误时需要停止程序并修正。

Exception类:异常可以靠程序本身进行处理,即使用一定的方法对异常进行捕获或抛出,而不需要终止程序运行。

第二题:简述异常处理的两种方式,并举例说明区别。

异常处理有两种方式,分别是声明抛出和捕获。声明抛出分为隐式声明抛出和显式声明抛出,捕获则使用try...catch...finally语句进行捕获。

1 声明抛出
  1. 显式声明抛出:通过在main函数与自定义函数后面加上throws 异常类语句来抛出异常,其逻辑是表明这个函数在遇到此种异常时可以将异常传递给调用该函数的对象(或方法、虚拟机,当main函数遇到异常且不捕获时则会抛给虚拟机)。
    下面是一个例子:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class Test{
        public static void main(String[] args) throws IOException{
            BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
            String c;
            int i = 0;
            String[] e = new String[10];
            while(i<10){
                c = keyin.readLine();
                e[i] = c;
                i++;
            }
            for(i=0; i<10; i++){
                System.out.println(e[i]);
            }
          }
    }
    

    对于上述代码,若将main函数后面的throws IOException去掉,则编译不会通过,会报错Unhandled exception type IOException,表明main函数无法处理IOException异常,因为main函数中使用了IO操作,便可能会发生IOException异常;main函数后面的throws IOException语句则表明在发生IOException时main函数具有将该异常抛出的“能力”。

  2. 隐式声明抛出:这种情况仅适用于RuntimeException类及其子类,可以将其看成是显式声明抛出的一种省略,即当程序中可能发生RuntimeException时无需在main函数与其他函数后面写上throws RuntimeException,因为编译器会默认所有函数均可以抛出RuntimeException异常。RuntimeException类顾名思义就是运行时异常,即编译可以通过,但运行时会出错。
    下面举一个出现RuntimeException子类ArrayIndexOutOfBoundsException异常的例子:

    public class Test{
        public static void main(String[] args) {
            int[] arr = new int[5];
            System.out.println(arr[6]);
        }
    }
    
2 捕获异常

使用try...catch...finally语句对异常进行捕获并处理,以下是该语句的使用方式:

try{
  statements //这里存放可能出现异常的语句块,
}
catch (ExceptionType1 ExceptionObject){
  Exception Handling  //若上述语句块出现了异常类ExceptionType1,则执行此句对异常进行处理
}
catch(ExceptionType2 ExceptionObject) { 
  Exception Handling  //若上述语句块出现了异常类ExceptionType2,则执行此句对异常进行处理
}
……
finally{
  Finally  Handling  //  不论try语句块有没有发生异常,都将执行此代码块,常用来做一些“善后”工作
}

下面是一个异常捕获的例子:

public class Test{
    public static void main(String[] args) {
        int N = 5;
        int[] arr = new int[N];
        for(int i=0; i<N; i++){
            arr[i] = i;
        }
        
        int idx = 10;
        try{
            System.out.println(arr[idx]);
        }
        catch(ArrayIndexOutOfBoundsException outbe){
            idx = N-1; // 若下标超过了数组元素个数,则取最后一个元素
            System.out.println(arr[idx]);
        }
    }
}

上述代码中想要输出数组arr中下标为idx的元素,但idx大小超过了数组元素个数,所以需要捕获ArrayIndexOutOfBoundsException异常,对该异常的处理方式是将idx置为数组元素个数-1,即输出数组arr的最后一个元素。

第三题:选取RuntimeException类的五个子类,编写抛出并捕获上述子类异常的程序。(例如算术异常,空指针异常,类转换异常,数组越界异常等)

分别选取以下异常进行说明:
a. 数组越界异常(ArrayIndexOutOfBoundsException),
b. 类转换异常(ClassCastException),
c. 负数组大小异常(NegativeArraySizeException),

  1. 数组越界异常(ArrayIndexOutOfBoundsException )

    public class Test{
        public static void main(String[] args) {
            int N = 5;
            int[] arr = new int[N];
            for(int i=0; i<N; i++){
                arr[i] = i;
            }
            
            int idx = 10;
            try{
                System.out.println(arr[idx]);
            }
            catch(ArrayIndexOutOfBoundsException outbe){
                idx = N-1; // 若下标超过了数组元素个数,则取最后一个元素
                System.out.println(arr[idx]);
            }
        }
    }
    
  2. 类转换异常(ClassCastException)
    官网上的解释是:Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance.
    意思就是当将一个对象转换为一个子类时,若这个对象不是这个子类的实例,则会报ClassCastException异常。
    如下例子,Animal是父类,DogCat分别是Animal的子类,在将Cat对象cat转换为Dog对象时,因为cat并不是Dog类的实例,所以会报异常。

    class Animal{}
    
    class Dog extends Animal{}
    
    class Cat extends Animal{}
    
    
    public class Test{
        public static void main(String[] args) {
            Animal dog = new Dog();
            Animal cat = new Cat();
            Dog newDog = (Dog)dog; // 没有异常
            try{
                Dog newCat = (Dog)cat; // 报ClassCastException异常
            }
            catch(ClassCastException cce){
                String className = cat.getClass().getName();
                System.out.println("the object cat is instance of "+className);
                Cat newCat = (Cat)cat;
            }
        }
    }
    
  3. 负数组大小异常(NegativeArraySizeException )
    在数组初始化时,若数组大小为负数则会报此异常。
    下面的例子中,从命令行输入数组大小,若数组大小为负数则捕获NegativeArraySizeException异常并将数组大小置为1。

    import java.io.*;
    
    public class Test{
        public static void main(String[] args) throws IOException{
            BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
            String arrSize = keyin.readLine();
            int arrSize2 = Integer.parseInt(arrSize);
            
            try{
                String[] arr = new String[arrSize2];
                System.out.println("array length is "+arr.length);
            }
            catch(NegativeArraySizeException e){
                System.out.println("Array size is negative! It will be assigned 1.");
                String[] arr = new String[1];
                System.out.println("array length is "+arr.length);
            }
        }
    }
    
  4. 数组存储异常(ArrayStoreException)
    这种异常的发生情形为:父类F有两个子类SD,当父类数组对象引用指向其中一个子类(S)的数组对象时,若要在父类数组对象引用中存储另一个子类(D)的对象时,则会报ArrayStoreException异常。
    下面的例子中,Object的数组对象引用指向了String对象,若要在其中存储Integer类型则会报错。

    public class Test{
        public static void main(String[] args) {
            Object[] o = new String[] {"a","b"}; // 对象引用指向了String对象
            
            System.out.println(o[0].getClass()); // 输出为java.lang.String
            o[0] = 123; // 会报ArrayStoreException异常
            System.out.println(o[0].getClass());
        }
    }
    

    有个小疑问,若将上面数组对象的初始化方法换成Object[] o = {"a","b"};,便不会报ArrayStoreException异常,具体原因还不清楚,后续搞清楚了再更新。

  5. 非法参数异常(IllegalArgumentException)
    这种异常一般是由于传入的参数类型与方法接收的参数类型不一致导致的。

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class Test{
        public static void print(String content){
            System.out.println("The content is "+content);
        }
        public static void main(String[] args) {
            Date day = new Date();   
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
            String date = df.format(day);
            System.out.println(date); // 会输出当前日期和时间
    
            SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd");
            String newFormat = dateFormat.format(date); //会报IllegalArgumentException: Cannot format given Object as a Date异常,因为format方法接收Date对象作为参数,而不接受String对象
            System.out.println(newFormat);
        }
    }
    

第四题:仿照例7.9,自定义一个异常类,并在某场景下抛出该异常对象。

下面写了一个TooLongException类,当字符串长度超过一定数值后抛出该异常对象。

class TooLongException extends RuntimeException{
    private static final long serialVersionUID = 1L;

    TooLongException(String msg){
        super(msg);   
    }
}

public class Test{
    public static void print(String content){
        int conLen = content.length();
        if(conLen > 10){
            throw new TooLongException("The length is Too Long. Length is "+conLen);
        }
        System.out.println("The content is "+content);
    }
    public static void main(String[] args) {
        print("I love Java programming");
    }
}

;