异常
异常的体系结构和分类
1.什么是异常?
异常就是程序出现了不正常的情况
注意: 语法错误不算在异常体系中
2.异常的结构体系
Error:严重问题,通过代码无法处理
比如:内存溢出
Exception:称为异常类,他表示程序本身可以处理的问题
- RuntimeException及其子类:运行时异常.(空指针异常,数组索引越界异常)
- 除RuntimeException之外所有的异常:编译器必须处理的,否则程序不能通过编译(比如:日期格式化异常)
3.编译时异常和运行时异常的区别
- 编译时异常
- 都是Exception类及其子类
- 必须显式(手动处理),否则程序就会发生错误,无法通过编译
- 运行时异常
- 都是RuntimeException类及其子类
- 无需显式处理(手动处理),也可以和编译时异常一样处理
- 图示
JVM默认处理异常的方式
1.如果程序出现了问题,我们没有处理,最终jvm虚拟机会做默认的处理,处理方式有两个步骤
-
把异常的名字,异常原因,异常出现的位置等信息输出在控制台
-
停止程序的运行
public class ExceptionDemo2 {
/*
* 在遇到异常的时候,jvm虚拟机会做那些事?
* 1.会把异常的名字,异常出现的原因,异常的位置带给我们
* 2.会让程序停止在异常出现的位置
* */
/*
* 当异常出现时候,首先看的时本程序中是否有异常的处理方法
* 如果没有,要看这个方法的调用者,是否处理了这个异常
* 如果还没有,最终这个异常会交给虚拟机处理
* */
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
// 可以看出程序在异常之前都能正常执行,
System.out.println("黑马666");
//在这里编译时期没有错,但是运行时候会报错,
// 那么就会在这里创建一个异常对象
// new ArrayIndexOutOfBoundsException();
System.out.println(arr[10]);
//在执行的遇到异常,程序会停止,后边的内容不再执行
System.out.println("黑马牛皮,我是黑马程序员");
}
}
throws方式处理异常
定义格式
public void 方法() throws 异常类名{
}
- 示例代码1
public class ExceptionDemo3 {
// 格式 public void 方法() throws 异常类名{}
// throws ParseException,是告诉调用者,你调用我,有可能出现这样的异常
// 把问题丢给了调用或者处理,调用者不处理,最终会交给虚拟机处理
public static void main(String[] args) throws ParseException {
String dateStr = "2011-11-11 12:12:12";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//这里出现了一个ParseException异常
simpleDateFormat.parse(dateStr);
}
}
throws方式处理异常-注意事项
简单来说:编译时的异常,必须声明,谁调用谁处理,最多让jvm来处理,运行时异常可以不声明
throw抛出异常
1.格式
throw new 异常();
2.注意
这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码无法再执行了
3.抛出异常处理的意义
1.在方法中如果传递的参数有误,没有继续运行下去的必要了,采取抛出处理,表示该方法结束
2.告诉方法调用者出现了问题
4.throws和throw的区别
throws
用在方法声明的后面,跟的是异常类名
表示的是声明异常,调用该方法有可能会出现这样的异常
可以声明多个异常
throw
用在方法体内,跟的是异常对象名
表示手动抛出异常对象
只能声明一个异常
public class ExceptionDemo4 {
public static void main(String[] args) {
System.out.println("黑马");
System.out.println("传智");
System.out.println("培训");
System.out.println("学习");
// 在这里创建一个运行时的异常对象,并且没做处理
// 下面的"开心"的字符串没有了打印的可能性,
// 就会报错
throw new RuntimeException();
// System.out.println("开心"); //这行代码不会执行,我就先注释了
}
}
public class ExceptionDemo5 {
public static void main(String[] args) {
method1();//调用方法的时候,如果输入不符合要求的值,我们就会接收到一个异常对象
// 我们需要自己处理这个异常
}
private static void method1() {
int[] arr = {1, 2, 3, 4, 5};
Scanner sc = new Scanner(System.in);
System.out.println("请输入索引值:");
int index = sc.nextInt();
if (index >= arr.length) {
// 当index的值大于或等于数组长度的时候,我们手动创建了一个异常对象
// 并抛给调用者一个异常
// 此时我们并没有处理这个异常
throw new ArrayIndexOutOfBoundsException();
} else {
System.out.println(arr[index]);
}
}
}
try-catch自己处理异常
- 定义格式
try{
可能出现的异常的代码段
}catch(异常类名 变量名){
异常代码的处理代码
}
- 执行流程
- 程序从try里面的代码开始执行
- 出现异常,就会跳转到对应的catch里面去执行
- 执行完毕后,程序还可以继续往下执行
- 示例代码
public class ExceptionDemo5 {
public static void main(String[] args) {
// 异常处理,把可能出现异常的代码加入到try里面,
// 当出现异常的时候,用catch捕获并做处理
try {
method1();//调用方法的时候,如果输入不符合要求的值,我们就会接收到一个异常对象
// 我们需要自己处理这个异常
} catch (ArrayIndexOutOfBoundsException ex){
System.out.println("亲,您的输入索引越界了");
}
// 当catch处理完捕获的对象的后,这行代码可以继续执行
// 当然,如果没有对异常进行处理,这个代码
System.out.println("还能继续执行这个");
}
private static void method1() {
int[] arr = {1, 2, 3, 4, 5};
Scanner sc = new Scanner(System.in);
System.out.println("请输入索引值:");
int index = sc.nextInt();
if (index >= arr.length) {
// 当index的值大于或等于数组长度的时候,我们手动创建了一个异常对象
// 并抛给调用者一个异常
// 此时我们并没有处理这个异常
throw new ArrayIndexOutOfBoundsException();
} else {
System.out.println(arr[index]);
}
}
}
try-catch常见问题
1.如果try中没有遇到问题,怎么执行
会把try中所有代码全部执行完毕,不会执行catch里面的代码
2.如果try中遇到问题,try下面的代码还会执行吗?
那么直接跳转到对应的catch语句中,try下面的代码就不会在执行了
当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码
3.如果出现的问题没有被捕获,那么程序如何运行?
那么try…catch就相当于没有写,也就是自己放弃了处理这个异常,交由调用者或者虚拟机处理这个异常
4.同时出现多个异常怎么处理?
出现多个异常,写多个catch就可以了
注意:如果多个异常之间存在子父类关系,父类一定要写在最下面,否则无法精确处理出现的异常
try{
可能会出现异常的代码段
}catch (异常类名1 变量名){
处理方案1
}catch (异常类名2 变量名){
处理方案2
}catch(Exception ex){
处理方案3 // 注意,Exception是所有异常类的父类,需要写在下面
}
Throwable中的常见方法
- 常用方法
方法名 | 说明 |
---|---|
public String getMessage() | 返回此throwable的详细消息字符串 |
public String toString() | 返回此throwable的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
- 示例代码
public class ExceptionDemo5 {
public static void main(String[] args) {
// 异常处理,把可能出现异常的代码加入到try里面,
// 当出现异常的时候,用catch捕获并做处理
try {
method1();//调用方法的时候,如果输入不符合要求的值,我们就会接收到一个异常对象
// 我们需要自己处理这个异常
} catch (ArrayIndexOutOfBoundsException ex) {
// 返回此throwable的详细消息字符串
System.out.println(ex.getMessage());
// 返回throwable的简短描述
System.out.println(ex.toString());
// 将错误信息显示在控制台
ex.printStackTrace();
}
}
private static void method1() {
int[] arr = {1, 2, 3, 4, 5};
Scanner sc = new Scanner(System.in);
System.out.println("请输入索引值:");
int index = sc.nextInt();
System.out.println(arr[index]);
}
}
异常的练习
- 需求
键盘录入学生的姓名和年龄,其中年龄为18-25岁,超出这个范围使异常数据不能赋值.需要重新录入,一直录到正确为止
- 实现步骤
- 创建学生对象
- 键盘录入姓名和年龄,并赋值给学生对象
- 如果是非法数据,就再次录入
- 代码实现
// 学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 18 && age <= 25) {
this.age = age;
} else {
throw new IndexOutOfBoundsException("年龄超出范围");
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 异常类
public class IndexOutOfBoundsException extends RuntimeException {
public IndexOutOfBoundsException() {
}
public IndexOutOfBoundsException(String message) {
super(message);
}
}
// StudentController类
import java.util.InputMismatchException;
import java.util.Scanner;
public class StudentController {
public static void main(String[] args) {
Student stu = new Student();
Scanner sc = new Scanner(System.in);
System.out.println("请输入姓名");
stu.setName(sc.next());
System.out.println("请输入年龄(范围18-25):");
String age;
while(true){
age = sc.next();
try{
int age1 =Integer.parseInt(age);
stu.setAge(age1);
break;
}catch(NumberFormatException ex){ // 如果输入的是小数,走的是这个
System.out.println("输入非法,请输入数字");
continue;
}
catch(InputMismatchException ex){ // 如果输入的字符串,走这个
System.out.println("输入非法,请输入数字");
continue;
}
//捕捉年龄输入的异常,从年龄学生类的set方法中抛出的异常
catch (IndexOutOfBoundsException ex){
System.out.println("您输入的年龄越界,请重新输入");
continue;
}
}
System.out.println(stu);
}
}
小结
1.什么是异常
程序中的不正常称为异常
2.异常的分类
编译器异常和运行时异常
3.谁处理异常
原则上是谁调用谁处理,不想处理throws声明异常,实际开发过程中,尽量不要让jvm处理异常
4.如何捕获异常
通过try…catch来捕获
5.如何手动抛出异常
throw new异常类
6.为什么要自定义异常
在实际开发过程中,有很多异常jdk没有帮我们定义好的,比如age负数或超出范围的异常,因此我们需要根据实际业务自定义异常
7.如何自定义异常
继承RuntimeException,调用父类中的带参构造方法
8.实际开发过程中如何使用异常的原则(看情况)
1.Java类库中定义的可以预检测方式避免,RuntimeException异常不因该通过catch的方式来处理,比如NullPointerException,IndexOutofBoundsException等等
2.catch 时分清稳定代码和非稳定代码,稳定代码指的是无论如何都不会出错的代码,对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理
3.捕获异常是为了处理它,不要捕获了却什么都处理而抛弃,如果不想处理,请将该异常抛弃给它的调用者.最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容
4.捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛出异常的父类
5.避免直接抛出new RuntimeException(),更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常.推荐业界已定义过的自定义异常,如DAOException/ServiceException等