异常处理
异常概述
Throwable是错误和异常的父类,Throwable分为Error和Exception。
Error属于严重性问题,程序员解决不了的错误,像内存溢出等都属于Error。
Exception是异常 我们可以通过写程序来处理它。异常又分为编译期异常和运行期异常。编译期异常是必检异常,我们必须做出异常处理,因为如果发生异常,程序将无法运行。运行期异常属于免检异常,可以对异常作出处理,也可以不做出处理。 但如果我们不做出处理,那么异常最终会交由虚拟机来处理。
运行期异常
运行下面代码就会发生一个异常,因为除数不能是0。当除数是0时,会产生一个ArithmeticException 异常。
int a=1;
int b=0;
System.out.println(a/b);
处理异常
可以使用try-catch-fianlly语句来处理异常。
将可能发生异常的语句放在try语句块中,使用catch语句块来捕获try语句块中可能发生的异常。最终不算异常是否发生,finally语句块中的代码都会执行。
注意
如果catch语句中有return语句,则会先执行finally语句在执行catch中的return语句。而如果catch语句中含有exit则会直接退出虚拟机。
public class ExceptionDemo1{
public static void main(String[] args) {
int a=1;
int b=0;
//自己尝试捕获处理异常,不再交由虚拟机去处理
try {
//try 里面一般放一些可能会出现问题的代码
System.out.println(a / 0);
//catche()括号里面放的是 你要捕获的异常类型
}catch (ArithmeticException e){
//try里面的代码一旦出现异常,就会执行catch里面的代码
System.out.println("除数为0了");
}finally {
//一般我们会在finally里面做一些善后首尾工作 比如释放一些资源
System.out.println("finally里面的代码最终都会执行");
}
}
}
}
如果try语句中可能发生多个异常,可以使用多个catch来捕获异常。 但是父类异常的捕获必须放在子类异常的后边,因为异常会被父类捕获,子类就无法捕获。如果是两个平级异常可以使用“|”符号将两个异常放在同一个catch语句中捕获。
public class ExceptionDemo{
public static void main(String[] args) {
int a = 1;
int b = 0;
int[] arr = {1, 2, 3};
arr = null;
//try 里尽量放的是有可能出现问题的代码
try {
System.out.println(a / b);
System.out.println(arr[0]);
//多个异常:如果是平级关系的异常,谁前谁后无所谓
//如果多个异常有父子关系,那么父级别的异常放在最后面
//能明确的异常,尽量明确
} catch (ArithmeticException e) {
//catch里面不要做空处理,哪怕你简单的输出一条语句
b = 1;
System.out.println("数学异常");
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (Exception e) {
System.out.println("其他异常");
}
}
}
编译期异常
编译期异常是指发生在编译期间的异常,该异常必须处理,否则程序无法运行。
public class DateUtils {
public static Date paseDateStr(String str, String format) throws ParseException{
SimpleDateFormat format1 = new SimpleDateFormat(format);
Date parse=null;
parse = format1.parse(str);
return parse;
}
}
public class ExceptionDemo3 {
public static void main(String[] args){
//编译期异常:发生在编译期间,必须处理,否则程序无法运行
String str="2019-10-10";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
//可能会发生编译期异常抛出ParseException
format.parse(str);
}catch (ParseException e){
System.out.println("解析失败");
}
System.out.println("=======");
}
}
异常处理的几个方法
getMessage():获取异常信息,返回字符串。
toString():获取异常类名和异常信息,返回字符串。
printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置。
public class MyTest {
public static void main(String[] args) {
try {
DateUtils.paseDateStr("2018-10-10","yyyy:MM-dd");
} catch (ParseException e) {
System.out.println("------解析异常-----");
e.printStackTrace();//打印堆栈信息 JVM 默认的处理的方式也是打印异常的堆栈信息
//String message = e.getMessage();
//System.out.println(message);
//System.out.println(e.toString());
}
System.out.println("===============================");
}
}
throws与throw的区别
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
这个异常对象可以是编译期异常对象, 可以是运行期异常对象
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
public class ThrowDemo {
public static void main(String[] args) throws ArithmeticException,NullPointerException,Exception{
yunsuan(1,0);
System.out.println("abcc");
}
private static void yunsuan(int a, int b) {
if(b!=0){
System.out.println(a / b);
}else if(b==0){
//throw 用在方法内部抛出异常
throw new ArithmeticException("除数为0");
}
}
}
自定义异常类
编写程序,有余额10,若取出存款额大于余额,则抛出自定义的异常。
//自定义异常类public class MyExcepiton extends RuntimeException{
String name;
public MyExcepiton(String name) {
this.name=name;
}//自定义 异常
@Override
public String toString() {
return name;
}
}
public class MyDemo {
public static void main(String[] args) {
int money = 100;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入取款金额");
int num = scanner.nextInt();
try{
if(num<=money){
money= money-num;
System.out.println("取款成功");
}else{
//抛出异常
throw new MyExcepiton("余额不足");
}
}catch (MyExcepiton e){
System.err.println(e);
}
}
}
注意
父类的方法没有抛出过异常,子类在重写父类方法时,也不能抛出异常,如果重写方法时有异常 你就try 掉
父类和子类都有抛出异常,子类抛出的异常不能比父类大,要么比父类小,或者跟父类一样,或者子类不抛出也可以