javaSE基础异常的刨析

发布于 2020-10-28  405 次阅读


异常机制是java一个重要的点,源码中经常可以看到它的身影,现在重新破解它

异常的定义:导致程序发生错误的行为

一,异常机制的分类和体系结构关系

一,异常机制的分类

异常机制把异常分为两种:错误(Error)和异常(Exception),而异常又分为两种编译期异常(IOExecption)和运行时异常(RuntimeExecption)

Error:错误在机制中不做任何处理,jvm本身也无法解决,只能后期修改避免,我们不重点说明

Error也分两类:

1,IOError(IO错误):出现严重IO错误时抛出

2,VirtualMachineError(虚拟机错误):虚拟机已损坏或已耗尽资源以使其继续运行  例如:OutOfMemoryError(堆溢出OOM),StackOverflowError(栈溢出)

异常:

1,编译期异常:在编写代码时会有提示抛出或者声明   常见的有:IOExecption(IO异常),FileNotFountExecption(文件异常)

2,运行期异常(RuntimeException):编译期间没有任何提示信息,运行时报错,常见的有:NullPointerException(空指针异常),ArrayIndexOutOfBoundsException(数组越界),         ClassCastException(类型转换异常),ArithmeticException(算数异常)

二,异常机制的体系结构图

总体架构层次图:

一,顶级父类:Throwable

Throwable:只有两个子类error和exception

 

二,error类

error下的jvm错误类:

三,异常类

在异常类中并没有严格的划分运行时异常和编译期异常,除非运行时运行的是编译器异常

运行时异常类:

三,常见的错误或异常例子

一,Error

1,OutOfMemoryError(堆溢出)

int[] n=new int[1024*1024*1024]; //堆溢出

结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at 异常.WQL.main(WQL.java:9)

2,StackOverflowError(栈溢出)

public static void g() {

g();
}

结果:

Exception in thread "main" java.lang.StackOverflowError
at 异常.WQL.g(WQL.java:28)
at 异常.WQL.g(WQL.java:28)
at 异常.WQL.g(WQL.java:28)

二,运行时异常

1,NullPointerException(空指针)

String[] a=null;
System.out.print(a[1]);

结果:

Exception in thread "main" java.lang.NullPointerException
at 异常.WQL.main(WQL.java:14)

2,ArrayIndexOutOfBoundsException(数组越界)

int[] a=new int[3];
System.out.print(a[4]);

结果:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 3
at 异常.WQL.main(WQL.java:17)

3,ClassCastException(类型转换异常)

Object a=new test1();
String b=(String)a;

结果:

Exception in thread "main" java.lang.ClassCastException: class 枚举.test1 cannot be cast to class java.lang.String 
at 异常.WQL.main(WQL.java:20)

4,ArithmeticException(算数异常)

System.out.print(0/0);

结果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
at 异常.WQL.main(WQL.java:22)

二,异常的处理

异常的的处理分为两种方式:

1,try..catch 捕获异常

2,throws 声明异常

一,异常处理模式

java异常机制采用的是抓抛模型

抓抛模式:

1,抛异常:

自动抛异常:出程序在正常运行时,出现异常,JVM会生产一个异常对象并抛出,程序中断

手动抛异常:在程序定义时,在可能发生错误的代码,手动抛出异常 (格式:new 异常类型("说明"))

2,抓异常:将程序抛出的异常进行处理,

声明式处理异常:通过throws将程序抛出的异常,再次抛出给程序的调用者,如果调用者再抛出最终抛给jvm处理,它本身不做处理

捕获式处理异常:通过try{}catch(){}finally{}将程序抛出的异常进行捕获处理

 

声明式异常和捕获式异常的区别:

1,定义方式:throws定义再方法头部,try{}catch定义在程序代码段中

2,处理方式:throws本身并不处理异常,只是抛出由调用者处理,像是自己不做事,将事情推由别人来做,try.catch本身处理异常

3,程序中断:throws定义在方法头部,程序程序异常,方法直接中断,异常后面的代码不会被执行,try.catch中的程序出现异常,异常处理完毕代码会继续执行

4,适应范围:throws定义方法所有编译期异常,都会被抛出,try.catch只能处理try内的异常

二,Throwable异常中的方法

构造方法:

方法:

三,throws处理异常

1,throws修饰在方法的声明处,在方法运行时出现异常时,生成一个异常对象,如果异常对象跟throws声明类型匹配,throws就会将方法抛出

throws把异常抛出给方法的调用者,调用throws的修饰的方法,要么继续throws抛出,要么捕获

 

public static void main(String[] args) {
g();
}
public static void g() throws NullPointerException {

System.out.print("wql");
}

2,方法重写throws的规则
重写方法,只能throws和父类相同的或者父类子类的异常类型
abstract class k {
	
	public abstract void h() throws RuntimeException;	
}
class j extends k{

