深入理解java的异常情况

目录
  • 什么是异常?
  • 异常的存在形式
  • Java异常体系
  • 异常的分类
    • 运行时异常
    • 编译时异常
    • 错误 Error
    • 编译时异常和运行时异常的区别
    • 常见的异常
  • 防御式编程
  • Java处理异常的语法
    • 异常抛出—throws
      • 异常捕获—try…catch
  • 自定义异常类
  • 总结

什么是异常?

在程序的运行或者编译时,所产生的错误统称为异常 (也叫Bug)

异常的存在形式

异常在java中以类的形式存在,每一个异常类都可以创建异常对象

我们平时看到的异常,都被封装成一个类

例如:0 为除数,异常为:ArithmeticException

查看在线文档会发现:

Java异常体系

异常的分类

异常分为:运行时异常 和 编译时异常

运行时异常

运行时异常又称为:非受查异常,指的是在程序运行时所抛出的异常

特点:Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过

编译时异常

编译时异常又称为:受查异常,在程序编译时的异常

是RuntimeException以外的异常,类型上都属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过

错误 Error

是程序无法处理的错误,表示运行应用程序中较严重问题

错误是预测不到的,如果应用程序出现了Error,那么将无法恢复,只能重新启动应用程序

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

编译时异常 运行时异常
发生概率 比较高 比较低
处理方式 需要在运行之前对其进行 预处理 没必要提前进行预处理

常见的异常

我们之前已经接触了一些异常,常见的异常还是有必要记住的

0为除数(算数异常)

int a = 10 / 0;

数组下标越界(数组下标越界异常)

int[] array = {1,2,3};
System.out.println(array[4]);

访问 null(空指针异常)

空指针异常出现的概率非常高!!

int[] array = {1,2,3};
array = null;
System.out.println(array[2]);

防御式编程

程序出现问题的时候及时通知程序猿,主要有两种主要的方式:

BYBL

Look Before You Leap,在操作之前就做充分的检查

举例:一个男孩子很喜欢一个女孩子,想要拉女孩子的手,提前问:我可以拉你的手嘛?

EAFP

It's Easier to Ask Forgiveness than Permission,“事后获取原谅比事前获取许可更容易”,也就是先操作,遇到问题再处理

举例:男孩子先拉的女孩子的手,女孩子很生气,呼了一巴掌,男孩子再回来巴拉巴拉道歉

异常的核心就是EAFP

Java处理异常的语法

异常抛出—throws

指编程人员主动抛出一个异常

任何Java代码都可以抛出异常,在方法声明的位置上使用 throws 关键字抛出,抛给方法的调用者来处理,这种处理异常的态度:上报

举例:

public static void testThrows() throws NullPointerException {
    Integer p = null;
    System.out.println(p + 1);
}

注意:

  • 方法的调用者负责处理该异常
  • 在定义方法时,把异常抛出就是为了提醒方法的使用者,有异常需要预处理
  • 如果异常对象是 非RuntimeException,则需要在方法申明时加上该异常的抛出
  • 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
  • 执行到 throw 语句,后面的语句块不再执行

异常捕获—try…catch

这个异常不会上报,自行处理,异常抛到此处为止,不再上抛

try {
 //可能出现异常的代码
}
catch (Exception1 e) { //参数:异常的类型 e
//捕获try当中可能出现的异常
}
catch (Exception2 e) { //参数:异常的类型 e
//捕获try当中可能出现的异常
} finally {

}

举例:

int[] array = {2,4,6,8};
try{
    System.out.println(array[4]);
}
catch (ArrayIndexOutOfBoundsException e){
    System.out.println("捕获到数组越界异常!");
}

对比:

注意:

  • try{ } 包含了可能出现异常的代码
  • catch 可以有一个或多个,catch中是捕获 try 当中可能出现的异常,可以通过 catch 捕获多个异常
  • catch 块当中,一定要捕获相应的异常,如果程序抛出的异常在 catch 块当中不能被捕获,则会交给JVM来处理异常,程序便会异常终止
  • 当 try 中的代码出现异常时,try 里异常之后的代码不会执行,马上会跳转到相应的catch语句块中,如果没有异常便不会跳转
  • 不管是否出现异常,finally{ } 里的代码都执行,finally和catch可以分开使用,但finally必须和try一块使用
  • 不建议直接捕获 Exception 异常,要捕获就捕获具体的
  • 不建议在 finally 中出现 return语句
  • catch 只能处理对应种类的异常

try 负责回收资源

Scanner scan = new Scanner(System.in);
try{
    int n = scan.nextInt();
    System.out.println(10/n);
}
catch (ArithmeticException e){
    e.printStackTrace();
}
finally {
    scan.close();
}

在Idea里,会发现 try 背景色会加深,将鼠标放在 try,按 Alt + Enter

出现上图,回车即可!

然后代码就会自动被优化:

