异常介绍
- 什么是异常?
Java语言中,将程序执行中发生的不正常情况成为“异常”。【语法错误和逻辑错误异常】
- 两类异常:
(1) Error:Java虚拟机无法解决的严重问题,会导致程序崩溃。例如:JVM系统内部错误,资源耗尽。
(2) Exception:因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:网络中断,空指针访问,读取不存在的文件等。
运行时异常可以不处理,编译时异常必须处理
- 异常体系图:
常见的运行时异常
(1)NullPointerException
当应用程序视图在需要对象的地方使用null,抛出该异常【使用的对象可能为空】
(2)AritmeticException
当出现异常的运算条件时,抛出此异常。【不满足数学规律】
(3)ArrayIndexOutOfBoundsException
数组下标越界【数组索引不满足条件】
(4)ClassCastException
类型转化异常,当试图将对象强制转化为不是实例的子类时,抛出该异常。
//假设B类和C类都继承A类
class A{}
class B extends A{}
class C extends A{}
//在测试类创建类的实例
class Test{
public static void main(String [] args){
A b = new B(); //向下转型创建B的对象
A a = (A)a; //向上转型创建A的对象
//一下为错误内容
C c = (C)b; //b 和 C类不存在继承关系,所以会抛出强制类型转化异常
}
}
(5)NumberFormatException
数字格式不正确异常,当应用程序试图将 字符串转化为一种数值类型,但该字符串不能转化为适当格式 时,抛出该异常
int num = Interger.parseInt("abc");
常见的编译异常
- 编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
- 常见的编译异常:【网络、数据库、文件异常】
SQLException 操作数据库时出现的异常
IOException 操作文件时发生的异常
FileNotFoundException 对不存在的文件操作发生的异常
ClassNotFoundException 加载不存在的类时,出现的异常
EOFException 操作文件,到文件末尾发生异常
IllegalArguementException 参数异常
异常处理
- 当异常发生时,对异常处理的方式,主要有一下两种
try-catch-finally
程序员在代码中捕获发生的异常,自行处理
try{
可能存在异常的代码
}catch(Exception e){
处理方式
}finally{
必然要执行的代码块
}
- 当异常发生时,系统将异常封装成Exception的对象e,传递给catch,得到异常对象后,程序员自己处理。
- 无论try代码块是否发生异常,finally代码块中的内容都会执行,一般将释放资源的代码放在finally代码块中
- 但catch代码块 只有 在try代码块发生异常时才会执行。
ctrl + alt + t 快捷键
如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
如果异常没有发生,则顺序执行try的代码块,不会进入到catch
如果希望不管是否发生异常,都执行某代码(比如关闭连接,释放资源等),则使用finally代码块
可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,如果发生异常,只会匹配一个catch【例如:NullPointerException在前,Exception在后】
package com.zwh.exception_;
/**
* @author Bonbons
* @version 1.0
*/
public class Exception01 {
public static void main(String[] args) {
Person p = new Person();
p = null;
try{
System.out.println(p.name);
int num = 1/0;
}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{
String name;
public String getName() {
return name;
}
}
(1)发生异常后不会继续执行try代码块中的内容,比如此时运行程序只会检测出来一个异常
(2)子类异常放在前面
如果将子类异常放在后面会报错。
(3)可以进行try-finally配合使用,这种用法相当于没有捕获异常,程序会直接崩掉。必须执行某个业务。
(4) finally中的内容必须要执行,即便在catch中包含 return
try{
发生了异常;
}catch(Exception e){
return 1;
}finally{
return 0;
}
最后返回的仍然是0
try{
int i = 3;
发生异常;
}catch(Exception e){
return i;
}finally{
i++;
System.out.println("i = " + i);
}
最后会打印出 i = 4,但程序返回的是3,因为在finally中没有返回语句
- try-catch-finally的应用
利用try输入一个整数,如果不满足条件就重新输入,直到满足条件为止
import java.util.Scanner;
public class ExceptionTest {
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(true){
try{
n = sc.nextInt();
System.out.println("您输入的整数为: " + n);
break;
}catch (Exception e){
System.out.println(e.getMessage());
System.out.println("请重新输入一个整数:");
// sc = new Scanner(System.in);
}
}
}
}
如果没有重新创建扫描器就会陷入到死循环中,因为 使用nextInt()
扫描器在输入内容结束按回车后,系统会在输入内容后自动添加上\n 或 \r
,也就导致扫描器只能使用一次
高配版,顺便引出一个问题【重要指数五颗星】
package com.zwh.exception_;
import com.sun.xml.internal.ws.addressing.WsaActionUtil;
import java.util.Scanner;
/**
* @author Bonbons
* @version 1.0
* 用户输入的不是一个整数就一直输入
*/
public class ExceptionTest {
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n;
while(true){
System.out.println("请输入一个整数:");
//此处利用的next()方法获取从键盘输入的内容
try {
n = Integer.parseInt(sc.next());
System.out.println("您输入的整数是: " + n);
break;
} catch (NumberFormatException e) {
System.out.println("您输入的并不是一个整数!");
}
}
}
}
利用类型转化报错也可以达到相同的效果,我们可以发现并没有创建新的扫描器对象,经过测试next()、nextLine()
方法都可以重复使用创建的扫描器对象,在使用后不会在输入的文本后面添加换行。
补充内容:
(1) next() 方法在遇到有效字符前所遇到的空格、tab 键、enter 键都不能当作结束符。所以,读到 abc 后有空格,存下第一个,读到 cba 后回车存下第二个。
(2)nextLine() 会读取中间的空格,无论是否输入了内容,只要读取到了回车就会结束。
throws
JVM 打印异常信息,退出程序
没有显示处理异常,就是默认使用了throws
(1)如果一个方法中的语句在运行时可能生成某种异常,但是并不能确定如何处理这些异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
(2)在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
(3)子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型
class Father{
public void method() throws RunTimeException{
}
}
class Son extends Father{
public void method() throws ArithmeticException{
}
}
自定义异常
当程序中出现了某些“错误”,但错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
自定义异常的步骤:
定义类:自定义异常类名继承Exception(代表编译异常)或RunTimeException(代表运行异常)
通过异常类的构造器设置异常信息提示
自定义异常通常是继承RuntimeException,就是设置为运行时异常,好处是可以使用默认的处理机制 【隐式抛出异常】
package com.zwh.exception_;
import java.util.Scanner;
/**
* @author Bonbons
* @version 1.0
* 自定义异常,如果用户输入的数不在18-120之间就提示异常信息
*/
public class CustomException {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Please input age: ");
int age = sc.nextInt();
//判断年龄是否符合规定的范围
if(age < 18 || age > 120){
throw new UserException("ageBoundException");
}
System.out.println("你的年龄范围正确!");
}
}
class UserException extends RuntimeException {
//自定义异常类的构造器
public UserException(String message) {
super(message);
}
}
throws 和 throw 的异常
- throws:异常处理的一种方式,在方法声明处使用,其后为异常类型
- throw:用于手动生成异常对象的关键字,在方法体中,其后为异常对象
public void fun(){
throw new Exception();
}
一个小案例
- 案例描述:编写应用程序,接收命令行的两个参数(整数),计算两数相除。计算两个数相除,要求使用方法cal(int n1,int n2),对数据格式不正确、缺少命令行参数、除0进行异常处理。
public class ExceptionWork01 {
public static void main(String[] args) {
try {
//通过args数组传入参数
if(args.length != 2){
throw new ArrayIndexOutOfBoundsException("参数个数不正确!");
}
//将args接收到的参数转化为整型
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
//调用两个数相除的方法
double res = cal(n1, n2);
System.out.println("计算结果为: " + res);
} catch (NumberFormatException e) {
System.out.println("参数格式错误,应该为整数!");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e.getMessage());
}catch (ArithmeticException e){
System.out.println("出现了零异常!");
}
}
//除法方法
public static double cal(int n1, int n2){
return (double)n1 / n2;
}
}
代码页点击这里,在红框栏输入参数即可