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)
总结:
不可检测异常,是运行时才抛出的异常,编译阶段可以选择性的处理异常
可检测异常,是编译阶段必须处理的异常,否则代码编译不通过,处理异常的方式有两种:
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 in2 = null;
try{
in1 = new FileReader("data1.txt");
in2 = new FileReader("data2.txt");
.......
if(n1 != null) //证明n1打开过
n1.close();
if(n2 != null)
n2.close();
}
* 但是,如果对象重写了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); } }
浙公网安备 33010602011771号