JavaSE基础——异常机制

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1. 异常的概念

  曾有人说过:“一个程序项目内30%的代码就能实现功能,剩下70%的代码都在检查异常、增加约束……”,为何会如此呢?

  • 程序员写的某个功能模块,用户未必会按照你的意愿操作,人的不靠谱性往往导致墨菲定律的发生,如你写一个四则运算,但是用户就是输入一个除数等于0;
  • 系统资源的一些缺陷,导致程序员的代码功能无法正常运行,如你去读取一个文件,但是该文件未必存在,或者是空文件,但是你的程序又要求文件不能为空;或者你运行代码时但是系统的磁盘、内存、CPU饱和了,等等……

  针对这些影响程序正常运行的意外,我们称之为异常(Exception);

2. 小试牛刀——第一个异常

package cn.rowyet.J0004Exception;

public class C0001FirstExcept {
    private String str;

    public static void main(String[] args) throws Exception 
    {

        int a=0;
        int b=1;
        System.out.println(b/a);

    }
}

复制代码

  以上代码就是最好理解的一个异常,俺上小学老师就教导我们,除数不能为0,不然除不尽,因此在Java代码中,如果出现除数为0的代码,就会出现图2.1的异常,那么异常该怎么解决呢?且听后文分晓。

first_exception.png

图2.1 第一个异常

3. 异常的分类

图片.png

图3.1 异常的分类

  如图3.1,本着面向对象的思想,Java对异常进行了分类,所有的异常根类为java.lang.Throwable,java.lang.Throwable派生出两个子类ErrorException,而Exception又派生出CheckedExceptionRuntimeException,是不是只有这些异常呢?当然不是,根据继承的特性,原则上后续还有无限的子子孙孙异常类,这里是列举不完,博主就展开讲讲这几个最基本的,后续的一场子类你们也就能举一反三了。

3.1 Error

  Error(错误):是指程序无法处理的严重问题,可能写Java遇到最多的一个问题就是OutOfMemoryError,即JVM虚拟机所需的内存不足时报错,一般会导致JVM无法继续工作而终止,解决方法就是JVM可用内存调小点,或者加大JVM的所需内存,图3.2是Error的API;

   注意:Error一般和你的程序关系不大,属于JVM内的错误,但是也不是完全没关系哈,程序写的过烂,也可能导致资源滥用而出现Error;

图片.png

图3.2 Error的API文档预览

3.2 Exception

  Exception(异常)是程序员能够处理的意外,Exception类是所有异常类的父类,派生出来的RuntimeException(运行时异常)CheckedException(已检查异常,也叫编译时异常),也就是说通常Java的异常有分为运行时异常编译型异常

3.2.1 RuntimeException

  RuntimeException(运行时异常),顾名思义就是派生于RuntimeException类的异常类型,如空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)等。如果显式的声明或捕获将会对程序可读性和运行效率影响很大。 因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。    这类异常通常是由编程错误导致的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,经常需要通过增加“逻辑处理来避免这些异常”,接下来代码实战一番;

  • ArithmeticException(运算条件异常)

  代码举例:


public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {
        int a=0;
        int b=1;
        System.out.println(b/a);  //运算条件异常,除数为0触发异常
    }
}

复制代码

  运行结果:

first_exception.png

图3.3 运算条件异常

  解决方法:

public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {

        int a=0;
        int b=1;
        if (a!=0) {  //加逻辑条件,避免异常
            System.out.println(b/a);  
        }

    }
}

复制代码
  • NullPointerException(空指针异常)

  代码举例:


public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {
        String str=null;
        System.out.println(str.charAt(2)); //空指针
    }
}

复制代码

  运行结果:

NullPoint.png

图3.4 空指针异常

  解决方法:

public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {
        String str=null;
        if (str!=null) {   //加逻辑判断
            System.out.println(str.charAt(2));
        }
    }
}

复制代码
  • ClassCastException (类型转换异常)

  代码举例:


public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {

        Animals dog=new Dog();
        Cat cat=(Cat) dog;  //类型转换

    }
}

class Animals
{

}

class Dog extends  Animals
{

}

class Cat extends Animals
{
}

复制代码

  运行结果:

CastClass.png

图3.5 类型转换异常

  解决方法:


public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {

        Animals dog=new Dog();
        Cat cat=(Cat) dog;  //类型转换
        if (dog instanceof Cat) {  //加逻辑判别
            Cat cat=(Cat) dog;
        }
    }
}

class Animals
{

}

class Dog extends  Animals
{

}

class Cat extends Animals
{
}

复制代码
  • ArrayIndexOutOfBoundsException(数组跨界异常)

  代码举例:


public class C0001FirstExcept {

    public static void main(String[] args) throws Exception {
        int[] intA=new int[5];
        System.out.println(intA[5]);
    }
}

复制代码

  运行结果:

arrayindexout.png

图3.6 数据下标越界异常

  解决方法:

public class C0001FirstExcept {
    private String str;

    public static void main(String[] args) throws Exception {

        int[] intA=new int[5];
        a=5;
        if (a<intA.length) {
            System.out.println(intA[a]);
        }

    }
}

复制代码
  • NumberFormatException(数据格式异常)

  代码举例:

public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {
        String str2="123456hij";
        System.out.println(Integer.parseInt(str2));
    }
}

复制代码

  运行结果:

NuberFormat.png

