跳至主要內容

异常

晨光-向大约 9 分钟JavaJava语言

异常

异常是程序经常会出现的,发现错误的最佳时机是在编译阶段,也就是你试图在运行程序之前。但是, 在编译期间并不能找到所有的错误,有一些NullPointerExceptionClassNotFoundException异常在编译期找不到,这些异常是RuntimeException运行时异常,这些 异常往往在运行时才能被发现。

我们写Java程序经常会出现两种问题,一种是java.Iang.Exception,—种是java.Iang.Error,都用来 表示出现了异常情况,下面就针对这两种概念进行理解。

0. 异常基础(学习)

0.1异常

  • 异常的概述

    ​ 异常就是程序出现了不正常的情况

  • 异常的体系结构

0.2 JVM默认处理异常的方式

  • 如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,处理方式有如下两个步骤:

  • 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台

  • 程序停止执行

0.3 try-catch方式处理异常

  • 定义格式

    try {
    	可能出现异常的代码;
    } catch(异常类名 变量名) {
    	异常的处理代码;
    }
    
  • 执行流程

    • 程序从 try 里面的代码开始执行
    • 出现异常,就会跳转到对应的 catch 里面去执行
    • 执行完毕之后,程序还可以继续往下执行
  • 示例代码

    public class ExceptionDemo01 {
        public static void main(String[] args) {
            System.out.println("开始");
            method();
            System.out.println("结束");
        }
    
        public static void method() {
            try {
                int[] arr = {1, 2, 3};
                System.out.println(arr[3]);
                System.out.println("这里能够访问到吗");
            } catch (ArrayIndexOutOfBoundsException e) {
    //            System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
                e.printStackTrace();
            }
        }
    }
    

0.4 Throwable成员方法

  • 常用方法

    方法名说明
    public String getMessage()返回此 throwable 的详细消息字符串
    public String toString()返回此可抛出的简短描述
    public void printStackTrace()把异常的错误信息输出在控制台
  • 示例代码

    public class ExceptionDemo02 {
        public static void main(String[] args) {
            System.out.println("开始");
            method();
            System.out.println("结束");
        }
    
        public static void method() {
            try {
                int[] arr = {1, 2, 3};
                System.out.println(arr[3]); //new ArrayIndexOutOfBoundsException();
                System.out.println("这里能够访问到吗");
            } catch (ArrayIndexOutOfBoundsException e) { //new ArrayIndexOutOfBoundsException();
    //            e.printStackTrace();
    
                //public String getMessage():返回此 throwable 的详细消息字符串
    //            System.out.println(e.getMessage());
                //Index 3 out of bounds for length 3
    
                //public String toString():返回此可抛出的简短描述
    //            System.out.println(e.toString());
                //java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    
                //public void printStackTrace():把异常的错误信息输出在控制台
                e.printStackTrace();
    //            java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    //            at com.itheima_02.ExceptionDemo02.method(ExceptionDemo02.java:18)
    //            at com.itheima_02.ExceptionDemo02.main(ExceptionDemo02.java:11)
    
            }
        }
    }
    

0.5 编译时异常和运行时异常的区别

  • 编译时异常

    • 都是Exception类及其子类
    • 必须显示处理,否则程序就会发生错误,无法通过编译
  • 运行时异常

    • 都是RuntimeException类及其子类
    • 无需显示处理,也可以和编译时异常一样处理

0.6 Throws方式处理异常

  • 定义格式

    public void 方法() throws 异常类名 {
        
    }
    
  • 示例代码

    public class ExceptionDemo {
        public static void main(String[] args) {
            System.out.println("开始");
    //        method();
            try {
                method2();
            }catch (ParseException e) {
                e.printStackTrace();
            }
            System.out.println("结束");
        }
    
        //编译时异常
        public static void method2() throws ParseException {
            String s = "2048-08-09";
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date d = sdf.parse(s);
            System.out.println(d);
        }
    
        //运行时异常
        public static void method() throws ArrayIndexOutOfBoundsException {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);
        }
    }
    
  • 注意事项

    • 这个throws格式是跟在方法的括号后面的
    • 编译时异常必须要进行处理,两种处理方案:try...catch …或者 throws,如果采用 throws 这种方案,将来谁调用谁处理
    • 运行时异常可以不处理,出现问题后,需要我们回来修改代码

0.7 Throws和Throw的区别

0.8 自定义异常

  • 自定义异常类

    public class ScoreException extends Exception {
    
        public ScoreException() {}
    
        public ScoreException(String message) {
            super(message);
        }
    
    }
    
  • 老师类

    public class Teacher {
        public void checkScore(int score) throws ScoreException {
            if(score<0 || score>100) {
    //            throw new ScoreException();
                throw new ScoreException("你给的分数有误,分数应该在0-100之间");
            } else {
                System.out.println("成绩正常");
            }
        }
    }
    
  • 测试类

    public class Demo {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入分数:");
    
            int score = sc.nextInt();
    
            Teacher t = new Teacher();
            try {
                t.checkScore(score);
            } catch (ScoreException e) {
                e.printStackTrace();
            }
        }
    }
    

1. 认识Exception

Exception位于java.lang包下,它是一种顶级接口,继承于Throwable类,Exception类及 其子类都是Throwable的组成条件,是程序出现的合理情况。

在认识Exception之前,有必要先了解一下什么是Throwable.

2. 什么是Throwable

Throwable类是Java语言中所有错误(errors)异常(exceptions)的父类。只有继承于 Throwable的类或者其子类才能够被抛出,还有一种方式是带有Java中的@throw注解的类也可以 抛出。

