10.1 介绍
Java语言中,将程序执行中发生的不正常情况称为异常。(开发过程中的语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两大类:
- Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError【栈溢出】、OOM(out of memory),Error是严重错误,程序会崩溃
- Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问、试图读取不存在的文件、网络连接中断等。分为:
- 运行时异常:程序运行时发生的异常
- 编译异常:编译时编译器检查出来的异常
10.2 异常体系图
- 异常分为两大类:运行时异常和编译时异常
- 运行时异常,编译器检查不出来。一般指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
- 对于运行时异常,可以不做处理,因为这种异常很普通,若全处理可能会对程序的可读性和运行效率产生影响
- 编译时异常,,是编译器要求必须处置的异常
10.3 运行时异常
10.3.1 常见的运行时异常
- NullPointerException 空指针异常
- ArithmeticException 数学运算异常
- ArrayIndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException 数字格式不正确异常
10.3.2 NullPointerException 空指针异常
当应用程序试图在需要对象的地方使用 null 时,抛出该异常
public class NullPointerException_ {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
10.3.3 ArithmeticException 数学运算异常
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例
10.3.4 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]);
}
}
}
10.3.5 ClassCastException 类型转换异常
当试图将对象强制转换为不是实例的子类时,抛出该异常
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上转型
B b2 = (B)b; //向下转型,这里是 OK
C c2 = (C)b; //这里抛出 ClassCastException
}
}
class A {}
class B extends A {}
class C extends A {}
10.3.6 NumberFormatException 数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用异常我们 可以确保输入是满足条件数字
public class NumberFormatException_ {
public static void main(String[] args) {
String name = "韩顺平教育";
//将 String 转成 int
int num = Integer.parseInt(name);//抛出 NumberFormatException
System.out.println(num);//1234
}
}
10.4 编译时异常
编译时异常是指在编译期间就必须处理的异常,否则代码不能通过编译
10.4.1 常见的编译时异常
- SQLException 操作数据库时,查询表可能发生异常
- IOException 操作文件时,发生的异常
- FileNotFoundException 当操作一个不存在的文件时,发生异常
- ClassNotFoundException 加载类,而该类不存在时,异常
- EOFException 操作文件,到文件末尾,发生异常
- IllegalArgumentException 参数异常
10.5 异常处理
10.5.1 异常处理的方式
- try-catch-finally:程序员在代码中捕获发生的异常
- throws:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
10.5.2 处理机制
-
try-catch-finally
-
try { //可能有异常的代码 } catch(Exception e) { //捕获到异常 //1.当异常发生时 //2.系统将异常封装成Exception对象e,传递给catch //3.得到异常对象后,程序员进行处理 //4.注意:若没有发生异常,catch代码块不执行 } finally { //1.不管try代码块是否有异常发生,始终要执行finally //2.所以通常将释放资源的代码放在finally }
-
-
throws
10.5.3 try-catch异常处理
10.5.3.1 说明
-
Java提供try和catch块来处理异常
- try块用于包含可能出错的代码
- catch块用于处理try块中发生的异常
- 可以根据需要在程序中创建多个try-catch块
-
语法
-
try { //可能有异常的代码 //将异常生成对应的异常对象,传递给catch块 } catch(Exception e) { //对异常得到处理 } finally { //finally根据需要可选可不选 }
-
10.5.3.2 示例
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());
}
}
10.5.3.3 注意事项
-
如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
-
如果异常没有发生,则顺序执行try的代码块,不会进入catch
-
如果不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用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()); //NullPointerException int n1 = 10; int n2 = 0; int res = n1 / n2; //ArithmeticException } 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; } }
-
-
可以进行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("程序继续执行.."); } }
-
10.5.3.4 练习
-
public class Exception01 { public static void main(String[] args) { System.out.println(method()); //4 } public static int method() { try { String[] names = new String[3]; if(names[1].equals("tom")) { //NullPointerException System.out.println(names[1]); } else { names[3] = "jack"; } return 1; }catch (ArrayIndexOutOfBoundsException e) { return 2; }catch (NullPointerException e) { return 3; //finally必须执行,此处不会返回,而是执行finally }finally { return 4; //返回4 } } }
-
public class Exception02 { public static void main(String[] args) { System.out.println(method()); //4 } public static int method() { int i = 1; try { i++; //i = 2 String[] names = new String[3]; if(names[1].equals("tom")) { //NullPointerException System.out.println(names[1]); } else { names[3] = "jack"; } return 1; }catch (ArrayIndexOutOfBoundsException e) { return 2; }catch (NullPointerException e) { return ++i; //finally必须执行,此处不会返回,但是会执行++i,之后执行finally //i = 3 }finally { return ++i; //i = 4, 返回4 } } }
-
public class Exception03 { public static void main(String[] args) { System.out.println(method()); //3 } public static int method() { int i = 1; try { i++; //i = 2 String[] names = new String[3]; if(names[1].equals("tom")) { //NullPointerException System.out.println(names[1]); } else { names[3] = "jack"; } return 1; }catch (ArrayIndexOutOfBoundsException e) { return 2; }catch (NullPointerException e) { return ++i; // i = 3,保存到临时变量temp = 3,再执行finally //finally没有return,执行完finally后,返回temp }finally { ++i; System.out.println("i = "+i); //i = 4 } } }
-
//如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止 public class TryCatchExercise04 { public static void main(String[] args) { //思路 //1. 创建 Scanner 对象 //2. 使用无限循环,去接收一个输入 //3. 然后将该输入的值,转成一个 int //4. 如果在转换时,抛出异常,说明输入的内容不是一个可以转成 int 的内容 //5. 如果没有抛出异常,则 break 该循环 Scanner scanner = new Scanner(System.in); int num = 0; String inputStr = ""; while (true) { System.out.println("请输入一个整数:"); // inputStr = scanner.next(); try { num = Integer.parseInt(inputStr); //这里可能抛出异常 break; } catch (NumberFormatException e) System.out.println("你输入的不是一个整数:"); } } System.out.println("你输入的值是=" + num); } }
10.5.4 throws异常处理
10.5.4.1 介绍
- 如果一个方法(中的语句执行时)可能生产某种异常,但是不能确定如何处理这种异常,则此种方法应显式地声明抛出异常,表明该方法将不对这些异常进行处理,而是由该方法的调用者负责处理
- 该方法声明中使用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
10.5.4.2 示例
public static void readFile(String file) throws FileNotFoundException {
//FileNotFoundException可以写为其父类Exception
//多个异常通过逗号隔开,如throws FileNotFoundException,NullPointerException,ArithmeticException
FileInputStream fis = new FileInputStream("C://a.txt");
}
10.5.4.3 使用细节
- 对于编译异常,程序中必须处理,比如try-catch或throws
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
- 子类重写父类的方法时,对抛出异常的规定:
- 子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么是父类抛出的异常的类型的子类型
- 在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws(二选一)
10.6 自定义异常
10.6.1 介绍
当程序中出现了某些“错误”,当该错误信息并没有在Throwable子类中描述,这个时候可以自己设计异常类,用于描述错误信息
10.6.2 步骤
- 定义类:自定义异常类名,继承Exception或者RuntimeException
- 如果继承Exception,属于编译异常
- 如果继承RuntimeException,属于运行时异常(一般继承RuntimeException)
10.6.3 示例
当我们接收Person对象年龄时,要求范围在18到120之间,否则抛出一个自定义异常,并给出提示信息
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);
}
}
10.7 throw和throws的区别
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理的一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
练习:
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 {
ReturnExceptionDemo.methodA();
} catch (Exception e) {
System.out.println(e.getMessage());;
}
ReturnExceptionDemo.methodB();
}
}
/**
进入方法A
调用A方法的finally
制造异常
进入方法B
调用B方法的finally
**/
10.8 练习
-
/** * 编写应用程序EcmDef.java,接收命令行的两个参数(整数),计算两数相除 * 计算两个数相除,要求使用方法cal(int n1, int n2) * 对数据格式不正确(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException)、除0(ArithmeticException)进行异常处理 */ public class ExceptionExer01 { static double cal(int n1, int n2) { return n1 / n2; } public static void main(String[] args) { try { if(args.length != 2) { throw new ArrayIndexOutOfBoundsException("参数个数不对"); } int n1 = Integer.parseInt(args[0]); int n2 = Integer.parseInt(args[1]); double res = ExceptionExer01.cal(n1, n2); System.out.println("计算结果:"+res); } catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.getMessage());; } catch (NumberFormatException e) { System.out.println("参数格式应为整数"); } catch (ArithmeticException e) { System.out.println("出现了除0异常"); } } }
-
//代码是否会异常 public class ExceptionExer02 { public static void main(String[] args) { //参数不够5个会ArrayIndexOutOfBoundsException if (args[4].equals("john")) { //可能NullPointerException System.out.println("AA"); } else { System.out.println("BB"); } Object o = args[2]; //向上转型 Integer i = (Integer)o; //String -> Integer,会ClassCastException } }
-
//输出结果 public class ExceptionExer03 { public static void func() { try { throw new RuntimeException(); } finally { System.out.println("B"); } } public static void main(String[] args) { try { func(); System.out.println("A"); } catch (Exception e) { System.out.println("C"); } System.out.println("D"); } } /**输出结果: B C D **/
-
//输出结果 public class ExceptionExer04 { public static void showExce() throws Exception { throw new Exception(); } public static void main(String[] args) { try { showExce(); System.out.println("A"); } catch (Exception e) { System.out.println("B"); } finally { System.out.println("C"); } System.out.println("D"); } } /**输出结果: B C D **/