图3.7 数据格式异常

  解决方法:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class C0001FirstExcept {
    public static void main(String[] args) throws Exception {
        String str2="123456hij";
        Pattern p=Pattern.compile("^\\d+$");  //正则表达式,以数字开头和结尾
        Matcher m=p.matcher(str2);  //字符串满足正则才可以
        if (m.matches()) {
            System.out.println(Integer.parseInt(str2));
        }
    }
}

复制代码

3.2.2 CheckedException

CheckedExcept.png

图 3.8 编译时异常

  那些不属于RuntimeException的异常,统称为Checked Exception(编译型异常),如IOException、SQLException等以及用户自定义的Exception异常。 这类异常在编译时就必须做出处理,否则无法通过编译,即如图3.8会出现红线提示异常;   一般怎么解决呢,就要用到接下的第四章的异常的处理;

4. 异常的处理

4.1 捕获异常try……catch……finally

package cn.rowyet.J0004Exception;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class C0003TryCatch {
    public static void main(String[] args) {
        FileReader reader=null;

        try {
            reader=new FileReader("src/test.txt");  //第一个异常文件可能不存在,对应第一个catch
            char read = (char)reader.read();  // 第二个异常,字符可能为空
            char read2 = (char)reader.read();
            System.out.println(read+read2);

        } catch (FileNotFoundException e) {  //第一个catch的异常类型要小于等于后续的catch异常
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {  //finally一定会执行
            try {
               if(reader!=null)
               {
                   reader.close();
               }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

复制代码

  以一个读文件的例子来列举下try-catch-finally的用法要点:

  • try语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码,所以不要每句都去try一遍;
  • try不能单独存在,必须跟着catch或者finally,注意是或,catch可以有多个,但是finally只能有一个;
  • 多个catch捕捉出来的异常类,如果这些类型不同且存在继承关系,那么越是父级的类越要往下放,否则会报错,原理也很简单,因为如果你catch出来的最大父类放在了第一个,那么后续的catch出来的子类根本发挥不了作用;
  • finally大多数情况下都是会执行的,不管发没发生异常,除非在finally之前遇到System.exit(0)或程序被断电、网络等因素终止了;

4.2 声明异常throws

  • CheckedException产生时,不一定立刻处理它,可以再把异常throws到上级,一直可以抛到JRE,有点类似甩锅,终究是JRE抗下了一切;

  • 方法重写中声明异常原则:子类重写父类方法时,如果父类方法有声明异常,那么子类声明的异常范围不能超过父类声明的范围。

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class C0002CheckedException {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fileInputStream=new FileInputStream("");

    }
}
复制代码

5. 自定义异常

  自定义异常的要点:

  1. 在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,或者想要加一些特殊的异常描述,比如中文描述,这种情况下可以创建自己的异常类,即自定义异常类,在开源项目中一般会定义一套服务于该项目的自定义异常;

  2. 自定义异常类一般只需从Exception类或者它的子类派生一个子类即可。但是如果自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。

  3. 习惯上,自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。

  4. 不需要定义新类,也可以直接throw一个异常包含自定义的描述,只是写项目这样显得很乱不规范,少用;

  案例代码:

package cn.rowyet.J0004Exception;

public class C0005SelfException {
    public static void main(String[] args) throws HeightExcept {
        Person person = new Person();
        person.setName("RowYet");
        person.setHeight(500.0);  //传一个超过3米的人引发异常
    }

}

class HeightExcept extends Exception
{
    public HeightExcept(){};

    public HeightExcept(String message){
        super(message);
    }
}

class Person
{
    private String name;
    private double height;

    public String getName() {
        return name;
    }

    public double getHeight() {
        return height;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setHeight(double height) throws HeightExcept {
        if(height>300.0)   //判断身高,超过3米则异常
        {
            throw new HeightExcept("人的身高不能超过3米");
        }
        this.height = height;
    }

}
复制代码

  运行结果: 在这里插入图片描述

图5.1 自定义新类的异常

  当然也可以直接抛出某一个异常类但是自定义抛出的异常内容,但是你抛出的什么类型就要遵循该类型的使用规范,代码如下;

public class C0005SelfException {
    public static void main(String[] args) {

        int b=0;
        if(b!=0)
        {
            System.out.println(1/b);
        }
        else
        {
            throw new ArithmeticException("除数不能为0");
        }

    }
}


复制代码

  运行结果; 在这里插入图片描述

图5.1 利用已存在的异常类抛出自定义的异常提示

6. 如何解决异常

  实际开发中,异常是家常便饭,而且只有解决的异常越多,你才举例大神越来越近,那么遇到异常该怎么处理呢?

6.1 尝试自己解决

  自己动手丰衣足食,第一步是先尝试自己动手,定位异常的位置,查看异常的原因,看看能否自己解决;

6.2 百度、必应、谷歌

  百度、必应、谷歌,三大神器,基本上常见的异常都有前人遇见过,针对自己处理不了的异常,可以将异常的关键字去百度、必应、谷歌查找相关网页寻找思路。

6.3 求助同行的大神

  以上两步都解决不了,可以尝试求助同行的大神,寻求帮助;

6.4 切忌看不懂,我不会

  切忌一看到异常,又是英文就慌了神,我不会,看不懂,逃避就去问别人,这样不但是对自己不负责,也是不尊重他人的成果。

  以上就是JavaSE异常及智能的总结;

猜你喜欢

转载自juejin.im/post/7086873815141056548