Java异常解析 在模仿JVM处理异常中理解异常的使用 & 老掉牙面试题【finally final finalize的区别】

异常

概述: Java针对代码中的错误或者异常的一种描述的对象体现 【错误、异常信息的描述 -->(封装成) 对象

1.代码中异常的分类

  • Throwable
    • Error【严重错误】 我们无法解决,比如内存溢出
    • Exception 【异常】
      • 编译时异常、受检异常: 我们如果不解决,会编译报错,Exception的子类但是不是RuntimeException都是编译时异常
      • 运行时异常: 我们也可以解决,一般程序不够严谨, RuntimeException或者RuntimeException的子类都是运行时异常

2.为什么要处理异常?

处理异常的本质–> 在程序出现异常的情况下,能够保证程序继续执行,最好打印错误信息

2.1默认虚拟机处理异常的方式

JVM处理异常的方式不能够满足我们的需求,所以需要自行处理异常

  1. 打印错误信息
    1. 错误的类名: java.lang.ArithmeticException
    2. 错误的描述: / by zero
    3. 错误的位置 main ExceptionDemo02 java:11
  2. 直接杀死程序,导致程序终止不能完整执行

3.try catch finally

程序必定转入finally代码块执行,除非虚拟机退出,一般用来释放资源,IO,数据库连接都需要释放连接

