java异常机制

面经问题:class前面都能用什么来修饰?

public class  公共类

abstract class  抽象类

final class   密封类(修饰方法为密封方法,修饰变量为常量)

strictfp class 跟常量的浮点运算有关

java异常体系

常见的异常:

StackOverFlow                  栈溢出错误   (写递归函数,没有写结束条件的时候)

ArrayIndexOutOfBounds   数组越界(new arr[10]  0-9)

NullPointerException         空指针异常(引用变量没有初始化指向有效的对象,打断点调试!!!)

ClassCastException          类型转换错误(Student s=(Student)object);

OutOfMemory(OOM)         堆内存耗尽了,没有可用的堆内存(调整JVM参数,逻辑上有内存泄漏(没有被回收完全))

 

                                             Object

                                                |
                                   Throwable(Java所有异常类型的基类类型)
                                           -------------------------------------
            |                                                                                                         |
  error(JVM抛出的错误)                                                   Exception(应用程序抛出的异常)
           |                                                                                                         |

StackOverFlowError

OOMError                                       RuntimeException                                             FileNotFoundException

                                                     运行时的异常(不可检测异常)                               可检测异常
                                                                          |
                                                             ArrayIndexOutofBounds(int[] a=new int[5];  sout(a[6]));
                                                             NullPointerException(Student s=new Student();  s=null;  sout(s.a);)
                                                             ClassCastException( Test extends Student   
Student s=new Student();Test t1=(Test)s; 运行要判断s instanceOf Test)

 

java程序如果不处理抛出的异常,那么异常会抛给jvm,jvm判断当前java进程有一个未被处理的异常,会直接结束java程序。如果抛出的异常被catch块捕捉到了,程序可以继续向下执行。

java异常涉及的关键字

try:一般把可能发生的异常括起来

catch:紧跟着try块的后面,可以出现多个catch块,用来捕获不同类型的异常

finally:不管程序有没有发生异常,finally块是一定会被执行到的,所以一般把资源释放的代码放在finally块里面

throw:抛出指定类型的异常对象的   throw+异常类对象(throw new IllegalArguementException)

throws:当前这个函数不想处理异常,把异常抛给上一级函数,让它处理异常,写在函数的签名后(  public static int myDiv(int a,int b)   throws  ArithmeticException)    

 

 继承关系的源码分析:

 

 

 

 

 简单的try catch语句测试:

 

finally语句:不管有没有异常都会执行

getStackTrace()方法可以打印出包名.类名.方法名:[second.ExceptionTest.main(ExceptionTest.java:9)]

 

 例:写一个除数的异常:

public class Test extends Student{
    public static void main(String[] args) {
            int a = 20;
            int b = 0;
            int result=myDiv(a, b);
            System.out.println(a+"/"+b+"="+result);
        }
    /**
     * 计算a和b的除法,在函数里对除数进行处理
     * @param a
     * @param b
     * @return
     */
    public static int myDiv(int a,int b){
        if(b==0){
            throw new IllegalArgumentException("除数不能为0");  //参数不正确的异常名IllegalArgumentException
        }
        return a/b;
    }
}

异常的处理(异常沿着函数的调用链条来抛):

JVM调用main函数,main函数调用dix函数;

div函数将异常抛给main函数,main函数并未处理该异常,抛给JVM,jvm发现当前有一个未被处理的异常,会终止程序(运行报错Exception in thread "main" java.lang.IllegalArgumentException): 除数不能为0

手动抛出异常:  throw+异常类对象,手动抛出异常

 所以下面在main函数进行处理:

           try {
               int a = 20;
               int b = 0;
               int result = myDiv(a, b);
               System.out.println(a + "/" + b + "=" + result);
           }catch (IllegalArgumentException e){
               System.out.println(e.getMessage());
           }catch (ArrayIndexOutOfBoundsException e){
               System.out.println(e.getMessage());
           }finally {
               System.out.println("程序正常结束bye bye");
           }
        }

   也可以div函数扔出该算术异常给main函数,main提出处理该函数的解决方案并打印错误信息:

        try {
               int a = 20;
               int b = 0;
               int result = myDiv(a, b);
               System.out.println(a + "/" + b + "=" + result);
           }catch (ArithmeticException e){
               System.out.println(e.getMessage());
           }catch (ArrayIndexOutOfBoundsException e){
               System.out.println(e.getMessage());
           }finally {
               System.out.println("程序正常结束bye bye");
           }
        }
    /**
     * 计算a和b的除法,在函数里对除数进行处理
     * @param a
     * @param b
     * @return
     */
    public static int myDiv(int a,int b) throws ArithmeticException{
//        if(b==0){
//            throw new IllegalArgumentException("除数不能为0");  //参数不正确的异常名IllegalArgumentException
//        }
        return a/b;
   }

 

 

 一:运行时异常,不可检测

 

 在main函数中捕获异常(不清楚异常类型时,可以采用多个catch块):

 

 二:可检测异常:必须处理的异常(FileNotFoundException)

 

 经try catch处理后:

 

