编译时程序写得再好,在运行过程中仍会出现一些问题,导致程序不能正常运行,这就是异常。
注:开发过程中的语法错误和逻辑错误不是异常。
Java中的发生的异常(广义:一切的不正常)事件分为两类:
①Error:Java虚拟机无法解决的问题,如JVM内部错误,资源耗尽等。这种错误一般不编写针对性的代码进行处理。
②Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以使用针对性的代码进行处理。(使用Java语言的异常处理机制,可以控制,后面代码可以继续执行的。)
例如:空指针异常
数组下标越界
试图读取不存在的文件
网络连接中断
狭义的异常:
对于这些异常有两种解决方法:①遇到异常就终止程序的运行②由程序员在编写程序时,就考虑到异常的检测,异常消息的提示,以及异常的处理。
捕获异常最理想的时期是在编译期间
这些异常又分为编译异常,和 运行异常。
当内存不够时,或抛出错误,称为Error
// jvm内存耗光 Error
List list = new ArrayList();
while(true){
list.add(new Random());
}
// 数组下标越界
// java.lang.ArrayIndexOutOfBoundsException
int[] a = new int[4];
a[5] = 1;
// 算术异常
// java.lang.ArithmeticException
int a = 10;
int b = 0;
System.out.println(a/b);
// 类类转换异常
// java.lang.ClassCastException
String s = "12";
Object obj = s;
Integer i = (Integer)obj;
// 数字格式化异常
// java.lang.NumberFormatException
int i = Integer.parseInt("a");
// 空指针异常
// java.lang.NullPointerException
String s = null;
s.length();
Throwable类有两个直接子类:Error类,Exception类。Exception表示异常,是所有异常类的父类,是程序员关心的。Error表示错误,可能是编译期错误或者系统错误。
异常又分为:
①运行期异常:所有RuntimeException的子类都是运行期异常。
②编译期异常(Checked Exception):除去运行期的异常都是编译期异常。编译期异常的层次和RuntimeException是同一层次的。编译期异常出现就需要处理。ParseException(解析异常)属于编译期异常。
Jvm默认异常处理方式:
① 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
②程序停止运行
// java编程语言使用异常处理机制为程序提供了错误处理的能力
// 步骤:① 程序中预先设置好处理异常的方法 ② 程序运行 ③异常出现
// ④处理异常 ⑤ 处理完毕,继续执行下面的语句
// try{
// 将可能出现的代码放入其中 : 代码范围尽可能地小
// }catch(异常类型 异常名){
// 异常处理语句
// }
// try中出现异常,然后执行catch,再执行下面地语句
int c = 5;
int d = 0;
try{
System.out.println(c/d);
}catch(ArithmeticException ex){
System.out.println("除数不能为0");
}
System.out.println("处理完异常,继续执行下面地语句");
}
// 嵌套式 try catch
try {
// 如果外层地 try catch异常,那么会越过内层try catch,执行相对应地catch,然后继续执行下面的语句
int f = 10;
int g = 2;
System.out.println(f / g);
try {
// 如果内层的异常,那也就意味着外层的是正常的。
// 因此会执行内层的相对应的catch,然后越过外层的catch,继续执行下面的语句
Integer.parseInt("abc");
} catch (NumberFormatException num1) {
System.out.println("数值格式化异常");
}
} catch (ArithmeticException ari1) {
System.out.println("算术异常");
}
// 一个try 对应多个 catch
try {
// 如果语句1 异常,那么就会越过语句2,直接执行对应地catch,之后会执行下面地语句
int a = 10;
int b = 0;
System.out.println(a/b);
int c = 10;
int d = 2;
System.out.println(c / d);
// 如果上面地正常,此语句出错,那么就会执行对应地catch,然后继续执行下面地语句
Integer.parseInt("abc");
System.out.println("如果上面语句有异常,那么此语句不会被执行");
} catch (ArithmeticException ari) {
System.out.println("算术异常");
} catch (NumberFormatException num) {
System.out.println("数值格式化异常");
} catch (Exception ex) {// 如果不知道具体的异常类型,就可以使用这个。
// 这是一个大范围的异常捕获,因此用的时候需要放在最下面
System.out.println("系统忙");// 用户看的
System.out.println(ex.getMessage());// 程序员看的
// 后期可以通过第三方日志组件向文件中输出信息。
}
System.out.println("处理完异常继续执行");
}
不管是否出现异常,并且异常是否被处理都会执行finally,结束之前优先执行finally
// 捕获中有return,还是会执行finally语句之后再返回状态码
public static int test(){
try{
int a = 10;
int b = 0;
System.out.println(a/b);
}catch(ArithmeticException ari){
System.out.println("算术运算异常");
return 0;// 状态码
}finally{
System.out.println("FINALLY无论如何都会执行");
}
System.out.println("并不会被执行");
return 1;
}
在正常情况(即程序正常执行try catch finally语句块,不会在语句中出现退出程序、线程终止等特殊情况)下,都会执行finally语句块,如果finally中有return,则程序会走finally中的return,如果没有,则先执行try或者catch中的return,将其存入临时栈中,执行完finally语句后才返回临时栈中的值。
// 如果finally 中有return,那么后面就不能再写语句了。因为最终的return已经在finally中了。
// 那么如果有异常的话,那么catch中的return会被finally中的return覆盖
public static int test1(){
try{
int a = 10;
int b = 0;
System.out.println(a/b);
}catch(ArithmeticException ari){
System.out.println("算术运算异常");
return 0;
}finally{
System.out.println("FINALLY无论如何都会执行");
return 2;
}
}
// 如果有异常,但是没有catch处理,那么就会在执行完finally后抛出异常,终止后面程序的执行
public static int test2(){
try{
int a = 10;
int b = 0;
System.out.println(a/b);
}finally{
System.out.println("FINALLY无论如何都会执行");
}
System.out.println("并不会被执行");
return 1;
}
定义方法时 声明 可能出现的变量,此方法可以不处理,交给方法调用处处理
一个方法可以声明多个异常
public static void main(String[] args) throws ParseException{
SimpleDateFormat s1 = new SimpleDateFormat();
// 这个是编译期异常,但是不用捕获的原因是,这个声明只是为了声明父类方法中的throw创建的异常对象
s1.parse("2020-11-12-ww");// 需要抛出异常
int a = 10;
int b = 0;
chu(a,b);
try{
test();
}catch(ParseException par){
System.out.println("编译期异常可以不立即处理,但是到达顶层后就必须处理");
}
}
// throws 声明此方法可能出现算术异常,如果throws抛出的是一个运行期异常,那么可以处理也可以不处理
public static void chu(int a, int b) throws ArithmeticException{
System.out.println(a/b);
}
// 如果throws声明的是一个编译期异常,可以立即处理
// 也可以不立即处理,但是一旦抛到顶层方法中,就必须处理
public static void test1() throws ParseException{
System.out.println("编译期异常");
}
public static void test() throws ParseException{
test1();
}
父类
/*
* 任何方法都可以通过throws 声明异常,包括抽象方法
* */
public abstract class AnomalyDemo6 {
public abstract void eat() throws ParseException;
public abstract void sleep() throws ArithmeticException;
}
子类
/*
* 子类重写父类方法时,也要声明父类异常
* 但是子类方法不能声明抛出比父类类型更大的异常(运行期影响不大)
* */
public class Demo6child extends AnomalyDemo6{
@Override
public void eat() throws ParseException {
}
@Override
public void sleep() throws RuntimeException {
// 这里可以使用RuntimeException的原因是,RuntimeException包含ArithmeticException
// 不能使用Exception 的原因是,Exception不仅包含RuntimeException,还包含其他的异常
// 子类声明RuntimeException,绝对可以找到父类的异常类型,
// 但是如果子类声明Exception,就不一定可以找到父类的异常类型。
}
}
throw用于显式抛出异常,在程序中主动抛出异常对象,在构造方法中可以传入异常原因
throw语句可以单独使用,
throw语句执行的不是异常类,而是一个异常实例,而且每次只能执行一次throw语句抛出异常对象后,后面的语句不会再执行。
throw如果创建的是一个编译期的对象,要么用try catch捕获,要么用throws声明异常
public class AnomalyDemo7 {
public static void test(int a){
if(a<0 | a>100){
throw new RuntimeException("不在合法范围");
}
System.out.println("zhenqu");
}
}
自定义异常类: 需要继承RuntimeException(运行期)或者Exception(编译期)才能称为异常类。
在我们写程序的过程中出现一些不满足条件的情况时,也可以自定义异常类,来表示某种情况。
public class SroceException extends Exception{
public SroceException() {
}
public SroceException(String message) {
super(message);
}
}
调用类
public class AnomalyDemo8 {
public static void main(String[] args) {
try{
fenShu(101);
}catch(SroceException s){
s.printStackTrace();
System.out.println(s.getMessage());
}
}
public static void fenShu(int a) throws SroceException{
if(a<0 | a>100){
throw new SroceException("分数不合法");
}
}
}
若有错误,欢迎私信指正。