虚拟机退出:(e.g.: System.exit(0); // 退出JVM

  1. try catch

            //格式 		
                    try{
                        //放置程序可能出现问题的代码
                    } catch(异常类  异常名){
                        //这里放置异常处理的代码
                    }
    
  2. try catch finally

            //格式
                    try{
                        //放置程序可能出现问题的代码
                    } catch(异常类  异常名){
                        //这里放置异常处理的代码
                    } finally {
                        //IO,数据库连接释放连接
                    }
    
  3. try finally

            //格式
                    try{
                        //放置程序可能出现问题的代码
                    } finally {
                        //IO,数据库连接释放连接
                    }
    
  4. try catch…catch…finally

            //格式
                    try{
                        //放置程序可能出现问题的代码
                    } catch(异常类  异常名){
                        //这里放置异常处理的代码
                    } catch(异常类  异常名){
                        //这里放置异常处理的代码
                    } ... 
                     finally {
                        //IO,数据库连接释放连接
                    }
    

3.1注意

  • try代码块中,异常的代码后的代码全部不执行
  • 系统抛出的异常对象和异常类进行匹配,可以使用多态
  • try块中的代码越少越好
  • 无论多少个catch块,只会匹配成功一个,或者都不匹配
  • 异常处理的顺序先子类后父类
  • 在catch的最后使用Exception作为父类处理
  • 一般不在类中进行try catch,因为实际开发并不一定能看到原码

3.2finally final finalize的区别

final:

  • 可以修饰类、方法、变量
  • 修饰的变量为自定义常量
  • 不可以继承
  • 不可以重写

finally:

  • 保证重点代码一定要被执行的一种机制
  • 进行类似资源释放保证解锁等动作

finalize:

  • 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。用于垃圾回收,但是什么时候回收不确定

4.如何模仿JVM处理异常

4.1源码解析

Throwable子类都没有方法实现,以下是关于异常的源码方法

class Throwable {
					"空指针异常啦!!!"
	private String detailMessage;
					"空指针异常啦!!!"
	public Throwable(String message) {
        fillInStackTrace();
        detailMessage = message;
    }
    
    public String toString() {
        String s = this.getClass().getName(); // java.lang.NullPointExcetion
        String message = this.getLocalizedMessage(); "空指针异常啦!!!"
        return (message != null) ? (s + ": " + message) : s;
        java.lang.NullPointExcetion: 空指针异常啦!!!
    }
    
    public String getLocalizedMessage() {
        return this.getMessage(); "空指针异常啦!!!"
    }
    
    public String getMessage() {
        return detailMessage;"空指针异常啦!!!"
    }
}

4.2从Throwble类中提取错误信息

说明
fillInStackTrace()向堆栈中填充错误信息
toString()返回错误信息的描述 类路径:错误信息
getLocalizedMessage返回错误描述
getMessage返回错误描述
StackTraceElement[] getStackTrace()获取堆栈中的错误信息
void printStackTrace()输出堆栈错误信息
void printStackTrace(PrintWriter s)日志文件 log
		//	日志文件 log生成(PrintWriter & FileWriter)
		try {
			System.out.println(a / b);
		} catch (Exception e) {
			e.printStackTrace(new PrintWriter(new FileWriter("exceptionLog.txt"), true));
			System.exit(0);
		}

4.3StackTraceElement中相关方法

说明例子
String getClassName()返回类的完全限定名,该类包含由该堆栈跟踪元素所表示的执行点。com.exceptiondemo.ExceptionDemo
String getFileName()返回源文件名,该文件包含由该堆栈跟踪元素所表示的执行点。ExceptionDemo.java
int getLineNumber()返回源行的行号,该行包含由该堆栈该跟踪元素所表示的执行点。55
String getMethodName()返回方法名,此方法包含由该堆栈跟踪元素所表示的执行点。main
int hashCode()返回此堆栈跟踪元素的哈希码值
boolean isNativeMethod()如果包含由该堆栈跟踪元素所表示的执行点的方法是一个本机方法,则返回 true。
  • 异常的相关方法Demo

    		try {
    			method();
    		} catch (Exception e) {
    			StackTraceElement[] sTraceElements = e.getStackTrace();
    			for (StackTraceElement ste : sTraceElements) {
    				String className = ste.getClassName();
    				String fileName = ste.getFileName();
    				String methodName = ste.getMethodName();
    				int lineNumber = ste.getLineNumber();
    				boolean isNative = ste.isNativeMethod();
    				System.out.println(fileName + "|" + className 
    						+ "|" + methodName + "|" + isNative + "|" + lineNumber);
    			}
    		}
            //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|show|false|82
            //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|demo|false|75
            //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|method|false|71
            //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|main|false|55
    
  • 为什么相关方法是数组形式?

    • 因为异常很多时候不止一个,出现多个方法中,数组就能把不同信息存储在不同的StackTraceElement相关方法中
		try {
			method();
		} catch (Exception e) {
			StackTraceElement[] sTraceElements = e.getStackTrace();
			for (StackTraceElement ste : sTraceElements) {
				String className = ste.getClassName();
				String fileName = ste.getFileName();
				String methodName = ste.getMethodName();
				int lineNumber = ste.getLineNumber();
				boolean isNative = ste.isNativeMethod();
				System.out.println(fileName + "|" + className 
						+ "|" + methodName + "|" + isNative + "|" + lineNumber);
			}
		}
        //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|show|false|82
        //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|demo|false|75
        //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|method|false|71
        //ExceptionDemo.java|com.exceptiondemo.ExceptionDemo|main|false|55

如何看异常:从下往上看,先看自己写的代码,再看别人或者源码的

4.4模拟虚拟机处理异常的方式

为什么要模拟JVM的异常处理?因为自己写的太简略了,模拟虚拟机,即可以解决虚拟机因为异常而终止程序,也可以获得详尽的异常信息

		try {
			System.out.println(a / b);
		} catch (Exception e) {
			StackTraceElement[] stackTrace = e.getStackTrace();
			String className = stackTrace[0].getClassName();
			String fileName = stackTrace[0].getFileName();
			String methodName = stackTrace[0].getMethodName();
			int lineNumber = stackTrace[0].getLineNumber();
			System.err.format("Exception in thread \"%s\" %s\r\n" + "	at %s.%s(%s:%d)", methodName, e.toString(),
					className, methodName, fileName, lineNumber);
            
            //printStackTrace()可以打印出上述的所有信息
			 e.printStackTrace();
			
		}

5.异常链

即在catch处理一个异常的时候,因为这个异常导致catch代码块也出现异常,这时可以cause,把新异常缘由标记为旧异常,这样就可以清晰知道相关关系,在处理好旧异常后,不进入catch,新异常就解决了

  • 常常会再捕获一个异常后抛出另外一个异常,并且希望把异常原始信息保存下来,这被称为异常链。

  • JDK1.4以前,程序员必须自己编写代码来保存原始异常信息

  • 现在所有Throwable的子类子构造器中都可以接受一个cause对象作为参数,这个cause就异常原由,代表着原始异常

    • 即使在当前位置创建并抛出新的异常,也可以通过这个cause追踪到异常最初发生的位置。
  • 只有Error,Exception,RunimeException提供了带cause参数的构造器,其他的所有异常就只有通过initCause()来设置cause了。

    	try {
    			test2();
    		} catch (NullPointerException ex) {
    			ex.printStackTrace();
    			//这是没有带cause参数的构造器的异常对象【Xxx(异常信息, cause)】
            	ArithmeticException ae = new ArithmeticException("算数异常!");
    			ae.initCause(ex);
    			//Error,Exception,RunimeException提供了带cause参数的构造器
            	Exception e = new Exception("出问题啦!大家快来看!", ex);
    			throw e;
    		}
    	}
    

