第十章、 异常Exception
10.1 异常的概念
- 引出异常:
(1). 运行下面代码,看看有什么问题---->引出异常处理和异常处理机制
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class Exception01 {
public static void main(String[] args) {
int num1=10;
int num2=0;
//当除数为0时,就会抛出异常:ArithmeticException,当抛出异常后,程序就退出了,不再执行下面的代码了
//问题来了:当一个程序很大的时候,只是出现了一点小bug,不影响整个程序运行的时候,如果因为这个小bug而停止了整个程序运行
//是不划算的,所以就提出来异常处理解决这个问题
int res=num1/num2;
System.out.println("程序正常运行.....");
}
}
(2). 对异常进行捕获,保证程序可以正常运行
将可能会出现的代码块–>选中–>快捷键ctrl+alt+t–>选中try-catch/直接按6
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class Exception01 {
public static void main(String[] args) {
int num1=10;
int num2=0;
//当除数为0时,就会抛出异常:ArithmeticException,当抛出异常后,程序就退出了,不再执行下面的代码了
//问题来了:当一个程序很大的时候,只是出现了一点小bug,不影响整个程序运行的时候,如果因为这个小bug而停止了整个程序运行
//是不划算的,所以就提出来异常处理解决这个问题
try {
int res=num1/num2;
} catch (Exception e) {
e.printStackTrace();//抛出异常
System.out.println(e.getMessage());//输出异常信息
}
System.out.println("程序正常运行.....");
}
}
/*
/ by zero
程序正常运行.....
*/
- 异常基本介绍
(1). 基本概念
Java语言中,将程序执行中发送不正常的情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
(2). 执行过程中所发送的异常事件可分为两类
① Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。如比:StackOverflowError[栈溢出]和OOM(out of memory内存不足),Error是严重错误,程序就崩溃。
② Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。比如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常[程序运行时,发送的异常]和编译时异常[编程时,编译器检测出的异常]
10.2 异常体系图(重点)
-
异常体系图,体系了继承和实现关系(简易版)
在IDEA中输入关键字Throwable后,ctr+b查看源码,然后直接右键->图表->显示图表。
选中Throwable,ctrl+alt+b可以添加子类或者右键显示实现就可以添加;ctrl+alt+b后直接按字母进行搜索即可出现搜索框。
-
异常体系图(完整版)
- 异常体系图的小结
(1). 异常分为两大类,运行时异常和编译时异常
(2). 运行时异常(编译器检测不出来),编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。Java.lang.RuntimeException类及它的子类都是运行时异常。
(3). 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率可以产生影响。
(4). 编译时异常,是编译器要求必须处置的异常。比如:读取文件时,文件不存在,这就是编译器异常,必须处理才可执行。
10.3 常见的异常
- 常见的运行时异常
(1). NullPointerException空指针异常
(2). ArithmeticException数字运算异常
(3). ArrayIndexOutOfBoundsException数组下标越界异常
(4). ClassCastException类型转换异常
(5). NumberFormatException数字格式不正常异常[]
(1). NullPointerException空指针异常
当应用程序试图在需要对象的地方使用null时,抛出异常。
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class NullPointerException_ {
public static void main(String[] args) {
String string=null;
System.out.println(string.length());
}
}
/*
Exception in thread "main" java.lang.NullPointerException
at chapter10.exception_.NullPointerException_.main(NullPointerException_.java:10)
*/
(2). ArithmeticException数字运算异常
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
(3). ArrayIndexOutOfBoundsException数组下标越界异常
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
int[] arr={1,2,3,4};
for (int i = 0; i <= arr.length; i++) {
System.out.println(arr[i]);
}
}
}
/*
1
2
3
4
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
at chapter10.exception_.ArrayIndexOutOfBoundsException_.main(ArrayIndexOutOfBoundsException_.java:11)
*/
(4). ClassCastException类型转换异常
当试图将对象强制转换为不是实例的子类时,抛出该异常。例如,一下代码将生成一个ClassCastException异常。
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class lassCastException_ {
public static void main(String[] args) {
A a=new B();//向上转型
B b=(B)a;//向下转型
C c=(C)a;//
}
}
class A{}
class B extends A{}
class C extends A{}
/*
Exception in thread "main" java.lang.ClassCastException: class chapter10.exception_.B cannot be cast to class
chapter10.exception_.C (chapter10.exception_.B and chapter10.exception_.C are in unnamed module of loader 'app')
at chapter10.exception_.lassCastException_.main(lassCastException_.java:11)
*/
(5). NumberFormatException数字格式不正常异常[]
当应用程序试图将字符串转成一种数值类型,但该字符串不能转换为设当格式时,抛出该异常=> 使用异常我们可以确保输入是满足条件数字。
package chapter10.exception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class NumberFormatException_ {
public static void main(String[] args) {
String name="hello";
int num=Integer.parseInt(name);
}
}
/*
Exception in thread "main" java.lang.NumberFormatException: For input string: "hello"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Integer.parseInt(Integer.java:652)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at chapter10.exception_.NumberFormatException_.main(NumberFormatException_.java:10)
*/
-
常见的编译异常
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
(1). SQLException 操作数据库时,查询表可能发生异常
(2). IOException 操作文件时,发生的异常
(3). FileNotFoundException 当操作一个不存在的文件时,发生异常
(4). ClassNotFoundException 加载类,而该类不存在时,发生异常
(5). EOFException 操作文件,到文件末尾,发生异常
(6). IllegalArgumentException 参数异常 -
判断下面代码是否正确,为什么?
10.4 异常处理概念
-
基本介绍
异常处理就是当异常发生时,对异常处理的方式。 -
异常处理的方式
(1). try-catch-finally
程序员在代码中捕获发生的异常,自行处理
(2). throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM。 -
try-catch-finally处理机制
-
throws处理机制
10.5 异常处理分类
10.5.1 try-catch方式处理异常
java提供try和catch块来处理异常。try块用于包含可能会出现错误的代码。Catch块用于处理try块中发生的异常。可以根据需要再程序中有多个数量的try…catch块。
- 基本语法
try{
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch (异常){
//对异常的处理
}
//如果没有finally,语法是可以通过的
- try-catch方式处理异常-注意事项
(1). 如果异常发生了,则异常发生后面的代码不会执行,直接进入到代码块。
(2). 如果异常没有发生,则顺序执行try的代码块,不会进入到catch。
(3). 如果不希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),在后面加上finally{}。
try{
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch (异常){
//对异常的处理
}finally {
//释放资源等..
}
如下代码,例子:TryCatchDetail01
package chapter10.try_;
import java.util.Scanner;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchDetail01 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
String str=scanner.next();
try {
int a=Integer.parseInt(str);//将输入的字符串类型的数字转换为整数类型
System.out.println("数字:"+a);//如果输入不是整数,这句话就不会执行
} catch (NumberFormatException e) {
System.out.println("异常信息:"+e.getMessage());
}finally {
scanner.close();//不论是否发生异常都关闭资源
}
}
}
/*
输入:123
数字:123
输入:fa1
异常信息:For input string: "fa1"
*/
(4). 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch。
try {
}catch (NullPointerException){
}catch (Exception){
}finally {
}
例子TryCatchDetail02
package chapter10.try_;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchDetail02 {
public static void main(String[] args) {
try {
Person person = new Person();
// person=null;//NullPointerException
person.say();
int i=10/0;//ArithmeticException
//要求子类异常要写在父类异常前面,不然会报错
} catch (NullPointerException e){
System.out.println("空指针异常"+e.getMessage());
} catch (Exception e) {
System.out.println("算术异常"+e.getMessage());
}finally {//这里可以没有
System.out.println("有没有异常都执行....");
}
}
}
class Person{
public void say(){}
}
/*
算术异常/ by zero
有没有异常都执行....
*/
(5). 可以进行try-finally配合使用,这种用法相等于没有捕获异常,因此程序会直接崩掉。应用场景,就是执行一端代码,不管是否发生异常,都必须执行某个业务逻辑。
try{
//代码
}final {//总是执行
}
例子:TryCatchDetail03
package chapter10.try_;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchDetail03 {
public static void main(String[] args) {
try {
int n1=10;
int n2=0;
System.out.println(n1/n2);
} finally {
System.out.println("执行了finally...");
}
System.out.println("程序继续执行...");
}
}
/*
执行了finally...
Exception in thread "main" java.lang.ArithmeticException: / by zero
at chapter10.try_.TryCatchDetail03.main(TryCatchDetail03.java:12)
*/
- 练习
(1). 判断下面代码输出是什么?
package chapter10.try_;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchExercise01 {
public static void main(String[] args) {
System.out.println(method());//4
}
public static int method(){
try {
String[] strings=new String[3];
if(strings[1].equals("mark")){//NullPointerException
System.out.println(strings[1]);
}else {
strings[3]="smith";
}
return 1;
} catch (NullPointerException e) {
return 2;
}catch (ArrayIndexOutOfBoundsException e){
return 3;
}finally {
return 4;
}
}
}
(2). 判断下面代码输出是什么?
package chapter10.try_;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchExercise02 {
public static void main(String[] args) {
System.out.println(method());//4
}
public static int method(){
int i=1;
i++;
try {
String[] strings=new String[3];
if(strings[1].equals("mark")){//NullPointerException
System.out.println(strings[1]);
}else {
strings[3]="smith";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
}catch (NullPointerException e){
return ++i;
}finally {//必须执行
return ++i;
}
}
}
(3). 判断下面代码输出是什么?
package chapter10.try_;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchExercise03 {
public static void main(String[] args) {
System.out.println(method());
//i=4
//3
}
public static int method(){
int i=1;
i++;
try {
String[] strings=new String[3];
if(strings[1].equals("mark")){//NullPointerException
System.out.println(strings[1]);
}else {
strings[3]="smith";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
}catch (NullPointerException e){
return ++i;//i=3=>保存临时变量temp=3
}finally {//必须执行
++i;//4
System.out.println("i="+i);
}
}
}
(4). 如果用户输入的不是一个整数,就提示他反复输入,知道输入一个整数为止。(用异常来做)。(肯定要循环的,不然try块 和catch块不管有没有问题,都只会执行一次)
package chapter10.try_;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* @aim java基础学习
* @note java笔记
*/
public class TryCatchExercise04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int input = 0;
boolean validInput = false;
while (!validInput) {
try {
System.out.print("请输入一个整数: ");
input = scanner.nextInt();
validInput = true; // 如果能成功获取整数则退出循环
} catch (InputMismatchException e) {
System.out.println("对不起,你输入的不是整数,请重新输入~~~~");
scanner.nextLine(); // 清除缓冲区的非整数输入
}
}
System.out.println("你输入的整数是: " + input);
scanner.close();
}
}
/*
请输入一个整数: 123f
对不起,你输入的不是整数,请重新输入~~~~
请输入一个整数: fa
对不起,你输入的不是整数,请重新输入~~~~
请输入一个整数: 12
你输入的整数是: 12
*/
- try-catch-finally执行顺序小结
(1). 如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally,最后还需执行finally里面的语句。
(2). 如果出现异常,则try块中异常发生后,try块剩下的语句不再执行,将执行catch块中的语句,如果有finally,最后还需执行finally里面的语句。
10.5.2 Throws异常处理
- 基本介绍
(1). 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如果处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
(2). 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
package chapter10.throws_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* @aim java基础学习
* @note java笔记
*/
public class Throws01 {
public static void main(String[] args) {
}
public void f2() throws FileNotFoundException {
//1. 这里的异常是一个FileNotFoundException,属于编译异常
//2. 这里可以使用try-catch-finally
//3. 也可以使用throws,抛出异常,让调用f2方法的调用者(方法)处理
//4. throws后面的异常类型可以是具体产生的异常,也可以是父类异常
//5. throws后面可以写多个异常,用逗号分隔。即异常列表。若父类异常都是一样的可以用父类异常替代
FileInputStream fis=new FileInputStream("D://a.test");
}
}
- throws注意事项和使用细节
(1). 对应编译异常,程序中必须处理,比如:try-catch-或者throws。
(2). 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
(3). 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型。
(4). 在throws过程中,如果有方法try-catch,相当于处理异常,就可以不必throws。
package chapter10.throws_;
import java.io.FileNotFoundException;
/**
* @aim java基础学习
* @note java笔记
*/
public class ThrowsDetail {
public static void main(String[] args) {
}
public void f1(){
//1.这里调用f2()会报错,因为f2抛出了一个编译异常
//2.f1作为f2的调用者,必须处理这个编译异常
//3.要么使用try-catch-finally处理,或者继续throws 这个编译异常
f2();
}
public void f2() throws FileNotFoundException {}
public void f3(){
//1.这里调用f4()不会报错,因为f4抛出了一个运行异常
//2.在Java中,并不要求程序员显示处理运行异常,因为有默认处理机制
f4();
}
public void f4() throws ArithmeticException{}
}
class Father{
public void method() throws RuntimeException{}
}
class Son extends Father{
//(3). 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,
// 所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型。
@Override
public void method() throws NullPointerException{}
}
10.6 自定义异常
-
基本概念
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。 -
自定义异常的步骤
(1). 定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException
(2). 如果继承Exception,属于编译异常
(3). 如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException) -
自定义异常的应用案例CustomException.java
当接收Person对象年龄时,要求范围在18-120之间,否则抛出一个自定义异常(要求 继承RuntimeException),并给出提示信息。
package chapter10.customexception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class CustomException01 {
public static void main(String[] args) {
int age=12;
if(!(age>=18 && age<=120)){
throw new AgeException("年龄需要在18-120之间");
}
System.out.println("你的年龄范围正确");
}
}
//1.一般情况下,都是自定义异常是继承RuntimeException
//2.即把自定义异常做成 运行时异常,好处是,可以使用默认的处理机制(比较方便)
class AgeException extends RuntimeException{
public AgeException(String message) {
super(message);
}
}
/*
Exception in thread "main" chapter10.customexception_.AgeException: 年龄需要在18-120之间
at chapter10.customexception_.CustomException01.main(CustomException01.java:11)
*/
10.7 throw和throws的对比
- throw和throws的区别
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
package chapter10.customexception_;
/**
* @aim java基础学习
* @note java笔记
*/
public class ThrowException {
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
methodB();
}
/*
1.首先调用methodA方法,进入catch块:输出进入方法A,抛出异常给对象e,再进入finally块:输出用A方法的finally,最后main方法的e.getMessage()
2.调用methodB方法,进入catch块:输出进入方法B,再进入finally块:输出调用B方法的finally
3.输出如下:
进入方法A
用A方法的finally
制造异常
进入方法B
调用B方法的finally
*/
static void methodA(){
try {
System.out.println("进入方法A");
throw new RuntimeException("制造异常");
} finally {
System.out.println("用A方法的finally");
}
}
static void methodB(){
try {
System.out.println("进入方法B");//4
return;
} finally {
System.out.println("调用B方法的finally");//5
}
}
}
10.8 本章作业
- 编程题
(1). 编写应用程序EcmDef.java,接收命令行的两个参数(整数),计算两数相除。
(2). 计算两个数相除,要求使用方法cal(int n1,int n2)
(3). 对数据格式不正确,缺少命令行参数、除0进行异常处理。
解析:什么是命令行参数?其实就是args的参数。前面讲过怎么传入args参数。
package chapter10.task_;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* @aim java基础学习
* @note java笔记
*/
public class Task01 {
public static void main(String[] args) {
try {
// 检查是否提供了足够的命令行参数
if (args.length != 2) {
throw new IllegalArgumentException("需要输入两个整数作为命令行参数");
}
// 将命令行参数转换为整数
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
// 计算并输出结果
double result = cal(n1, n2);
System.out.println("两数相除的结果是:" + result);
} catch (NumberFormatException e) {
System.out.println("输入的参数格式不正确,请输入整数。");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} catch (ArithmeticException e) {
System.out.println("除数不能为零。");
}
}
public static double cal(int n1, int n2) {
if (n2 == 0) {
throw new ArithmeticException("除数不能为零。");
}
return (double) n1 / n2;
}
}
//输出:两数相除的结果是:2.0
- 判断下面代码是否会发生异常,如果会,是哪种异常?如果不会,则打印结果是什么?
package chapter10.task_;
/**
* @aim java基础学习
* @note java笔记
*/
public class Task02 {
public static void main(String[] args) {
if(args[4].equals("john")){//可能发生NullPointerException
System.out.println("hello");
}else{
System.out.println("hi");
}
Object obj=args[2];
Integer i =(Integer) obj;//一定会发生ClassCasException
}
}
- 写出下面代码输出结果
package chapter10.task_;
/**
* @aim java基础学习
* @note java笔记
*/
public class Task03 {
public static void main(String[] args) {
try {
func();
System.out.println("a");
} catch (Exception e) {
System.out.println("c");//2
}
System.out.println("d");//3
}
public static void func(){
try {
throw new RuntimeException();
}finally {
System.out.println("b");//1
}
}
}
- 写出下面代码输出结果
package chapter10.task_;
/**
* @aim java基础学习
* @note java笔记
*/
public class Task04 {
public static void main(String[] args) {
try {
showExce();
System.out.println("a");
} catch (Exception e) {
System.out.println("b");//1
} finally {
System.out.println("c");//2
}
System.out.println("d");//3
}
public static void showExce() throws Exception{
throw new Exception();
}
}