Java规范open in new window中,对非受查异常和受查异常的定义是这样的:

The unchecked exception classes are the run-time exception classes and the error classes.

The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Error and its subclasses.

也就是说,除了 RuntimeException和其子类,以及error和其子类,其它的所有异常都是 checkedException

那么,按照这种逻辑关系,我们可以对Throwable及其子类进行归类分析

可以看到,Throwable位于异常和错误的最顶层,我们查看Throwable类中发现它的方法和属性有很 多,我们只讨论其中几个比较常用的

//返回抛出异常的详细信息
public string getMessage();
public string getLocalizedMessage();
//返回异常发生时的简要描述
public public String toString()Ҕ
//返回异常发生时的简要描述
public void printStackTrace();
public void printStackTrace(PrintStream s);
public void printStackTrace(PrintWriter s)
//记录栈帧的的当前状态
public synchronized Throwable fillInStackTrace();

此外,因为Throwable的父类也是Object,所以常用的方法还有继承其父类的getClass()。和 getName()方法。

3. 常见的Exception

下面我们回到Exception的探讨上来,现在你知道了 Exception的父类是Throwable,并且Exception 有两种异常,一种是RuntimeException ;—种是CheckedException,这两种异常都应该去捕获。

下面列出了一些Java中常见的异常及其分类,这块面试官也可能让你举出几个常见的异常情况并将其分类

  • RuntimeException
序号异常名称异常描述
1ArraylndexOutOfBoundsException数组越界异常
2NullPointerException空指针异常
3HlegalArgumentException非法参数异常
4NegativeArraySizeException数组长度为负异常
5HlegalStateException非法状态异常
6ClassCastException类型转换异常
  • UncheckedException
序号异常名称异常描述
1NoSuchFieldException表示该类没有指定名称抛出来的异常
2NoSuchMethod Exception表示该类没有指定方法抛出来的异常
3HlegalAccessException不允许访问某个类的异常
4ClassNotFoundException类没有找到抛出异常

4. 与Exception有关的Java关键字

那么Java中是如何处理这些异常的呢?在Java中有这几个关键字throwsthrowtryfinallycatch下面我们分别来探讨一下

4.1 throws 和 throw

在Java中,异常也就是一个对象,它能够被程序员自定义抛出或者应用程序抛出,必须借助于 throws和throw语句来定义抛出异常。

throws和throw通常是成对出现的,例如

static void cacheException() throws Exception{
 throw new Exception();
}

throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。throws语句用在方法声明后面, 表示再抛出异常,由该方法的调用者来处理。

throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。throw是具 体向外抛异常的动作,所以它是抛出一个异常实例。

4.2 try、finally、catch

这三个关键字主要有下面几种组合方式try...catch、try...finally、try...catch...finally。

1. try...catch

try...catch表示对某一段代码可能抛出异常进行的捕获,如下

static void cacheException() throws Exception{
 try {
     System.out.println("1");
 }catch (Exception e){
     e.printStackTrace();
 }
}

2. try...finally

try...finally表示对一段代码不管执行情况如何,都会走finally中的代码

static void cacheException() throws Exception{
 for (int i = 0; i < 5; i++) {
     System.out.println("enter: i=" + i);
     try {
     	System.out.println("execute: i=" + i);
     	continue;
     } finally {
     	System.out.println("leave: i=" + i);
     }
 }
}

3. try...catch...finally

try...catch...finally也是一样的,表示对异常捕获后,再走finally中的代码逻辑。

5. 什么是Error

Error是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作 无关,而表示代码运行时JVM (Java虚拟机)出现的问题。这些错误是不可检查的,因为它们在应用 程序的控制和处理能力之夕卜,而且绝大多数是程序运行时不允许出现的状况,比如

OutOfMemoryErrorStackOverflowError异常的出现会有几种情况,这里需要先介绍一下 Java内存模型JDK1.7。

其中包括两部分,由所有线程共享的数据区和线程隔离的数据区组成,在上面的Java内存模型中,只 有程序计数器是不会发生OutOfMemoryError情况的区域,程序计数器控制着计算机指令的分支、 循环、跳转、异常处理和线程恢复,并且程序计数器是每个线程私有的。

什么是线程私有:表示的就是各条线程之间互不影响,独立存储的内存区域。

如果应用程序执行的是Java方法,那么这个计数器记录的就是虚拟机字节码指令的地址;如果正在执 行的是Native方法,这个计数器值则为空(Undefined)

除了程序计数器外,其他区域:方法区(Method Area)虚拟机栈(VM Stack)本地方法栈 (Native Method Stack)堆(Heap)都是可能发生 OutOfMemoryError 的区域。

  • 虚拟机栈:如果线程请求的栈深度大于虚拟机栈所允许的深度,将会出现 StackOverflowError异常;如果虚拟机动态扩展无法申请到足够的内存,将出现 OutOfMemoryError 。

  • 本地方法栈和虚拟机栈一样

  • 堆:Java堆可以处于物理上不连续,逻辑上连续,就像我们的磁盘空间一样,如果堆中没有内存 完成实例分配,并且堆无法扩展时,将会抛出OutOfMemoryError。

  • 方法区:方法区无法满足内存分配需求时,将抛出。utOfMemoryError异常。

在Java中,你可以把异常理解为是一种能够提高你程序健壮性的机制,它能够让你在编写代码中注意 这些问题,也可以说,如果你写代码不会注意这些异常情况,你是无法成为一位硬核程序员的。