即:将 Scan 对象在 try( )中创建,就能保证在 try 执行完毕后自动调用 Scanner 的 close 方法

向上传递,处理异常

先看代码:

public static void func(){
    int[] array = {2,4,6,8};
    System.out.println(array[4]);
}
public static void main(String[] args) {
    func();
}

上述代码,func 方法里存在数组越界异常,但并没有处理,到 main 中调用 func 方法,由于 main方法里也没有处理该异常,则会交给 JVM,那么程序一运行,遇到异常,直接就会终止

改进:

public static void func(){
    int[] array = {2,4,6,8};
    System.out.println(array[4]);
}
public static void main(String[] args) {
    try {
        func();
    }
    catch (ArrayIndexOutOfBoundsException e){
        e.printStackTrace();
    }
    finally {
        System.out.println("hahaha");
    }

异常的传递是可以沿着栈向上传递的,方法是要在栈上开辟内存的

处理流程

  • 程序先执行 try 中的代码
  • 若 try 中的代码出现异常,就会结束 try 中的代码,看和 catch 中的异常类型是否匹配
  • 若找到匹配的异常类型,就会执行 catch 中的代码
  • 若没有找到匹配的异常类型,就会将异常向上传递到上层调用者
  • 无论是否找到匹配的异常类型,finally 中的代码都会被执行到(在该方法结束之前执行)
  • 若上层调用者也没有处理的了异常,就继续向上传递
  • 一直到 main 方法也没有合适的代码处理异常,就会交给 JVM 来进行处理,此时程序就会异常终止

自定义异常类

自定义异常类步骤:

编写一个类继承 Exception 或者 RuntimeException提供两个 构造方法,一个无参数的,一个带有 String 参数的

class MyException extends RuntimeException{
    //构造方法
    public MyException() {
        super();
    }
    public MyException(String message) {
        super(message);
    }
}

自己玩~~

public static void func(int x){
    if(x == 10){
        throw new MyException("x==10");
    }
}
public static void main(String[] args) {
    try {
        func(10);
    }
    catch (MyException e){
        e.printStackTrace();
    }
}

输出结果:

注意事项:

  • 一定要继承一个父类异常,通常是 Exception / RuntimeException
  • 一般建议继承Exception,好处是必须要处理异常

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 在日志中记录Java异常信息的正确姿势分享

    目录 日志中记录Java异常信息 遇到的问题 原因分析 正确的做法 java异常在控制台和日志里面的打印记录 1.e.printStackTrace()打印在哪里 2.e.printStackTrace()打印的内容是什么 3.如果将e.printStackTrace()的信息打印在日志里应该怎么做呢? 日志中记录Java异常信息 遇到的问题 今天遇到一个线上的BUG,在执行表单提交时失败,但是从程序日志中看不到任何异常信息. 在Review源代码时发现,当catch到异常时只是输出了e.get

  • Java异常的处理机制

    图片解析: 1.生成字节码文件的过程可能产生编译时异常(checked),由字节码文件到在内存中加载.运行类此过程可能产生运行时异常(unchecked), 2.JAVA程序在执行过程中所发生的异常事件可分为两类: > Error: Java虚拟机无法解决的的严重问题.如:JVM系统内部错误.资源耗尽等严重情况.比如:StackOverflowError和OOM.一般不编写针对性的代码进行处理. > Exception: 其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行

  • Java异常类型及处理详情

    目录 一.异常处理的关键字 1.throw 2.throws 异常结构为: Throwable 为顶级父类 子类Error为严重报错 , 子类Exception就是我们所说的异常了 一.异常处理的关键字 java中处理异常的有五个关键字: try.catch .finally . throw .throws throw抛出异常 , thorws声明异常 , 捕获异常 try_catch 1.throw public class SegmentFault { public static void

  • Java 知识难点之异常的认知与使用详解

    目录 前言 一. 异常的背景 初识异常 数组下标越界 访问 null 对象 异常分为2种: 异常体系: 防御式编程: 异常的好处 LBYL 风格的代码(不使用异常) EAFP 风格的代码(使用异常) 二.异常的基本用法 基本语法 异常处理流程 关于异常的处理方式 抛出异常 三. 自定义异常类 前言 本篇文章你会学习到什么是异常,异常的基本语法使用,和自定义异常,干货多多!!! 一. 异常的背景 初识异常 我们曾经的代码中已经接触了一些 "异常" 了. 例如 除以 0 public st

  • Java try()语句实现try-with-resources异常管理机制操作

    目录 Java try()语句实现try-with-resources异常管理机制 不使用try-with-resources时,使用的资源要在finally中进行释放 使用try-with-resources时 自定义AutoCloseable实现 try-with-resources语句优雅的关闭资源 在java1.7以前,我们关闭资源的方式如下 我们先直接上一个demo,方便理解 反编译之后我们可以看见 源码里面有解释 总结 Java try()语句实现try-with-resources

  • 深入理解java的异常情况

    目录 什么是异常? 异常的存在形式 Java异常体系 异常的分类 运行时异常 编译时异常 错误 Error 编译时异常和运行时异常的区别 常见的异常 防御式编程 Java处理异常的语法 异常抛出-throws 异常捕获-try-catch 自定义异常类 总结 什么是异常? 在程序的运行或者编译时,所产生的错误统称为异常 (也叫Bug) 异常的存在形式 异常在java中以类的形式存在,每一个异常类都可以创建异常对象 我们平时看到的异常,都被封装成一个类 例如:0 为除数,异常为:Arithmeti

  • 深入理解java中的重载和覆盖

    说到java中的重载和覆盖呢,大家都很熟悉了吧,但是呢我今天就要写这个. 本文主题: 一.什么是重载 二.什么是覆盖 三.两者之间的区别 重载(overload): 在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数的类型不同,即可称之为该函数重载了. 即当函数同名时,只看参数列表.和返回值类型没关系. 重载使用的时候需要注意: 1.在使用重载时只能通过不同的参数样式.例如,不同的参数类型,不同的参数个数,不同的参数顺序. 2.方法的异常类型和数目不会对重载造成影响

  • 深入理解java异常处理机制及应用

    1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看看下面的代码,"猜猜"它执行后的结果会是什么?不要往后看答案.也不许执行代码看真正答案哦.如果你的答案是正确,那么这篇文章你就不用浪费时间看啦. package Test; public class TestException { public TestEx

  • 深入理解Java编程中异常处理的优劣

    Java编程中的异常处理是一个很常见的话题了,几乎任何一门介绍性的Java课程都会提到异常处理.不过,我认为很多人其实没有真正掌握正确处理异常情况的方法和策略,最多也就不过了解个大概,知道概念.我想对三种不同程度和质量的Java异常处理进行了讨论,所阐述的处理异常的方式按手法的高下分为:好,不好和恶劣三种.同时提供了一些解决这些问题的技巧.首先解释一些java异常处理中必须搞清楚的定义和机制.Java语言规范将自Error类或RuntimeException类衍生出来的任何违例都称作"不可检查&

  • 理解java中的深复制和浅复制

    Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文会试图澄清这一概念.并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象.本文会让你了解什么是影子clone与深度clone,认识它们的区别.优点及缺点. 看到这个标题,是不是有点困惑:Java语言明确说明取消了指针,因为指针往往是在带来方便的同时也是导致代码不安全的根源,同时也会使程序的变得非常复杂难以理解,滥用指针写成的代码不亚于

  • 深入理解Java注解类型(@Annotation)

    Java注解是在JDK5时引入的新特性,鉴于目前大部分框架(如spring)都使用了注解简化代码并提高编码的效率,因此掌握并深入理解注解对于一个Java工程师是来说是很有必要的事.本篇我们将通过以下几个角度来分析注解的相关知识点 理解Java注解 实际上Java注解与普通修饰符(public.static.void等)的使用方式并没有多大区别,下面的例子是常见的注解: public class AnnotationDemo { //@Test注解修饰方法A @Test public static

  • 全面理解java中的异常处理机制

    一.java异常总结: 异常就是程序运行时出现不正常运行情况 1.异常由来: 通过java的类的形式对现实事物中问题的描述,并封住成了对象 其实就是java对不正常情况描述后的对象体现 2.对于问题的划分有两种:一种是严重的问题,一种是非严重的问题 对于严重的,java通过Error类来描述 对于Error一般不编写针对性的代码对其进行处理 对于非严重的,java通过Exception类来描述 对于Exception可以使用针对性的处理方式进行处理 3.常见的异常有:数组角标越界异常,空指针异常

  • 深入理解Java编程线程池的实现原理

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,

  • 深入理解Java虚拟机体系结构

    1概述 众所周知,Java支持平台无关性.安全性和网络移动性.而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操作系统是什么.正是得益于Java虚拟机,它号称的"一次编译,到处运行"才能有所保障. 1.1Java程序执行流程 Java程序的执行依赖于编译环境和运行环境.源码代码转变成可执行的机器代码,由下面的流程完成: Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行.Java程序的运行需要Java虚拟

  • 解析Java 泛型什么情况下不能使用

    一.前言 Java泛型来保证类型安全,防止在运行时发生类型转换异常,让类型参数化,提高了代码的可读性和重用率.但是有些情况下泛型也是不允许使用的,以下是不能使用泛型的一些场景. 二. 什么情况下不能使用Java泛型 1 不能使用泛型的形参创建对象. T o=new T(); // 不允许 2 在泛型类中,不能给静态成员变量定义泛型 Java 中的静态类型随着类加载而实例化,此时泛型的具体类型并没有声明.同时因为静态变量作为所有对象的共享变量,只有类实例化或者方法调用时才能确定其类型.如果是泛型类

随机推荐