6.throws

有时处理不了,不想处理,没有权限处理的异常可以用throws把异常抛给调用者

//格式
        [访问权修饰符] 返回值类型  方法名(参数列表) [throws 异常类名] {
                方法体;
        }

特点

  1. 这种处理方式是一种不负责任的处理方式,也是一种提醒调用者的方式,谁调用谁处理

  2. 如果是主方法,我们必须处理,因为我们不处理JVM会处理,JVM会将程序结束,不满足异常处理的本质

  3. 如果一个方法抛出一个异常,那么子类不能抛出的异常不能够被放大(异常不能大于父类

  4. 我们可以在一个方法上声明多个异常,中间使用逗号隔开,表示异常出现的可能性(可能出现,不是一定异常)

  5. 当一个方法抛出的是一个编译时异常的时候,必须处理,不处理就会报错

7.throw

throw是主动的说明异常位置问题原因,这便于之后问题异常的处理

//格式
		throw  异常对象;

7.1throw和throws的区别

  1. throws是在方法的声明上,throw是在方法体内
  2. throws声明的是异常类,并且可以声明多个,throw抛出的是异常对象,只能够抛出一个
  3. throws表示异常出现的可能性,throw表示异常出现的必然性
//throw是主动的,把问题标识出来,防止错误因为规避而导致别的地方出错
//Demo中的“int b = 0;”即使这里可以规避错误,但也可能因为= 0,导致别的地方出错,所以抛出更优 
public static void method1() throws ArithmeticException {
		int a = 10;
		int b = 0;
		// System.out.println(a / b);
		
		// 方式一: 规避错误
		if (b != 0) {
			System.out.println(a / b);
		}
		
		// 方式二: 提示错误
		if (b == 0) {
			throw new ArithmeticException("除数不能为0!");
		}
	}

8.自定义异常

继承自Exception,当Throwable中的异常不满足我们需求时,可以自定义满足自己需求的异常

        public static boolean checkScore(double score) throws ScoreException {
            if (score < 0 || score > 100) {
                //有参构造才能输出说明
                throw new ScoreException("兄弟啊!分数要在0~100分之间,不能是" + score + "这个分数");
            }
            return true;
        }


        //直接建一个继承Exception类的类就完成自定义异常了
        class ScoreException extends Exception {}

        //但要想可以输出说明,就需要有参构造
        class ScoreException extends Exception {

            private static final long serialVersionUID = -1989349702073875392L;

            public ScoreException() {
                super();
            }
            //有参构造
            public ScoreException(String s) {
                super(s);
            }
        }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值