介绍
编译时异常:
除RuntimeException和他的子类,其他都是编译时异常。编译阶段需要进行处理,作用在于提醒程序眼
运行时异常:
RuntimeException本身和其所有子类,都是运行时异常。编译阶段不报错,是程序运行时出现的。一般是由于参数传递错误带来的问题
作用:
作用一:异常是用来查询bug的关键信息
作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
异常演示:
数组越界
代码:
public class ExceptionDemo1 {
public static void main(String[] args) {
String[] arr = {"111","222","333"};
System.out.println(arr[10]);
}
}
运行结果:
抛出异常:
目的:
告诉调用者出错了
格式:
在方法体中:throw+异常对象
在定义方法中:在方法后写 throws+可能出现的异常类型(只需要写编译时异常)
题目要求:设定学生对象的年龄应位于18~22之间
代码:
学生类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
if(age < 18 || age > 22){
throw new RuntimeException();
}else {
this.age = age;
}
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
测试类:
public class ExceptionDemo2 {
public static void main(String[] args) {
Student stu = new Student();
stu.setAge(30);
}
}
运行结果:
JVM虚拟机默认处理异常的方式:
1.把异常的名称,异常原因及异常出现的位置等信息以红色字体输出在控制台
2.程序停止执行,异常下面的代码不会再执行
捕获异常:
目的:
不让程序停止
格式:
格式一:
try{
可能出现异常的代码
} catch(异常类名 变量名){
出现上面小括号的异常类后的处理代码
}
格式二:
try{
可能出现异常的代码
} catch(异常类名 变量名){
出现上面小括号的异常类后的处理代码
} finally {
被finally控制的语句一定会执行,除非JVM退出
}
代码演示:
public class ExceptionDemo2 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//可能出现异常的代码
System.out.println(arr[10]);
//↑此处出现了异常,程序就会在这里创建一个ArrayIndexOutOfBoundsException对象
//拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象
//如果能接受,就表示该异常被捕获,执行catch里面对应的代码
//当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码
} catch(ArrayIndexOutOfBoundsException e) {
//如果出现了ArrayIndexOutOfBoundsException异常,则运行下面代码↓
System.out.println("数组越界");
}
System.out.println("后续代码仍执行");
}
}
运行结果:
问题思考:
问题1:
如果try中没有遇到问题,怎么执行?
代码演示:
public class ExceptionDemo3 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//可能出现异常的代码
System.out.println(arr[0]);
} catch(ArrayIndexOutOfBoundsException e) {
//如果出现了ArrayIndexOutOfBoundsException异常,则运行下面代码↓
System.out.println("数组越界");
}
System.out.println("后续代码仍执行");
}
}
运行结果:
答案:
会执行try中的所有代码,不会执行catch中的代码,会执行后续代码
问题2:
如果try中可能遇到多个问题,怎么执行?
代码演示:
public class ExceptionDemo4 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[10]);
System.out.println(2/0);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界");
}
System.out.println("后续代码仍执行");
}
}
运行结果:
答案:
会执行到try中的出现异常的代码,不会执行后续代码,会执行出现指定异常类的catch中的代码,会执行后续代码,一般try中可能遇到多个问题的情况后面应写多个catch涵盖所有可能的异常类。并且如果要捕获多个异常,这些异常存在父子关系的话,那么父类要写到最下边。
问题3:
如果try中遇到的问题没有被捕获,怎么执行?
代码演示:
public class ExceptionDemo5 {
public static void main(String[] args) {
int[] arr = {1,2,3};
try {
System.out.println(arr[10]);
} catch (NullPointerException e) {
System.out.println("空指针异常");
}
System.out.println("后续代码仍执行");
}
}
运行结果:
答案:
这种情况下没有捕获真正的异常,会采用JVM虚拟机默认处理异常的方式
问题4:
如果try中遇到了问题,那么try下面的代码还会执行吗?
代码演示:
public class ExceptionDemo6 {
public static void main(String[] args) {
int[] arr = {1,2,3};
try {
System.out.println(arr[10]);
System.out.println("----------");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界");
}
System.out.println("后续代码仍执行");
}
}
运行结果:
答案:
try下面的代码不会执行,直接跳转到对应的catch中,执行catch里面的语句体,如果没有对应的catch与之匹配,那么还是会交给虚拟机处理。
常见方法:
public String getMessage() 返回此throwable的详细消息字符串 public String toString() 返回此可抛出的简短描述 public void printStackTrace 把异常的错误信息以红色字体输出在控制台 在底层是利用System.err.println进行输出 细节:仅仅是打印信息,不会停止程序运行
代码演示:
public class ExceptionDemo7 {
public static void main(String[] args) {
/*
public String getMessage() 返回此throwable的详细消息字符串
public String toString() 返回此可抛出的简短描述
public void printStackTrace 把异常的错误信息以红色字体输出在控制台
在底层是利用System.err.println进行输出
细节:仅仅是打印信息,不会停止程序运行
*/
int[] arr = {1,2,3};
try {
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
//public String getMessage() 返回此throwable的详细消息字符串
String message = e.getMessage();
System.out.println(message);
System.out.println();
//public String toString() 返回此可抛出的简短描述
String str = e.toString();
System.out.println(str);
System.out.println();
//public void printStackTrace 把异常的错误信息以红色字体输出在控制台
e.printStackTrace();
}
System.out.println("后续代码仍执行");
}
}
运行结果:
综合练习:
需求: 键盘录入人类对象的姓名和年龄 姓名的长度在3~10之间 年龄的范围为18~40岁 超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止 提示: 需要考虑用户在键盘录入时的所有情况 比如:录入年龄时超出范围、录入年龄时录入abc等
代码:
public class ExceptionTest1 {
public static void main(String[] args) {
/*
需求:
键盘录入人类对象的姓名和年龄
姓名的长度在3~10之间
年龄的范围为18~40岁
超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止
提示:
需要考虑用户在键盘录入时的所有情况
比如:录入年龄时超出范围、录入年龄时录入abc等
*/
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
//创建人对象
Person p = new Person();
while (true) {
try {
//录入姓名
System.out.println("请输入姓名");
String name = sc.nextLine();
p.setName(name);
//录入年龄
System.out.println("请输入年龄");
String ageStr = sc.nextLine();
int age = Integer.parseInt(ageStr);
p.setAge(age);
//没有异常直接跳出
break;
}catch (NumberFormatException e) {
System.out.println("年龄格式不符合规范");
} catch (RuntimeException e) {
System.out.println("姓名或年龄不符合要求");
}
}
System.out.println(p);
}
}
运行结果:
自定义异常:
意义:
就是为了让控制台的报错信息更加的见名知意
定义步骤:
①定义异常类
②写继承关系
③空参构造
④带参构造
接下来使用自定义异常完善上述综合练习
代码演示:
异常类NameFormatException:
public class NameFormatException extends RuntimeException{
public NameFormatException() {
}
public NameFormatException(String message) {
super(message);
}
}
异常类AgeOutOfBoundsException:
public class AgeOutOfBoundsException extends RuntimeException{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
Person类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
if(name.length() < 3 || name.length() > 10) {
throw new NameFormatException(name + "不满足长度3~10要求");
}
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
if (age < 18 || age > 40) {
throw new AgeOutOfBoundsException(age + "不属于18~40范围");
}
this.age = age;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + "}";
}
}
测试类:
public class ExceptionTest1 {
public static void main(String[] args) {
/*
需求:
键盘录入人类对象的姓名和年龄
姓名的长度在3~10之间
年龄的范围为18~40岁
超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止
提示:
需要考虑用户在键盘录入时的所有情况
比如:录入年龄时超出范围、录入年龄时录入abc等
*/
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
//创建人对象
Person p = new Person();
while (true) {
try {
//录入姓名
System.out.println("请输入姓名");
String name = sc.nextLine();
p.setName(name);
//录入年龄
System.out.println("请输入年龄");
String ageStr = sc.nextLine();
int age = Integer.parseInt(ageStr);
p.setAge(age);
//没有异常直接跳出
break;
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (NameFormatException e) {
e.printStackTrace();
} catch (AgeOutOfBoundsException e) {
e.printStackTrace();
}
}
System.out.println(p);
}
}