一、异常介绍
异常是程序中可能出现的问题,用Exception表示
Exception:异常,代表程序可能出现的问题,通常用Exception以及它的子类来封装程序出现的问题
运行时异常:RuntimeException及其子类,在编译阶段不会出现异常提醒,在运行时会报错,如:数组的下标越界
编译异常:在编译阶段就会提醒的异常
二、异常的作用
1.用来查询bug的关键参考信息
如:数组越界异常
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr[3]);
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Exception_test.Test1.main(Test1.java:6)
2.可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
①创建一个Student的类,并创建一个方法
public class Student {
private int age;
public void setAge(int age) {
if(age<40&&age>18){
this.age = age;
}else{
System.out.println("年龄超出范围");
}
}
}
②在测试类中调用此方法
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setAge(50);
}
}
③运行后控制台显示的结果
年龄超出范围
进程已结束,退出代码为 0
这个程序的关系图
因此如果在被调用的方法中抛出异常,调用者就会得到异常的提示,从而选择处理该异常的方法
public class Student {
private int age;
public void setAge(int age) {
if(age<40&&age>18){
this.age = age;
}else{
throw new RuntimeException();
}
}
}
此时的运行结果为
Exception in thread "main" java.lang.RuntimeException
at Exception_test.test2.Student.setAge(Student.java:9)
at Exception_test.test2.Test.main(Test.java:6)
三、异常的处理方式
1.jvm默认的处理方案
①把异常的名称,异常的原因和异常出现的位置打印在控制台上
②程序停止执行,下面的代码不会再执行
2.捕获异常
格式:try{ //可能出现错误的代码 }catch (异常类名 变量名){ //异常的处理代码 }
public class Test3 {
public static void main(String[] args) {
int[] arr = {1,2,3,4};
try{
System.out.println(arr[10]);
}catch (ArrayIndexOutOfBoundsException a){
System.out.println("数组越界");
}
System.out.println("测试是否执行该语句");
}
}
如果出现异常,将会执行catch中的语句,执行完成后继续执行下面的语句
其中的ArrayIndexOutOfBoundsException是程序出现异常的类名
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Exception_test.Test1.main(Test1.java:6)
常见的问题:
①如果try中没有出现异常,则会将try中的代码执行完毕后执行catch后面的代码,不会执行catch中的代码
②如果try中遇到了多种异常,则需要在try后面写多个catch语句,分别对应每种异常(如果异常类种存在父子关系,父类要写到下面)
在jdk7之后,一个catch中可以捕获多个异常,中间用 | 隔开,如catch( 1 | 2),表示对这两种异常采用同一种处理方式
③如果try中出现的异常没有被捕获,则会采用jvm默认处理异常的方式
④如果try中的某一条语句出现了异常,则会跳转到catch中,不会执行该语句下面的语句
3.抛出异常
编译异常:必须要写
运行异常:可以不写
①throws:写在方法定义处,表示声明一个异常告诉调用者,使用本方法可能会有哪些异常,便于调用者对异常进行处理
格式:public void 方法名()throws 异常类名1,异常类名2…{}
public class Test4 {
public static void main(String[] args) {
int[] arr = null;
try {
getMax(arr);
} catch (NullPointerException e) {
System.out.println("测试");
}
}
public static void getMax(int[] arr) throws NullPointerException{
int max=0;
for (int i = 0; i < arr.length; i++) {
if(max<arr[i])max=arr[i];
}
}
}
②throw:写在方法内,结束方法,手动抛出异常对象,交给调用者
格式:public void 方法名(){throw new 异常类名();}
public class Test4 {
public static void main(String[] args) {
int[] arr = null;
try {
getMax(arr);
} catch (NullPointerException e) {
System.out.println("测试");
}
}
public static void getMax(int[] arr){
int max=0;
for (int i = 0; i < arr.length; i++) {
if(arr==null)throw new NullPointerException();
if(max<arr[i])max=arr[i];
}
}
}
四、异常中常见的方法
1.public String getMessage();返回异常的原因
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[3];
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
String message = e.getMessage();
System.out.println(message);
}
}
}
Index 3 out of bounds for length 3
2.public String toString();返回异常的名字和原因
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[3];
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
String message = e.toString();
System.out.println(message);
}
}
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
3.public void printStackTrace();打印异常的名字,原因和地址
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[3];
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Exception_test.Test1.main(Test1.java:7)
五、例题
创建一个学生类,构造方法为无参构造,定义年龄和姓名,设置姓名时长度范围为3-10,年龄的范围是18-40,并且给出输入数字之外的内容的处理方法,如在输入年龄时输入aaa(注:运行异常可以不用手动抛出异常)
1.创建学生对象,并抛出异常
在RuntimeException后的括号内可以自定义异常的原因
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) {
if(name.length()<3||name.length()>10)throw new RuntimeException();
else this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age<18||age>40)throw new RuntimeException();
else this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
2.创建测试类,并捕获异常
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Student s = new Student();
boolean flag=true;
do {
try {
System.out.println("请输入姓名:");
String name = sc.next();
s.setName(name);
sc.nextLine();
System.out.println("请输入年龄:");
String s1 = sc.nextLine();
int age=Integer.parseInt(s1);
s.setAge(age);
flag=false;
} catch (NumberFormatException e){
System.out.println("年龄输入的格式有误,请重新输入");
} catch (RuntimeException e) {
System.out.println("输入名字的长度或年龄的范围不符合要求,请重新输入");
}
}while(flag);
System.out.println(s.toString());
}
}
六、自定义异常
在上面的例题中,名字长度和年龄的范围都直接抛出RuntimeException,是运行异常的最顶级的父类,不能清晰描述出现的异常,并且在判断两者时不能将两者区分开,因此可以创建一个自定义异常(运行异常继承于RuntimeException,编译异常继承于Exception),用来具体描述这两种异常
定义异常类的步骤
①创建异常类
②写继承关系
③空参构造
④带参构造
1.用于年龄范围的异常
public class AgeOutOfBoundsException extends RuntimeException{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
2.用于名字长度的异常
public class NameFormatException extends RuntimeException{
public NameFormatException() {
}
public NameFormatException(String message) {
super(message);
}
}
3.将抛出异常的两个方法修改为对应的自定义异常
public void setName(String name) {
if(name.length()<3||name.length()>10)throw new NameFormatException("输入的姓名长度有误,请重新输入");
else
this.name = name;
}
public void setAge(int age) {
if(age<18||age>40)throw new AgeOutOfBoundsException("输入的年龄范围有误,请重新输入");
else
this.age = age;
}
4.在catch中直接调用异常的方法,把异常的位置,名字和原因打印出来
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Student s = new Student();
boolean flag=true;
do {
try {
System.out.println("请输入姓名:");
String name = sc.next();
s.setName(name);
sc.nextLine();
System.out.println("请输入年龄:");
String s1 = sc.nextLine();
int age=Integer.parseInt(s1);
s.setAge(age);
flag=false;
} catch (NumberFormatException e){
e.printStackTrace();
} catch (AgeOutOfBoundsException e) {
e.printStackTrace();
}catch (NameFormatException e){
e.printStackTrace();
}
}while(flag);
System.out.println(s.toString());
}