目录
1 异常的概述
1.1 什么是异常?
- 异常就是程序出现了不正常情况 , 程序在执行过程中 , 数据导致程序不正常 , 最终导致了JVM的非正常停止
- 注意 : 语句错误不算在异常体系中
1.2 异常的存在的形式
- 异常有类型之分,比如我们之前有接触过的比较熟悉的数组越界异常(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException),类型转换异常(ClassCastException)。当程序中产生异常时,其实就是在异常的位置创建了一个该异常的对象,该对象携带了相关的异常信息。
- 简单来说 : 异常就是Java中提供的类的对象
1.3 程序中异常产生后,是如何处理的
- 程序中一旦产生异常,首先会中断向下执行。异常的传递要根据处理方式(后面章节会讲到)而定,如果没有处理,默认是将异常传递给本方法的调用者。不断往回传递,直到JVM收到该异常,此时程序终止执行
2 异常的分类
-
1.编译时异常(checked异常)
是指编译器要求必须处置的异常。
即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。对于这类异常,如果程序不处理,可能会带来意想不到的结果。
- IOException
- FileNotFoundException
- ClassNotFoundException
2.运行时异常(unchecked异常)
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmaticException
3 异常的处理方式
3.1 JVM处理异常的方式
-
如果程序出现了问题,我们没有做任何处理,最终JVM会做默认的处理 , 那么JVM是如何处理的呢 ?
- 把异常的类型 , 原因 , 位置打印在控制台
- 程序停止执行
-
注意 : 程序中出现了异常 , 会在当前位置创建此异常的对象 , 对象中包含了异常的信息 , 并把此异常交给本方法的调用者处理
-
缺点 : 用户体验不好
3.2 手动处理异常方式
3.2.1 声明异常
-
声明异常—— throws
- 修饰符 返回值类型 方法名(参数列表) throws 异常类型1 , 异常的类型2… { … }
- 举例 : public void show() throws NullPointerException , ArrayIndexOutOfBoundsException { … }
-
作用 :
- 表示调用当前的方法可能会出现某些异常,使用时需要注意哦!
- 如果当前方法没有出现任何异常, 那么代码会正常执行
- 如果当前方法中出现了异常 , 会把异常交给本方法调用者处理(甩锅)
-
package com.itheima.exception_demo; import sun.java2d.pipe.SpanShapeRenderer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /* 声明异常—— throws 格式 : 修饰符 返回值类型 方法名(参数列表) throws 异常类型1 , 异常的类型2... { ... } 举例 : public void show() throws NullPointerException , ArrayIndexOutOfBoundsException { .... } 作用 : 1 表示告知调用者当前的方法可能会出现某些异常,使用时需要注意哦! 2 如果当前方法没有出现任何异常, 那么代码会正常执行 3 如果当前方法中出现了异常 , 会把异常交给本方法调用者处理(甩锅) 需求 : 练习 : 定义两个方法一个运行时期异常 , 一个声明编译时期异常 ! 注意 : 1 编译时异常因为在编译时就会检查,所以必须要写在方法后面进行显示声明 2 运行时异常因为在运行时才会发生,所以在方法后面可以不写 3 如果声明多个异常有子父类关系 , 那么只要声明一个父类即可(多态) */ public class Exception_Throws { public static void main(String[] args) throws ParseException{ printArr();// 如果此方法出现了异常 , 会交给jvm进行处理 StringToDate();// 如果此方法出现了异常 , 会交给jvm进行处理 } // 1 告诉调用者 , 此方法可能会出现异常哦 // 2 如果此方法没有出现异常 , 那么会正常执行 // 3 如果此方法中出现了异常 , 会把此异常交给调用者处理 // 注意 : 如果声明的异常是一个运行时期异常 , 那么此声明可以省略 public static void printArr() /*throws NullPointerException*/ { int[] arr = null; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } // 1 告诉调用者 , 此方法可能会出现异常哦 // 2 如果此方法没有出现异常 , 那么会正常执行 // 3 如果此方法中出现了异常 , 会把此异常交给调用者处理 // 注意 : 如果声明的异常 是一个编译时期异常 , 那么在编译时期必须处理 , 要么程序无法执行 public static void StringToDate() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = sdf.parse("2000-03-11 12:12:12"); } }
3.2.2 抛出异常
思考:
- 以前出现了异常,虚拟机帮我们创建一个异常对象,抛给调用者。但是如果我们需要自己手动创建一个异常对象该如何写?
格式 :
-
修饰符 返回值类型 方法名(参数列表) { throw new 异常对象(); }
注意 :
-
抛出异常的格式必须在方法的内部完成
-
如果手动抛出一个异常,下面的代码无法执行
-
package com.itheima.exception_demo; /* 抛出异常演示 : 格式 : 修饰符 返回值类型 方法名(参数列表) { throw new 异常对象(); } 注意 : 1 抛出异常的格式必须在方法的内部完成 2 如果手动抛出一个异常,下面的代码无法执行 */ public class Exception_Throw { public static void main(String[] args) { System.out.println("家里有一个貌美如花的老婆"); System.out.println("还有一个当官的兄弟"); System.out.println("自己还有一个买卖"); System.out.println("这样的生活你要不要?"); // 程序不想往下执行了 ,怎么做 ??? // 1 自己手动制造出一个异常 // 2 当前异常也是交给了方法的调用者处理 , 也就是jvm处理 // 3 下面代码无法执行 throw new RuntimeException(); // System.out.println("武大郎的标准生活!"); } }
作用 :
- 在方法中,当传递的参数有误,没有继续运行下去的意义了,则采取抛出处理,表示让该方法结束运行。
- 告诉调用者方法中出现的问题原因
package com.itheima.exception_demo;
/*
抛出异常存在的意义所在 :
1 在方法中,当传递的参数有误,没有继续运行下去的意义了,则采取抛出处理,表示让该方法结束运行。
2 告诉调用者方法中出现了问题
练习 : 定义一个方法 , 方法的参数接收一个数组 , 在方法中遍历数组 .
需求1 : 如果方法接收的数组为null , 使用输出语句提示
需求2 : 如果方法接收的数组为null , 使用抛出异常解决
思考 : 两种方式的区别在哪里 ?
*/
public class Exception_Throw2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
arr = null;
// printArr1(arr);
printArr2(arr);// 接收方法返回的异常 , 但是此异常有jvm进行处理
}
// 需求1 : 如果方法接收的数组为null , 使用输出语句提示
public static void printArr1(int[] arr) {
if (arr == null) {
System.out.println("数组为null");
} else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
// 需求2 : 如果方法接收的数组为null , 使用抛出异常解决
public static void printArr2(int[] arr) {
if (arr == null) {
throw new RuntimeException();
} else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KpTPFVkD-1656169060085)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210410224739667.png)]
3.2.3 throws和throw的区别
- throws :
- 用在方法声明后面,跟的是异常类名
- 表示声明异常,告知调用者调用该方法有可能会出现这样的异常
- throw :
- 用在方法体内,跟的是异常对象名
- 表示手动抛出异常对象,告知调用者数据传入有误
3.2.4 捕获异常
-
捕获处理异常介绍 : try, catch
- 之前的声明或者抛出都是将异常传递出去,让调用者知道异常信息。
而捕获处理是本方法内部进行处理 , 能够阻止异常的传递,从而保证程序能够继续往下执行。
- 之前的声明或者抛出都是将异常传递出去,让调用者知道异常信息。
-
捕获异常的格式
-
try { try中存放可能会出现问题的代码 1.代码... 2.代码... 3.代码... } catch (异常类型 变量名) { 4.处理异常方案 打印异常,获取异常原因记录日志......) } 5.其他代码...
-
-
执行方式
- 如果 try 中没有遇到问题,怎么执行?
- 从上往下依次执行 , catch中不执行
- 如果 try 中代码2遇到了问题,问题下面的代码还会执行吗?
- 不会执行,会拿当前异常对象和异常类型匹配,匹配成功执行处理异常代码
- 如果出现的问题没有被捕获,那么程序如何运行?
- 如果异常没有捕获到 , 虚拟机会帮助我们处理
- 如果 try 中没有遇到问题,怎么执行?
-
多异常捕获处理方案
-
多个异常,每个异常单独处理
-
try{ 异常1 }catch(异常1){ } try{ 异常2 }catch(异常2){ }
-
-
多个异常,一次捕获,多次处理
-
try{ 异常1 异常2 }catch(异常1){ }catch(异常2){ }
-
-
多个异常,异常一次捕获,一次处理
-
try{ 异常1 异常2 }catch(Exception e){ }
-
-
3.3 Throwable 的成员方法
方法名 | 说明 |
---|---|
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
3.4 异常练习
package com.itheima.exception_demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
定义一个方法接收一个生日日期字符串(xxxx年xx月xx)
main方法中让用户输入一个生日日期字符串,调用设计好的方法计算在地球上活了多少天。
要求:如果解析发生异常,捕获异常,提示用户要重新输入生日日期字符串,直到输入正确的日期为止。
思考:设计代此码的过程中想想什么时候捕获异常,什么时候声明异常?
*/
public class ExceptionTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入生日(xxxx-xx-xx):");
while (true) {
String birthday = sc.nextLine();
try {
method(birthday);
break;// 如果生日没有问题结束死循环
} catch (ParseException e) {
System.out.println("录入生日格式有误!");
}
}
}
public static void method(String strDate) throws ParseException {
// 创建日期模板对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 解析字符串
Date date = sdf.parse(strDate);
// 获取生日到1970/1/1 经历的毫秒值
long time1 = date.getTime();
// 当前系统时间到1970/1/1 经历的毫秒值
Date d2 = new Date();
long time2 = d2.getTime();
System.out.println("活了" + (time2 - time1) / (1000L * 60 * 60 * 24) + "天");
}
}
4 自定义异常
4.1 概述
- 当JDK中的异常类型,不满足实际的业务需要时。就可以自己定义异常。例如,学生的年龄数据,如果是负数或者数据 超过了150认为是不合法的,就需要抛出异常。JDK中就没有表示年龄的异常,就需要自己定义异常了
4.2 实现步骤
- 定义异常类
- 写继承关系
- 空参构造
- 带参构造
4.3 自定义异常注意
- 如果要自定义编译时异常,就继承Exception
- 如果要自定义运行时异常,就继承RuntimeException
日志框架
程序中的日志用来记录程序运行过程中的信息,并可以进行永久存储。
可以将系统执行的信息选择性的记录到指定位置(控制台、文件、数据库)
可以随时以开关的形式控制是否记录日志。
体系结构
日志规范:一些接口,提供给日志的实现框架设计的标准
JCL:Commons Logging
slf4j:Simple Logging Facade for Java
日志实现框架:Log4j、Logback、其他实现
Logback日志框架
- Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好
- 官网:Logback Home (qos.ch)
- Logback是基于slf4j的日志规范实现的框架
Logback主要分为三个技术模块
- logback-core:logback-core模块为其它两个模块奠定了基础,相当于入口,必须有。
- logback-classic:它是log4j的一个改良版本,核心功能模块,同时它完整实现了slf4j API
- logback-access模块与Tomact和Jetty等Servlet容器集成,以提供HTTP访问日志功能。
使用Logback的开发步骤是:
- 在项目下新建文件夹lib,导入Logback相关的jar包到该文件夹下,并添加到项目库中
- 必须将Logback的核心配置文件logback.xml直接拷贝到src目录下
- 在代码中获取日志对象
- 使用日志对象输出日志信息