Java异常详解——一篇文章搞定Java异常
1. 异常的体系结构
定义:异常就是有异于常态,和正常情况不一样,有错误出现。在java中,将程序执行过程中的不正常的情况称之为异常,开发过程中的语法错误和逻辑错误不是异常,发生异常时java会阻止当前方法或作用域的情况。
异常的体系结构
- Throwable:是java中所有异常和错误的超类,其两个子类为 Error(错误) 和 Exception(异常)
- Error: 是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。
- Exception: 是程序本身可以捕获并且可以处理的异常。其中可分为运行时异常(RuntimeException)和非运行时异常,也叫做受检异常
- 运行时异常(不受检异常): RuntimeException类极其子类表示JVM在运行期间可能出现的错误。编译器不会检查此类异常,并且不要求处理异常,比如用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
- 非运行时异常(受检异常): Exception中除RuntimeException极其子类之外的异常。编译器会检查此类异常,如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。
从程序执行的过程来看编译时异常和运行时异常
- 编译时异常:程序在编译时发生的异常(javac 源文件名.java)
- 运行时异常: 程序在运行时发生的异常(java 字节码文件名)
2. 常见的异常
2.1 运行时异常
- NullPointerException (空指针异常)
指针指向的对象为空(null)
package com.broky.exception;
public class NullPointerEx {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[3]);
/*Exception in thread "main" java.lang.NullPointerException: Cannot load from int array because "arr" is null
at com.broky.exception.NullPointerEx.main(NullPointerEx.java:6)*/
}
}
- ArrayIndexOutOfBoundException (数组角标越界异常) StringIndexOutOfBoundException (字符串越界异常) ...
package com.broky.exception;
public class XIndexOutOfBoundEx {
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr[3]);
/*Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at com.broky.exception.XIndexOutOfBoundEx.main(XIndexOutOfBoundEx.java:6)*/
String str = "abc";
System.out.println(str.charAt(3));
/*Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 3
at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
at java.base/java.lang.String.charAt(String.java:711)
at com.broky.exception.XIndexOutOfBoundEx.main(XIndexOutOfBoundEx.java:11)*/
}
}
- ClassCastException (类型转换异常)
package com.broky.exception;
public class ClassCastEx {
public static void main(String[] args) {
Object obj = new Double(1);
String str = (String)obj;
/*Exception in thread "main" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String (java.lang.Double and java.lang.String are in module java.base of loader 'bootstrap')
at com.broky.exception.ClassCastEx.main(ClassCastEx.java:7)*/
}
}
- NumberFormatException
package com.broky.exception;
public class NumberFormatException {
public static void main(String[] args) {
String str = "abc";
int a = Integer.parseInt(str);
/*Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:660)
at java.base/java.lang.Integer.parseInt(Integer.java:778)
at com.broky.exception.NumberMismatchEx.main(NumberMismatchEx.java:6)*/
}
}
- InputMismatchException
package com.broky.exception;
import java.util.Scanner;
public class InputMismatchEx {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.println(num);
/*asd
Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:939)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
at com.broky.exception.InputMismatchEx.main(InputMismatchEx.java:8)*/
}
}
- ArithmeticException (算数异常)
package com.broky.exception;
public class ArithmeticEx {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b;
/*Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.broky.exception.ArithmeticEx.main(ArithmeticEx.java:7)*/
}
}
2.2 编译时异常 (编译时异常必须进行处理否则无法运行)
- IOException
- FileNotFoundException
- ClassNotFoundException
3. 异常的抓抛模型原理
- 异常的抛出:如果程序在运行过程中执行某段代码时发生了异常,那么系统(JVM)将会根据异常的类型,在异常代码处创建对应的异常类型的对象并抛出,抛出给程序的调用者。一旦抛出对象以后,其后的代码不再运行,程序终止.
异常的抛出分为:① 系统向外抛出异常 ② 手动向外抛出异常(throw) - 异常的抓取:异常的抓取可以理解为异常的处理方式, 取有 try-catch-finally 和 throws 两种方式(详情见异常的处理部分)
4. 异常的处理
4.1 try - catch - finally
- 将可能出现异常的代码放到try{}中, 运行时, 如果代码发成了异常的话,就会生成一个对应的异常类的对象.
这个产生的异常对象会与catch的异常类型相匹配,匹配成功后就被 catch 捕获,然后运行catch{}中的代码, 一般catch中的代码为处理异常的代码, 比如返回异常的信息和返回异常的详细信息等. 一旦异常处理完成,就会跳出当前的try-catch结构
无论有没有发生异常finally中的代码都会最后被执行
- catch多个异常类型的时候, 如果有子父类关系,小的范围写上面大的范围写下面; 如果没有子父类关系,谁在上谁在下无所谓.
- 常用的异常对象处理方式: ① e.getMessage() ② e.printSrackTrace()
- 在try结构中生命的变量,在出了try结构以后,就不能在被调用。如果想避免这种情况,就需要在try之前声明变量并初始化,在try中赋值。
- 如果finally里面有return那么返回的一定是finally里面的
- try-catch-finally结构可以相互嵌套
- 体会1:使用try-catch-finally处理编译时异常,是让程序在编译时就不再报错,但是运行时仍然有可能报错。相当于我们使用try-catch将一个编译时可能出现的异常,延迟到运行时出现。p376
- 体会2:在开发中,运行时异常比较常见,此时一般不用try-catch去处理,因为处理和不处理都是一个报错,最好办法是去修改代码。针对编译时异常,我们一定要考虑异常处理。
package com.broky.exception.demo02;
public class TryCatchFinally {
public static void main(String[] args) {
String str = "abc";
try {
int i = Integer.parseInt(str);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("运行完毕");
}
}
}
4.2 throws + 异常类型
throws 一般用于方法中可能存在异常时, 需要将异常情况向方法之上的层级抛出, 由抛出方法的上一级来解决这个问题, 如果方法的上一级无法解决的会就会再将异常向上抛出, 最终会抛给main方法. 这样一来main方法中调用了这个方法的时候,就需要解决这个可能出现的异常.
当然main方法也可以不解决异常, 将异常往上抛出给java虚拟机, 如果java虚拟机也无法解决的话,那么java虚拟机就over了throws + 异常类型
写在方法的声明处.指明此方法执行时,可能会抛出的异常类型.
一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。
异常代码的后续的代码,就不再执行!体会:tyr-catch-fianlly:真正的将异常给处理掉了。throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉。
注意: 将异常向上抛出也算是一种处理异常的方法
package com.broky.exception;
import java.io.FileNotFoundException;
public class ThrowsEx {
public void setAge2(int age) throws FileNotFoundException {
if (age < 0) {
throw new FileNotFoundException("输入的年龄小于0");
}
}
public void TestsetAge2() throws FileNotFoundException {
setAge2(2);
}
public static void main(String[] args) {
try {
ThrowsEx.throwsExTest();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
5. 重写方法异常抛出的规则
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
下面代码中,main方法中向display方法中传入了SuperClass的子类对象.那么到display方法中调用s.method调用的就是SpuerClass的子类SubClass重写的method方法, 如果这个方法抛出的异常范围大于父类SuperClass所抛出的异常的话,那么在display方法中对异常的catch处理就会catch不到这个异常.
package com.broky.exception;
import java.io.FileNotFoundException;
import java.io.IOException;
public class OverrideTest {
public void display(SuperClass s) {
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
OverrideTest overrideTest = new OverrideTest();
overrideTest.display(new SubClass());
}
}
class SuperClass {
public void method() throws IOException {
System.out.println("super");
}
}
class SubClass extends SuperClass {
public void method() throws FileNotFoundException {
System.out.println("sub");
}
}
6. 开发中如何选择使用try-catch-finally 还是throws?
- 如果分类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用tyr-catch-finally方式处理.
- 执行的方法中,先后又调用了另外的几个方法,这几个方法是递进关系执行的.我们建议这几个方法使用throws的方式处理.而执行的方法a可以考虑使用try-catch-finally方式进行处理.
因为如果在a方法要调用d方法时,如果在b方法内try-catch,当b方法异常时,并不会给方法a返回所需要的数据.因此最好使用throws将异常集合到一块再处理. - 注意: try-catch和throws再方法中不要同时使用,因为只要使用try-catch就已经将异常处理掉了,再throws没有任何意义.
7. 手动抛出异常 throw
手动抛出的异常有两种, 分别为运行时异常和编译时异常.
抛出运行时异常时, 可以不用处理抛出的这个异常.
抛出编译时异常时, 必须要处理抛出的这个异常.
详细解析请看下面的代码
package com.broky.exception;
/*Demo
手动抛出异常
自己throw(制造一个异常抛出)
*/
import java.io.FileNotFoundException;
public class ThrowEx {
// 手动抛出运行时异常
public void setAge(int age) {
if (age < 0) {
throw new NullPointerException("输入的年龄小于0");
}
}
/*
此方法手动抛出了运行时异常
运行时异常可以不用处理
*/
// 手动抛出编译时异常
public void setAge2(int age) throws FileNotFoundException {
if (age < 0) {
throw new FileNotFoundException("输入的年龄小于0");
}
}
/*
此方法手动抛出的了编译时异常
编译时异常需要被处理
这里采用了 throws 这个异常, 也就是说方法并没有处理这个异常, 而是将异常抛给了调用者
这样一来调用了这个方法的人就必须要处理这个异常才可以.
注意: 在这里并不用自己使用 try catch 处理这个异常
自己在方法里抛出异常, 方法再自己处理没有任何作用.
所以方法中的异常需要抛给调用者去处理.
*/
public static void main(String[] args) {
ThrowEx throwEx = new ThrowEx();
throwEx.setAge(-5);
}
}
8. 自定义异常类
- 继承现有的异常结构:RuntimeExceptiona(不用处理)、Exception(需要处理)
- 提供全局常量:serialVersionUID
- 提供重载的构造器
package com.broky.exception;
public class MyException extends RuntimeException{
static final long serialVersionUID = -1234719074324978L;
public MyException(){
}
public MyException(String message){
super(message);
}
public static void main(String[] args) {
throw new MyException("自定义运行时异常");
/*Exception in thread "main" com.broky.exception.MyException: 自定义运行时异常
at com.broky.exception.MyException.main(MyException.java:15)*/
}
}
Java异常详解——一篇文章搞定Java异常的更多相关文章
- Java多线程详解——一篇文章搞懂Java多线程
目录 1. 基本概念 2. 线程的创建和启动 2.1. 多线程实现的原理 2.2.多线程的创建,方式一:继承于Thread类 2.3.多线程的创建,方式一:创建Thread匿名子类(也属于方法一) 2 ...
- Java 集合详解 | 一篇文章解决Java 三大集合
更好阅读体验:Java 集合详解 | 一篇文章搞定Java 三大集合 好看的皮囊像是一个个容器,有趣的灵魂像是容器里的数据.接下来讲解Java集合数据容器. 文章篇幅有点长,还请耐心阅读.如只是为了解 ...
- 一篇文章搞定Java注解^_^
0.序言 自己写这些文章本来想着自己系统的整理下知识,将知识串起来,后面复习用,或者以后年龄大了,去教育机构呀,拿出自己整理的笔记,你看这人爱总结爱分享,文笔也还能看,方便找工作不是. 很开心的是,有 ...
- 一篇文章搞定百度OCR图片文字识别API
一篇文章搞定百度OCR图片文字识别API https://www.jianshu.com/p/7905d3b12104
- 一篇文章搞定Git——Git代码管理及使用规范
一篇文章搞定Git--Git代码管理及使用规范 https://blog.csdn.net/weixin_42092278/article/details/90448721
- Java枚举类与注解详解——一篇文章读懂枚举类与注解详
目录 一.枚举类 ① 自定义枚举类 ② enum关键字定义枚举类 ③ enum 枚举类的方法 ④ enum 枚举类实现接口 二.注解 ① 生成文档相关注解 ②注解在编译时进行格式检查 ③注解跟踪代码的 ...
- 一篇文章搞定面试中的二叉树题目(java实现)
最近总结了一些数据结构和算法相关的题目,这是第一篇文章,关于二叉树的. 先上二叉树的数据结构: class TreeNode{ int val; //左孩子 TreeNode left; //右孩子 ...
- 一篇文章搞定面试中的链表题目(java实现)
最近总结了一下数据结构和算法的题目,这是第二篇文章,关于链表的,第一篇文章关于二叉树的参见 废话少说,上链表的数据结构 class ListNode { ListNode next; int val; ...
- 一篇文章搞定 Nginx 反向代理与负载均衡
代理 要想弄明白反向代理,首先要知道啥是正向代理,要搞懂正向代理只需要知道啥是代理即可.代理其实就是一个中介,在不同事物或同一事物内部起到居间联系作用的环节.比如买票黄牛,房屋中介等等. 在互联网中代 ...
随机推荐
- 「NGK每日快讯」12.29日NGK第56期官方快讯!
- NLog整合Exceptionless
前言 在实际的.Net Core相关项目开发中,很多人都会把NLog作为日志框架的首选,主要是源于它的强大和它的扩展性.同时很多时候我们需要集中式的采集日志,这时候仅仅使用NLog是不够的,NLog主 ...
- winform解析json API数据
{ "retCode" : "SUCCESS", "msg" : "", "data" : { ...
- TERSUS无代码开发(笔记08)-简单实例电脑端后台逻辑开发
主管审批功能逻辑开发 1.查询逻辑开发(查询待审批记录) 2.批准处理(将选中的一条记录进行批准处理) =============================================== ...
- 构造方法和this的作用
一.构造方法概述 构造方法是一个特殊的方法 是创建对象时候调用的方法 方法的名字很特殊:必须和类名保持一致,大小写都要一样 方法没有返回值 方法也没有返回值类型 构造方法无法在外部手动调用 publi ...
- WIFI6 基本知识(一)
什么是WI-FI6(802.11ax) Wi-Fi 6 是下一代 802.11ax 标准的简称.随着 Wi-Fi 标准的演进,WFA 为了便于 Wi-Fi 用户和设备厂商轻松了解其设备连接或支持的 W ...
- zabbix 面板graph图上没有数据显示
1. 问题: zabbix_server日志出现大量如下错误: query failed: [1526] Table has no partition for value 1507509984 2. ...
- HDOJ-6656(数论+逆元)
Kejin Player HDOJ-6656 设f[i]为从i升级到i+1期望需要的金钱,由于每级都是能倒退或者升级到i+1,所以询问从l,r的期望金钱可以直接前缀和,那么推导每一级升级需要的期望钱也 ...
- Linux:使用systemd管理进程
Blog:博客园 个人 概述 systemd是目前Linux系统上主要的系统守护进程管理工具,由于init一方面对于进程的管理是串行化的,容易出现阻塞情况,另一方面init也仅仅是执行启动脚本,并不能 ...
- 如何在 C# 8 中使用 Channels
在面对 生产者-消费者 的场景下, netcore 提供了一个新的命名空间 System.Threading.Channels 来帮助我们更高效的处理此类问题,有了这个 Channels 存在, 生产 ...