	@Override
	public void h() throws NumberFormatException { // NumberFormatException是RuntimeException的子类
		// TODO Auto-generated method stub
	}

四,try{}catch(){}finally{}捕获异常

try{}catch是程序自身处理异常,catch匹配出现异常,不匹配将捕获异常失败

try:可能出现异常的代码段

catch:假如try中出现异常,就匹配catch中的异常,并执行catch中的语句

finall:无论是否捕获都执行的代码段

例:

 try{
System.out.print(0/0);
}catch(ArithmeticException a){
a.printStackTrace();
System.out.print("哈哈哈");
}

结果:

java.lang.ArithmeticException: / by zero
at 异常.FQ.main(FQ.java:8)
哈哈哈

try...catch和throws的具体用途:

         1,运行时异常其实是在代码编写中很难发现的,不知道那一段代码会出问题,运行时才会发现,这种异常可以不做处理,JVM会自动抛出,也可以直接在方法中声明throws

         2,编译期异常在程序编译时就通不过,有具体的代码段,用try...catch直接处理比较合适,不用throws,但也有特例

 3,在编译期异常,程序有大量的变量赋值定义和方法引用,try...catch就不是最好的选择了,但try出现异常,变量赋值出现错误,catch的数据也会出错,直接throws中断程序是                    最好的

五,自定义异常

一,手动抛异常

手动抛出异常在一些源代码中被大量用到,但数值或者方法引用违反当前自定义程序时(但并没有违法JVM构造和编译规则,不会自动产生异常对象),只有手动抛出异常才能终止程序,它和自定义异常常相伴相生,

注:手动抛出的异常也可以处理

格式:throw new 异常类("提示符")

它和throwable的构造方法一致

例:

public class FQ {
public static void main(String[] args) {

WQL(10);

} 
public static void WQL(int a) {
if(a > 100) {
System.out.print(a);

}else {

throw new ArithmeticException("数字违法!!"); //手动抛出异常
}
}

结果:

Exception in thread "main" java.lang.ArithmeticException: 数字违法!!
at 异常.FQ.WQL(FQ.java:16)
at 异常.FQ.main(FQ.java:6)

二,自定义异常

自定义异常也不能说是真正的自定义,它需要基础现有的异常结构

步骤:

1,继承一个现有的异常类

2,提供一个全局的序列号:static final long serialVersionUID(它的作用和以前IO中的序列化流中UID是一样的,判断类是否一致用的)

3,重写方法

例:

public class test2 {

public static void main(String[] args) {
//把自定义的异常捕获
try {
num("gg");}
catch(mywqlExcepton e) {

//打印追溯的错误信息
e.printStackTrace();
}
finally {

System.out.print("哈哈");
}
}


public static void num(String name) {

if(name.equals("FQ")) {

System.out.print("WQL LOVE"+name);
}else {

throw new mywqlExcepton("FQ说,你不love我了,拜拜了您");//手动抛出异常

}}}


class mywqlExcepton extends RuntimeException{//继承运行时异常

private static final long serialVersionUID = 1L;//全局变量

//重写方法,没有动,它的父类继承Execption也是没有动其他方法
public mywqlExcepton() {
super();
// TODO Auto-generated constructor stub
}

public mywqlExcepton(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}

public mywqlExcepton(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}

public mywqlExcepton(String message) {
super(message);
// TODO Auto-generated constructor stub
}

public mywqlExcepton(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}

结果:

异常.mywqlExcepton: FQ说,你不love我了,拜拜了您
哈哈 at 异常.test2.num(test2.java:26)
at 异常.test2.main(test2.java:8)

六,源码查看

1,RuntimeExecption解析

源码:

public class RuntimeException extends Exception {//继承了Exception
static final long serialVersionUID = -7034897190745766939L;//序列号
 
public RuntimeException() {
        super();//掉用父类构造
    }

public RuntimeException(String message) {//带参构造,错误提示信息
        super(message);//掉用父类构造并传入
    }

public RuntimeException(String message, Throwable cause) {//构造一个具有指定的详细信息和原因的新的throwable
        super(message, cause);//老样子
    }

public RuntimeException(Throwable cause) {//产生一个新的throwable
        super(cause);//一样
    }

protected RuntimeException(String message, Throwable cause,boolean enableSuppression,boolean writableStackTrace) {
//构造具有指定的原因和详细消息的新throwable (cause==null ? null : cause.toString()) (它通常包含的类和详细消息 cause )
        super(message, cause, enableSuppression, writableStackTrace);
    }

}

2,Exception源码

源码:

public class Exception extends Throwable {//继承了Throwable顶级父类
static final long serialVersionUID = -3387516993124229948L;//序列号
 public Exception() {
        super();//调用父类的构造
    }
//其他方法和RuntimeException是一样的,不说了,
}

3,Throwable的源码

源码:

public class Throwable implements Serializable {//实现了Serializable标识接口
/** use serialVersionUID from JDK 1.0.2 for interoperability *//这个是这个类出现的版本时间,它在JDK1.0就出来了

private static final long serialVersionUID = -3042686055658047285L;//序列号
//太多了,不写了,比上面两个复杂
}

Error和其他异常类结构也和这个差不多,最终在做事情就是Throwable


路漫漫其修远兮,吾将上下而求索