注:为了避免写几行代码就要处理异常的情况,可以在调用它的函数后面加throws FileNotException(public static void main(String[] args) throws FileNotFoundException),只关注代码!!

可是,当有很多异常的时候就一直要在函数后面声明该异常,比较麻烦!所以直接throws 所有异常的基类(public static void main(String[] args) throws Exception)

总结

不可检测异常,是运行时才抛出的异常,编译阶段可以选择性的处理异常

可检测异常,是编译阶段必须处理的异常,否则代码编译不通过,处理异常的方式有两种:

            1.把可能发生异常的代码用try catch块进行处理
            2.在函数签名的后面通过throws 异常类型 把可能发生的异常抛给函数的调用方来处理异常
 
异常使用注意事项:
1.把同类型的异常,最好放在一起进行处理
2.把循环放在异常处理的里面,不要把循环放在外面
 
如果我们自定义一个异常类型,需要注意:
1.如果想定义不可检测异常,需要继承自RuntimeException类(class MyException extends RuntimeException)
2.如果想定义可检测异常,需要直接继承自Exception

 

面经知识:

final            finally                 finalize的区别

final可以修饰变量、方法、类。

final修饰类,表示这个类是个密封类,不能被继承,所以如果在设计一个类的时候,不想把这个类作为基类使用,可以用final修饰,像JDK里面的包装类型和字符串类型,通常都是final定义的,都是密封类。final修饰方法,称为密封方法,表示这个方法不能再被重写,一般放在不需要派生类重写的基类里面。final修饰变量,不能改变值当作左值,一般哪些量是固定的值,不能再被改变的时候,设计的时候这样的量定义为final修饰,保证固定的量的值不能被修改,这样的量被称为常量,以免万一改变其值,程序出现不可预期的结果。

finally用在java的异常当中,放在try catch后面,不管有没有异常都会执行,一般把资源释放的代码放在finally语句中,保证资源释放。(下面实例)

FileReader in1 = null;
FileReader in2 = null;
try{
    in1 = new FileReader("data1.txt");
    in2 = new FileReader("data2.txt");
    .......
} catch(FilenotFoundException e){
} finally{
    //只要文件打开过,就会得到关闭
    if(n1 != null)  //证明n1打开过
     n1.close();
     if(n2 != null)
      n2.close();
}
finalize用在垃圾回收机制中;JVM的gc是以固定的周期进行对象回收的,gc的周期到了,会找那些没有被引用的对象判断是无效对象,对这些对象回收它们的堆内存;
     * 但是,如果对象重写了finalize方法,那么gc在当前回收周期的时候,会调用对象的finalize,然后再下一个gc周期再回收对象内存
     * 所以一般可以把对象需要释放的资源写在finalize中,但是这样的代码设计的不好,因为不知道gc什么时候到回收周期,
     * 所以不知道finalize函数不知道什么时候会被调用到,需要释放对象资源的时候,一般会提供释放资源的成员方法,然后手动调用
public class Student{
    /**
     * 重写finalize方法
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("bye bye!!");
        super.finalize();
    }
    public void realse(){

    }
    /**
     * JVM的gc是以固定的周期进行对象回收的,gc的周期到了,会找那些没有被引用的对象判断是无效对象,对这些对象回收它们的堆内存;
     * 但是,如果对象重写了finalize方法,那么gc在当前回收周期的时候,会调用对象的finalize,然后再下一个gc周期再回收对象内存
     * 所以一般可以把对象需要释放的资源写在finalize中,但是这样的代码设计的不好,因为不知道gc什么时候到回收周期,
     * 所以不知道finalize函数不知道什么时候会被调用到,需要释放对象资源的时候,一般会提供释放资源的成员方法,然后手动调用
     * @param args
     * @throws Throwable
     */
    public static void main(String[] args) throws Throwable{
        Student s=new Student();
        s.realse();//手动释放资源
        s=null;
        System.gc();//启动垃圾回收
        Thread.sleep(10000);
    }
}

 

posted @ 2019-10-28 17:48  acehm  阅读(327)  评论(0)    收藏  举报