异常
异常的概念:
- 异常指在程序运行期间,JVM遇到了无法编译的代码,这种情况对于JVM来说就是一种“异常情况”。当玉带这种异常情况,JVM就会在控制太打印“异常信息”,并结束程序。
- Java的“异常处理机制”就可以使JVM在遇到“异常代码”时,可以“跳过”有异常的代码,继续执行后续代码。
代码演示:
package com.代码练习.test01;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
//外部进行while死循环,内部输入正确则结束循环。
while(true) {
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入年龄:");
int age = sc.nextInt();//假如用户输入的不是int类型,则会出现异常:InputMismatchException
System.out.println("你的年龄是:"+age);
break;//
} catch (InputMismatchException e) {
//这个地方就可以进行对异常处理并输出提示语:
System.out.println("输入错误");
}
}
System.out.println("后续代码。。。。");
}
}
异常的产生过程及JVM默认处理异常的方式
异常产生过程:
-
JVM执行到有异常的代码;
-
JVM必须要能够识别出这种异常;
-
JVM会到类库中找到“描述这个异常的异常类”,并创建对象;
-
JVM会查看我们的代码是否有catch语句,“捕获”这个异常;
没有catch语句:就在控制台打印异常信息,然后结束程序;【默认情况】 有catch语句:跳转到catch语句中,程序就不会结束,会跳过有异常的代码,继续健康的运行下去。
JVM默认处理异常的方式:
当出现异常,会在控制台打印“异常信息”,并结束程序。
异常的体系结构及分类
- Java内部针对任何Java程序所可能出现的所有“异常情况”都做了定义,并制定了对应的“异常类”。
很多的异常类,Java内部又进行抽取,最终形成了一套“异常体系结构” - 异常体系结构:
Throwable(类)
1. Error错误:不需要晨旭捕获,因为捕获没有意义,程序必须结束。
package com.代码练习.test02;
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
//定义一个int的包装类数组,大概4M左右
Integer[] arr = new Integer[1000*1000];
//定义一个泛型是Integer类型的集合
ArrayList<Integer> inlist = new ArrayList<>();
for (int i = 0; i >=0 ; i++) {
//在集合中添加arr数组
inlist.add(arr[i]);
//报错Error:ArrayIndexOutOfBoundsException
//原因:无论Java虚拟机的内存有多大,for死循环下,会一直给集合添加4M大小的数组,最终会导致Java虚拟机内存不足。
}
}
}
2. Exception(异常):需要程序处理的,程序运行过程中产生的一些“异常情况”。
2.1 RuntimeException(运行时异常):指比较低级的异常情况,这种异常是不应该出现的,程序员完全可以通过if判断避免这种异常情况产生,例如:NullPointerException,ArrayIndexOutOfBoundsException
2.2 除RunTimeException外的其它异常类(编译期异常):指程序员无法事先判断出来,很难预料的异常情况。这种异常情况必须要处理。
异常的处理
try…catch语句的方式处理异常
-
异常处理的基本语法:
try{
}catch(异常类名 变量名){
} -
可以捕获到异常的正常情况:
public class Demo05 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入年龄:");
int age = sc.nextInt();
System.out.println("你的年龄是:" + age);
}catch (InputMismatchException e){
System.out.println("你的输入有误!!");
}
System.out.println("后续代码......");
}
}
- 不能捕获到异常的情况:
public class Demo05 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入年龄:");
int age = sc.nextInt();//可能出现的是:InputMismatchException异常
System.out.println("你的年龄是:" + age);
}catch (NullPointerException e){//而捕获的是:NullPointerException异常,所以此catch无效
System.out.println("你的输入有误!!");
}
System.out.println("后续代码......");
}
}
- 可以捕获所有异常的情况:【不建议】
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入年龄:");
int age = sc.nextInt();//可能出现的是:InputMismatchException异常
System.out.println("你的年龄是:" + age);
String s = null;
System.out.println(s.length());
}catch (Throwable e){//而捕获的是:NullPointerException异常,所以此catch无效
System.out.println("出现异常!");
}
System.out.println("后续代码......");
5).建议的写法:
try {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入年龄:");
int age = sc.nextInt();//可能出现的是:InputMismatchException异常
System.out.println("你的年龄是:" + age);
} catch (InputMismatchException e) {
System.out.println("输入错误!");
}
try {
String s = null;
System.out.println(s.length());
} catch (NullPointerException e) {//可能出现NullPointerException异常
System.out.println("空指针异常!!");
}
System.out.println("其他代码....");
}catch (Throwable e){
System.out.println("程序出现未知异常,请联系系统管理员!!");
//写日志——将异常信息写入到文件,后期查看这个文件,找到问题,对程序打补丁。
}
System.out.println("后续代码......");
注意:try{}和catch{},在它们的大括号内部定义的变量,只能在那个大括号内部使用。也就是说:在try中定义的变量,在catch中不能使用。finally可以使用
操作异常对象:
当JVM执行try中代码,出现异常时,会识别这个异常,并到类库中找到这个“异常类”,然后创建它的对象,然后判断程序是否有catch来捕获这个异常,有,就执行这个catch——实际上是调用catch方法,JVM会将“异常对象”作为“实参”传入到catch方法。我们可以操作这个异常对象。
常用方法:
- public String getMessage();获取异常信息
- public String toString();获取异常类名+异常信息
- public String printStackTrace(); 获取异常类名+异常信息+错误位置提示。
代码演示:
package com.代码练习.test03;
public class Demo01 {
public static void main(String[] args) {
int arr[] = {1,2,3};
try {
System.out.println(arr[1]);//正常
System.out.println(arr[10]);//异常:数组越界
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("异常了");
//getMessage()方法:异常信息
System.out.println("异常信息:"+e.getMessage());
//toString()方法:异常类名+异常信息
System.out.println("toString:"+e.toString());
//printStackTeace()方法:异常类名+异常信息+错误代码提示
e.printStackTrace();
}
}
}
try…catch…catch语句的方式处理异常
- 在try{}中,如果某行代码出现异常,try{}中此行代码后面的代码就全部跳过。
- 一个try中可能会出现多种异常的情况,可以使用多个catch进行捕获:
语法:
try{
//可能出现异常的代码
}catch(异常类名1 变量名){
}catch(异常类名2 变量名){
}…
注意:
1. try中某行代码出异常,try中的后续代码全部跳过。所以通常是“后面的代码”依赖“前面的代码”,如果前面成功,后面被执行,如果前面失败,那么后面也不执行了,这种情况才使用这种try…catch…catch…的语法。
2. 在“多catch”中,它们没有顺序关系。
3. 在“多catch”中,使用的“异常类名”通常也是“同级的”;也可以是“子父级”的,但“父级”的异常类型必须放在多catch的末尾。
try…catch…finally语句的方式处理异常
1).语法格式:
try{
}catch(异常类名1 变量名){
}catch(异常类名2 变量名){
}finally{
//无论是否出现异常,都会被执行的代码
}
这种格式,通常用在一个方法中,而且在try和catch中有return 值;语句时,在保证返回前做一些事情,就可以写在finally中。
代码演示:
package com.代码练习.test05;
public class Demo01 {
public static void main(String[] args) {
String s = readFile();
System.out.println("-----------");
System.out.println(s);
}
private static String readFile() {
try {
System.out.println("打开文件");//可能出异常
System.out.println("读取文件"+(10/0));//可能出异常
return "文件内容";
}catch (Exception e){
System.out.println("异常了!!!");
return null;
}finally{//无论上面代码是否出现异常,都会执行finally
System.out.println("关闭文件!");//可能出现异常
}
}
}
throw抛出异常
- throw关键字:表示在方法中可以“抛出一个异常对象”。
- 第一种使用情况(图解):
- 第二种使用情况:
第二种可以自己设置“异常信息”。
throws声明抛出异常【常用】
- throws用在“方法声明”处,表示:此方法可能会产生这种异常,如果产生,请JVM将这个异常抛给调用代码。
public class Demo11 {
public static void main(String[] args) {
try {
int[] arr = new int[0];
int sum = getMax(arr);
System.out.println("和:" + sum);
} catch (NullPointerException e) {
System.out.println("空指针异常!");
} catch (ArrayIndexOutOfBoundsException e) {
} catch (ArithmeticException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
public static int getMax(int[] arr) throws NullPointerException,
ArrayIndexOutOfBoundsException,
ArithmeticException ,Exception{
int sum = arr[0];//抛出索引越界异常
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
}
throw与throws区别:
-
throw:
1).用在方法内部; 2).throw + (一个)异常对象; 3).throw 如果抛出的是“运行时异常(RuntimeException)”,调用代码可以不处理,编译可以通过; 如果抛出的是“编译期异常(除RuntimeException及其子类以外的其他异常)”,调用的代码必须处理,要么try...catch..,要么继续抛出。 常见的“运行时异常”:NullPointerException、ArrayIndexOutOfBoundsException、StringIndexOutOfBoundsException、InputMismatchException 常见的“编译期异常”:ParseException(SimpleDateFormae的parse()方法会抛出此异常)
-
throws:
1).用在方法的“声明处”; 2).throws + (一个/多个)异常类名(属于声明) 3).如果声明抛出的是“运行时异常”,调用代码可以不处理,编译可以通过; 如果声明抛出的是“编译期异常”,调用代码必须处理,要么try...catch...,要么继续抛出。
子类重写父类方法时的异常声明:
汇总:无论父类方法是否抛出异常,子类都可以:
1. 可以不抛出任何异常。
2. 可以抛出任何的“运行时异常”;
3. 子类不能抛出跟父类不同的“编译期”异常;(只能抛出跟父类相同的“编译期”异常)
自定义异常:
- 当我们写程序时,由于业务逻辑的原因,想抛出一个异常,但类库中并没有相关的“异常类”,这时,我们可以自己来定义“异常类”。
public class Student{
private int age;
public void setAge(int age){
if(age < 15 || age > 50){
//return;
throw new 自定义异常对象();
}
this.age = age;
}
}
- 自定义异常类:
如果继承Exception,就是“编译期”异常;
如果继承RuntimeException或者它的子类,就是“运行时”异常;
public class AgeException extends Exception {
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
- 使用自定义异常:
public class Student {
private int age;
public void setAge(int age) throws AgeException {
if (age < 15 || age > 50) {
throw new AgeException("异常,年龄必须在15到50之间!");//
}
this.age = age;
}
public int getAge() {
return age;
}
}
- 测试类:
public class Demo13 {
public static void main(String[] args) {
Student stu = new Student();
try {
stu.setAge(100);
} catch (AgeException e) {//捕获AgeException
System.out.println("异常信息:" + e.getMessage());
e.printStackTrace();
}
System.out.println(stu.getAge());
}
}