??1 问题引入
运行下面的代码,看看有什么问题?
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
int res = num1 / num2;
System.out.println("程序继续运行....");
}
💻运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.hj.第十二章异常.a.main(a.java:21)
🧐解读:
- num1 / num2 => 10 / 0
- 当执行到 num1 / num2 因为 num2 = 0, 程序就会出现(抛出)异常 ArithmeticException
- 当抛出异常后,程序就退出,崩溃了 , 下面的代码就不在执行
- 大家想想这样的程序好吗? 不好,因为出现了一个不算致命的问题,就导致整个系统崩溃
??2 解决方案-异常捕获
java 设计者,提供了一个叫 异常处理机制 来解决该问题,如果程序员,认为一段代码可能出现异常/问题,可以使用try-catch 异常处理机制来解决从而保证程序的健壮性,将该代码块->选中->快捷键 ctrl + alt + t -> 选中 try-catch
public class a {
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
try {
int res = num1 / num2;
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序继续运行....");
}
}
💡如果进行异常处理,那么即使出现了异常,程序可以继续执行 💻运行结果:
java.lang.ArithmeticException: / by zero
at com.hj.第十二章异常.a.main(a.java:22)
程序继续运行....
可以看到运行结果中不仅提示了我们异常,还让程序(输出语句)继续执行下去。
如果想要知道异常的信息,可以使用下面代码:
try {
int res = num1 / num2;
} catch (Exception e) {
System.out.println("出现异常的原因:" + e.getMessage());
}
??3 异常介绍
? 基本概念: Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两大类:
Error(错误) :Java虚拟机无法解决的严重问题。如:JVM 系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory). Error是严重错误,程序会崩溃。 2)Exception :其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception 分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。
??4 异常体系图
🧐异常体系图的小结
- 异常分为两大类,运行时异常和编译时异常.
- 运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
- 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响
- 编译时异常,是编译器要求必须处置的异常。
??5 常见的运行时异常
🚀5.1 常见的运行时异常包括
- NullPointerException 空指针异常
- ArithmeticException 数学运算异常
- ArrayIndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException 数字格式不正确异常
🚀5.2 常见的运行时异常举例
① NullPointerException 空指针异常 当应用程序试图在需要对象的地方使用 null 时,抛出该异常,看案例演示。
public class NullPointerException_ {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
💻运行结果:
Exception in thread "main" java.lang.NullPointerException
at com.hj.第十二章异常.运行时异常.NullPointerException_.main(NullPointerException_.java:9)
② ArithmeticException 数学运算异常 当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例,案例如问题引出代码。
③ ArrayIndexOutOfBoundsException 数组下标越界异常 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
int[] arr = {1,2,4};
for (int i = 0; i <= arr.length; i++) {
System.out.println(arr[i]);
}
}
}
💻运行结果:
1
2
4
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at com.hj.第十二章异常.运行时异常.ArrayIndexOutOfBoundsException_.main(ArrayIndexOutOfBoundsException_.java:10)
④ ClassCastException 类型转换异常 当试图将对象强制转换为不是实例的子类时,抛出该异常。例如,以下代码将生成一个 ClassCastException
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B();
B b2 = (B)b;
C c2 = (C)b;
}
}
class A {}
class B extends A {}
class C extends A {}
Exception in thread "main" java.lang.ClassCastException: com.hj.第十二章异常.运行时异常.B cannot be cast to com.hj.第十二章异常.运行时异常.C
at com.hj.第十二章异常.运行时异常.ClassCastException_.main(ClassCastException_.java:10)
⑤NumberFormatException 数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用异常我们可以确保输入是满足条件数字.
public class NumberFormatException_ {
public static void main(String[] args) {
String name = "hujian";
int num = Integer.parseInt(name);
System.out.println(num);
}
}
💻运行结果:
Exception in thread "main" java.lang.NumberFormatException: For input string: "hujian"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at com.hj.第十二章异常.运行时异常.NumberFormatException_.main(NumberFormatException_.java:10)
??6 编译异常
🚀6.1介绍
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
🚀6.2 常见的编译异常
🚀6.3 案例说明
因为我们还没有学习SQL,文件编程等等,这里我们先举一个(FileNotFoundException)案例来说明,其它异常使用方式类似。
import java.io.FileInputStream;
import java.io.IOException;
public class Exception02 {
public static void main(String[] args) {
try {
FileInputStream fis;
fis = new FileInputStream("d:\\aa.jpg");
int len;
while ((len = fis.read()) != -1) {
System.out.println(len);
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
??8 异常处理
🚀8.1 基本介绍:
异常处理就是当异常发生时,对异常处理的方式。
🚀8.2 异常处理的方式
① try-catch-finally 程序员在代码中捕获发生的异常,自行处理
② throws 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
🚀8.3 示意图
??9 try-catch 异常处理
🚀9.1 try-catch 方式处理异常说明
① Java提供try 和catch 块来处理异常。try 块用于包含可能出错的代码。catch 块用于处理try 块中发生的异常。可以根据需要在程序中有多个try...catch 块。
② 基本语法
try {
}catch(异常){
}
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
try {
int res = num1 / num2;
} catch (Exception e) {
System.out.println("出现异常的原因:" + e.getMessage());
}
System.out.println("程序继续运行....");
}
🚀9.2 try-catch 方式处理异常-注意事项
注意事项一:如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块.
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "Baridhu";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("异常信息=" + e.getMessage());
}
System.out.println("程序继续...");
}
}
💻运行结果:
异常信息=For input string: "Baridhu"
程序继续...
注意事项二: 如果异常没有发生,则顺序执行try的代码块,不会进入到catch.
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "123";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("异常信息=" + e.getMessage());
} finally {
}
System.out.println("程序继续...");
}
}
💻运行结果:
数字:123
程序继续...
注意事项三:如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码- finally
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "Baridhu";
int a = Integer.parseInt(str);
System.out.println("数字:" + a);
} catch (NumberFormatException e) {
System.out.println("异常信息=" + e.getMessage());
} finally {
System.out.println("finally 代码块被执行...");
}
System.out.println("程序继续...");
}
}
💻运行结果:
异常信息=For input string: "Baridhu"
finally 代码块被执行...
程序继续...
注意事项四:可以有多个catch 语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException 在前),如果发生异常,只会匹配一个catch ,案例演示。
public class TryCatchDetail02 {
public static void main(String[] args) {
try {
Person person = new Person();
person = null;
System.out.println(person.getName());
int n1 = 10;
int n2 = 0;
int res = n1 / n2;
} catch (NullPointerException e) {
System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
}
}
}
class Person {
private String name = "jack";
public String getName() {
return name;
}
}
💻运行结果:
空指针异常=null
public class TryCatchDetail02 {
public static void main(String[] args) {
try {
Person person = new Person();
System.out.println(person.getName());
int n1 = 10;
int n2 = 0;
int res = n1 / n2;
} catch (NullPointerException e) {
System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
}
}
}
class Person {
private String name = "jack";
public String getName() {
return name;
}
}
💻运行结果:
jack
算术异常=/ by zero
注意事项五:可以进行try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
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 com.hj.第十二章异常.trycatch.TryCatchDetail03.main(TryCatchDetail03.java:16)
🚀9.3 异常处理课堂练习
T1:下面代码的输出是多少?
public class Exception01 {
public static int method() {
try {
String[] names = new String[3];
if (names[1].equals("tom")) {
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return 3;
} finally {
return 4;
}
}
public static void main(String[] args) {
System.out.println(method());
}
}
🧐答案:4
🧐分析: 因为String[] names = new String[3]; 后,数组都是null,而names[1].equals("tom") 会触发NullPointerException ,然后会进入catch 语句中,我们看到catch (NullPointerException e) { return 3;} ,有些同学就会觉得输出就是3了,这是错误的,因为finally 语句是一定会执行的,所以会去执行finally 的语句,从而输出4
T2: T3:
public class Exception02 {
public static int method() {
int i=1;
try {
i++;
String[] names = new String[3];
if (names[1].equals("tom")) {
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i;
} finally {
i++;
System.out.println("i=" + i);
}
}
public static void main(String[] args) {
System.out.println(method());
}
}
💻运行结果:
i=4
3
🧐分析: 先执行method语句,所以会执行finally语句输出:4,method会返回一个3。
🚀9.5 try-catch-finally 执行顺序小结
①如果没有出现异常,则执行try 块中所有语句,不执行catch 块中语句,如果有finally ,最后还需要执行finally 里面的语句
②如果出现异常,则try 块中异常发生后,try 块剩下的语句不再执行。将执行catch 块中的语句,如果有finally ,最后还需要执行finally 里面的语句!
🚀9.6 课后练习题
T:如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
public class TryCatchExercise {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int num=0;
String intputStr="";
while(true){
System.out.println("请输入一个整数:");
intputStr=sc.next();
try {
num=Integer.parseInt(intputStr);
break;
} catch (NumberFormatException e) {
System.out.println("你输入的不是一个整数!");
}
}
System.out.println("你输入的值为:" + num);
}
}
💻运行结果:
请输入一个整数:
a
你输入的不是一个整数!
请输入一个整数:
1
你输入的值为:1
??10 throws 异常处理
🚀10.1 throws 异常处理
?声明抛出异常是Java中处理异常的第二种方式
① 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Throws01 {
public static void main(String[] args) {
}
public void f2() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("d://aa.txt");
}
}
② 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可 以是方法中产生的异常类型,也可以是它的父类。
1.在方法声明中用throws语句可以声明抛出异常的列表
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Throws01 {
public static void main(String[] args) {
}
public void f2() throws FileNotFoundException,NullPointerException{
FileInputStream fis = new FileInputStream("d://aa.txt");
}
}
2.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
import java.io.FileInputStream;
public class Throws01 {
public static void main(String[] args) {
}
public void f2() throws Exception {
FileInputStream fis = new FileInputStream("d://aa.txt");
}
}
🚀10.2 注意事项和使用细节
①对于编译异常,程序中必须处理,比如 try-catch或者throws
import java.io.FileInputStream;
public class Throws01 {
public static void main(String[] args) {
}
public void f2() {
FileInputStream fis = new FileInputStream("d://aa.txt");
}
}
解决方案就是try-catch或者throws,参考前面案例。
②对于运行时异常,程序中如果没有处理,默认就是throws的方式处理[举例]
public class ThrowsDetail {
public static void main(String[] args) {
f2();
}
public static void f2() {
int n1 = 10;
int n2 = 0;
double res = n1 / n2;
}
}
💻运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.hj.第十二章异常.抛出异常throws.注意事项.ThrowsDetail.f2(ThrowsDetail.java:18)
at com.hj.第十二章异常.抛出异常throws.注意事项.ThrowsDetail.main(ThrowsDetail.java:10)
③子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
class Father {
public void method() throws RuntimeException {
}
}
class Son extends Father {
@Override
public void method() throws ArithmeticException {
}
}
④在throws过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws
🧐补充: 看下面代码:
public static void f1() {
f3();
}
public static void f3() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("d://aa.txt");
}
这里f3会报错,大家想一想这是为什么?
因为 f3() 方法抛出的是一个编译异常,即这时,就要 f1() 必须处理这个编译异常,在 f1() 中,要么 try-catch-finally ,或者继续 throws 这个编译异常
再来看看下面代码:
public static void f4() {
f5();
}
public static void f5() throws ArithmeticException {
}
f5()不会报错,大家想想这又是为什么? 🧐分析:
在 f4()中调用方法 f5() 是 OK, 原因是 f5() 抛出的是运行异常 ,而 java 中并不要求程序员显示处理,因为有默认处理机制。
??11自定义异常
🚀11.1 基本概念
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
Throwable子类
🚀11.2 自定义异常的步骤
- 定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException
- 如果继承Exception,属于编译异常
- 如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
🚀11.3 自定义异常的应用实例
当我们接收Person对象年龄时,要求范围在18-120之间,否则抛出一个自定义异常(要求继承RuntimeException),并给出提示信息。
public class CustomException {
public static void main(String[] args) {
int age = 180;
if(!(age >= 18 && age <= 120)) {
throw new AgeException("年龄需要在 18~120之间");
}
System.out.println("你的年龄范围是正确的。");
}
}
class AgeException extends RuntimeException {
public AgeException(String message){
super(message);
}
}
💻运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.hj.第十二章异常.抛出异常throws.注意事项.ThrowsDetail.f2(ThrowsDetail.java:18)
at com.hj.第十二章异常.抛出异常throws.注意事项.ThrowsDetail.main(ThrowsDetail.java:10)
🧐说明:
class AgeException extends RuntimeException {
public AgeException(String message){
super(message);
}
}
这里的super(message); 调用了RuntimeException的构造器
public RuntimeException(String message) {
super(message);
}
同理再向上调用Exception的构造器:
public Exception(String message) {
super(message);
}
最后调用Throwable的构造器:
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
💡总结:
自定义一个异常一般情况下,我们自定义异常是继承RuntimeException,即把自定义异常做成运行时异常,好处是:我们可以使用默认的处理机制,即比较方便。
??12 throw 和 throws 的区别
我们给一个练习,通过练习加深印象。
T:判断程序的输出结果
public class ReturnExceptionDemo {
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");
return;
} finally {
System.out.println("调用B方法的finally");
}
}
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
methodB();
}
}
💻输出结果:
进入方法A
用A方法的finally
制造异常
进入方法B
调用B方法的finally
🧐分析: methodA(); 调用方法
static void methodA() {
try {
System.out.println("进入方法A");
throw new RuntimeException("制造异常");
}finally {
System.out.println("用A方法的finally");
}
}
第一步methodA() :进入try 语句,首先是输出语句:System.out.println("进入方法A"); 然后执行的并不是手动抛出异常:throw new RuntimeException("制造异常"); ,而是finally 里面的语句:System.out.println("用A方法的finally"); ,因为如果我们先处理throw new RuntimeException("制造异常"); 的话,程序就会之间结束这个方法(参考try-catch 方式处理异常 注意事项五:可以进行try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。),而finally 语句必须要执行,所以会先执行System.out.println("用A方法的finally"); 再执行throw new RuntimeException("制造异常"); ,主方法的catch语句catch (Exception e) { System.out.println(e.getMessage()); } 对其进行处理输出信息。
第二步methodB(); :先执行:System.out.println("进入方法B"); 然后同理在返回之前一定要执行语句finally { System.out.println("用A方法的finally"); } 。
|