异常
1.传统异常处理
import java.util.Scanner;
public class Test01 {
public static void main(String[] args) {
// 需求:从控制台输入两个整数,求他们的商
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个整数:");
// int num1 = sc.nextInt();
// a => InputMismatchException异常,输入不匹配异常
if (sc.hasNextInt()){
int num1 = sc.nextInt();
System.out.println("请输入第二个整数:");
int num2 = sc.nextInt();
if (sc.hasNextInt()){
num2 = sc.nextInt();
if (0== num2 ){
System.out.println("除数不能为0");
}else{
int r = num1 / num2;
System.out.println(r);
}
}
int r = num1 / num2;
}// num2 = 0; => ArithmeticException,数学计算异常
}
}
传统不正常情况处理方案的缺点:
[1] 代码太长,增加了维护(review)的成本
[2] 判断逻辑和业务逻辑耦合
2.异常概念
异常(Exception)是指在程序运行过程中所发生不正常的事件,它会中断正在进行的程序
异常不是错误,更不是程序的逻辑错误!!!!!!!!!
程序预先设置好处理异常的方式=====>异常=====>程序中断
异常会导致程序中断运行.
java编程语言使用异常处理机制为程序提供了异常处理的能力
程序预先设置好处理异常的方式=====>异常=====>异常捕获,异常匹配分析,异常处理====>正常结束
异常处理机制可以保证程序出现异常后,继续向正确的方向运行.
3.异常处理
java异常处理包含两种代码块.一种是try…catch,一种是try…catch…finally.
1.try…catch
把有可能产生异常的代码放到try中,如果产生异常由catch代码块处理.
语法:
try{
// 可能产生异常的代码块
}catch(异常类型 e){
// 异常处理
}
try...catch执行有三种情况:
[1] 没有发生异常,正常执行
import java.util.Scanner;
public class Test01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
// 情况1: 没有发生异常
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
System.out.println(r);
}catch (Exception e){
// 异常处理
System.out.println("程序出问题了!");
}
System.out.println(r);
System.out.println("程序正常结束!");
}
}
[2] 出现异常,catch 代码块捕获异常,并进行异常处理,处理完成后程序继续执行
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
// 情况2: 发生异常
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
System.out.println(r);
}catch (Exception e){
// 异常处理
System.out.println("程序出问题了!");
}
System.out.println(r);
System.out.println("程序正常结束!");
}
}
// 总结:1.异常发生后,从异常发生的那句代码开始,程序不继续向下运行,立即进入异常处理
[3] 异常不匹配
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test03 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
// 情况3: 异常不匹配(多重catch来捕获)
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
}catch (InputMismatchException e){
// 异常处理
System.out.println("输入格式有问题,请输入整数");
}catch (ArithmeticException e){
System.out.println("除法不能为0");
}catch (Exception e){
System.out.println("未知错误");
}
System.out.println(r);
System.out.println("程序正常结束!");
}
}
// 总结: 1.异常不匹配,程序中断
2.多重catch
可以为try 代码书写多个catch用于捕获多个具体的异常.
书写格式:子类在上,父类在下
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test04 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
}catch (InputMismatchException | ArithmeticException e){
// 异常处理
System.out.println("计算错误");
}catch (Exception e){
System.out.println("未知错误");
}
System.out.println(r);
System.out.println("程序正常结束!");
}
}
3.异常对象
异常对象是出现异常时的那条语句产生的(JVM自动创建)
异常在java类中通过Exception或其具体子类创建,大多数情况下都是由其子类
(命名方式:异常类型 + Exception)创建,Exception是所有异常类的父类
异常对象的方法
toString | 返回异常类型和异常信息 |
---|---|
getMessage | 返回异常信息 |
printStackTrace | 打印堆栈信息(红色),位置不固定 |
异常堆栈信息:
- 第一句: 表示异常类型和异常的Message构成
- 最后一句: 包含具体发生异常的全路径类,方法,产生异常语句的行数。
4.try…catch…finally
try…catch用于捕获并处理异常, finally代码块用于处理异常后的收尾工作.不管是否发生异常, finally总执行
finally的收尾工作包含释放内存, 关闭文件, 关闭网络连接, 关闭数据库, 关闭…
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test01 {
/**
* 未来进行DB操作
* 1> 打开数据库
* 2> 操作数据
* 3> 关闭数据库
*/
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("程序正常结束!");
}
System.out.println(r);
}
}
finally 唯一不执行的情况: jvm正常退出
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test05 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入被除数:");
int r = 0;
try {
// 可能存在异常的代码
int num1 = sc.nextInt();
System.out.println("请输入除数:");
int num2 = sc.nextInt();
r = num1 / num2;
}catch (Exception e){
System.out.println(e.toString());
// jvm正常退出
System.exit(0);
}finally{
System.out.println("我是finally");
}
System.out.println("程序正常结束!");
}
}
存在return的try…catch…finally
public class Test03 {
public static int div(int a,int b) {
int r = 0;
try {
r = a / b;
return r;
} catch (Exception e) {
System.out.println(e.toString());
} finally {
System.out.println("我是 finally");
}
return r;
}
public static void main(String[] args) {
System.out.println(div(10,0));;
System.out.println("程序正常结束!");
}
}
总结:
[1] 存在return的try...catch...finally 块, finally 先执行, 然后执行 return
[2] return 总是最后执行
4.异常分类
1.异常继承体系
在Java中, Throwable 类是 Java 语言中所有错误(Error)或异常(Exception)的父类
2.Error
Error,表示代码运行时JVM(Java虚拟机)出现的问题。如果系统崩溃或内存溢出,不需要处理Error
常见的Error:
StackOverflowError:当应用程序递归太深而发生堆栈溢出是,抛出该错误。比如死循环或者没有出口的递 归调用;
OutOfMemoryError:因为内存溢出或没有可用的内存提供给垃圾回收器是,Java 虚拟机无法分配一个对象, 这时抛出该错误。比如 new了非常庞大数量的对象而没有释放。
3.Exception
Exception,表示程序在运行时出现的一些不正常情况,一般大多数表示轻度到中度的问题,属于可预测、可恢复问题。如 除数为0,数组索引越界等,这种情况,程序员通过合理的异常处理,确保程序的正常运行知道结束。
异常根据其是否必须要处理,分为运行时异常和检查时异常
1.运行时异常
运行时异常(RuntimeException):在程序运行过程中可处理,可不处理,父类是RuntimeException
常见的运行时异常:
InputMismatchException : 输入不匹配异常
ArithmeticException : 数学计算异常 e.g:除数为0
IndexOutOfBoundsException : 下表/索引越界异常
ArrayIndexOutOfBoundsException : 数组下标越界异常 遍历数大于下标最大值
StringIndexOutOfBoundsException : 字符串下标越界异常
NullPointerException : 空指针异常 当一个对象为空,调用其成员方法会报
IllegalArgumentException : 方法接收到非法参数
ClassCastException : 强制类型转换异常
NumberFormatException : 数字格式转换异常
2.检查时异常
检查时异常(Checked Exception): 也称编译时异常,指的是编译期间检查程序可能存在不正常的情况,在程序运行过程中必须处理,否则编译不通过.
常见的检查时异常
ClassNotFoundException : class 没找到异常
FileNotFoundException : 未见未找到异常
IOException:IO 异常
SQLException : 数据库相关异常
UnsupportedEncodingException : 不支持的字符串编码异常
遇到不懂的异常类时,首先要分辨它属于检查时还是运行时异常。通过快捷键查看
IEDA : ctrl+h
Eclipse: ctrl + t
5.声明异常
1.throws
在java语言中通过 throws 声明某个方法可能抛出的各种异常.
当方法的定义者在定义方法时知道调用该方法时可能出现异常,定义者又不知道如何处理时,此 时方法定义者可以选择声明异常,使用 throws 关键字,可以声明多个异常,用**(,)**号分隔。
语法:
[修饰符] 返回值类型 方法名(形参列表) throws 异常1,异常2....{
}
声明异常时要根据异常的分类来确定是否外界(调用处)是否必须处理该方法产生的异常.
如果 需要外界必须处理,需要声明检查时异常,否则声明运行时异常。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CalcUtils {
public static int add(int num1,int num2){
return num1 + num2;
}
/** public static int divide(int num1,int num2){
try {
int r;
r = num1 / num2;
return r;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
*/
// 当方法的定义者不知道如何更好的处理时,可以向外声明异常
public static int divide(int num1,int num2) throws ArithmeticException{
int r;
r = num1 / num2;
return r;
}
public static Date parseDate(String dateStr) throws ParseException{
SimpleDateFormat df = new SimpleDateFormat();
return df.parse(dateStr);
}
}
import java.text.ParseException;
public class Test01 {
public static void main(String[] args) throws ParseException {
try {
CalcUtils.divide(10,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
// 如果调用处能处理,优先处理异常
try {
CalcUtils.parseDate("abc");
} catch (ParseException e) {
e.printStackTrace();
}
// 异常的继续上抛
CalcUtils.parseDate("abc");
}
}
2.声明异常和重载的关系
无关
3.声明异常和重写的关系
声明异常和方法重写有关系!
如果父类声明运行时异常,子类可以声明运行时异常或者不声明如果父类声明检查时异常,子类可以声明检查时异常或者不声明或者运行时异常。
如果父类没有声明任何异常,子类要么不声明任意异常,要么可以声明运行时异常,但不能声明检查时异常。
一句话 : 子类方法声明的异常 <= 父类方法声明的异常
4.异常上抛
/*
在实际开发过程中,如果调用 A 类 showInfo 存在异常,调用处也不知如何处理时,可以继续向
上声明异常,我们把这个过程称为异常的上抛。
*/
public class Sub extends Parent{
@Override
public void showInfo() throws Exception{
}
}
public class Test01 {
public static void main(String[] args) throws Exception{
Sub sub = new Sub();
sub.showInfo();
System.out.println("程序正常结束!");
}
}
// 在声明异常的过程中,能内部处理有限处理,然后在选择上抛
6.手动抛出异常
在实际开发过程中,开发者也可以根据程序的需要,手动抛出异常,通过throw关键字.
当程序出现某种逻辑错误时,由程序员主动抛出某种特定类型的异常
public class GenderException extends Exception{
public GenderException(){
}
public GenderException(String message){
super(message);
}
public void log(){
System.out.println("我是日志");
}
}
////////////////////////////////////////////////////////////////////////////////////
public class Student {
private String name;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
/** public void setGender(String gender) throws Exception {
if (gender.equals("男") || gender.equals("女")) {
this.gender = gender;
}else {
Exception ex = new Exception("性别值不合法");
throw ex;
// throw new Exception("性别值不合法");
throw new RuntimeException("性别值不合法");
}
}*/
public void setGender(String gender) throws GenderException{
if (gender.equals("男") || gender.equals("女")){
this.gender = gender;
}else{
throw new GenderException("性别值不合法");
}
}
public Student(String name, String gender) {
this.name = name;
this.gender = gender;
}
public Student() {
}
}
/////////////////////////////////////////////////////////////////////
public class Test01 {
public static void main(String[] args) {
Student stu =new Student();
// 手动抛出检查时异常
/*try {
stu.setGender("小姐姐");
} catch (Exception e) {
System.out.println(e.toString());
}*/
// 手动抛出运行时异常
// stu.setGender("保密");
try {
stu.setGender("保密");
} catch (GenderException e) {
e.printStackTrace();
}
}
}
7.自定义异常
当JDk 中的异常类型不能满足程序的需要时(也即需要定义具体的业务异常时),可以自定义异常;
自定义异常的步骤:
[1] 确定异常类型(检查时异常/运行时异常)
[2] 继承异常的父类(检查时异常 继承 Exception/运行时异常 继承 RuntimeException)
[3] 声明构造方法