Java异常处理
文章目录
-
- Java异常处理
- 一、异常介绍&异常分类
- 二、异常的处理&生成异常对象
-
- [2.1 try-catch异常处理](#2.1 try-catch异常处理)
- [2.2 声明抛出异常类型throws](#2.2 声明抛出异常类型throws)
- [2.3 手动抛出异常对象(生成异常对象)](#2.3 手动抛出异常对象(生成异常对象))
- [2.4 自定义异常](#2.4 自定义异常)
- 三、异常总结&企业真题
一、异常介绍&异常分类
语法错误:编译不通过不会产生字节码文件,根本不能运行;逻辑错误:不是想要的功能。
1、异常 :指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止
异常的抛出机制:
Java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象
,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。
2、如何处理异常:遇到错误就终止程序运行;编写程序时充分考虑到可能发生的异常和错误,预防和避免,无法避免地就检测、处理
3、异常分类:出现异常会导致程序挂掉
- Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。一般不编写针对性的代码进行处理。
如:StackOverflowError(栈内存溢出------比如递归)和OutOfMemoryError(堆内存溢出,简称OOM------比如对象创建太多)-JVM调优会用到
比较:溢出和泄露:溢出的有栈、堆、方法区都有可能,而泄露指的是对象不用了但是还存在引用导致GC无法及时回收 - Exception:需要使用针对性的代码进行处理,使程序继续运行。否则一旦发生异常,程序也会挂掉。
- 编译时期异常(checked受检):【Maybe可能】在代码编译阶段,执行javac.exe命令时出现的异常,不处理直接编译失败,根本不能生成字节码文件。这种异常不是由于代码引起的,无法避免。
必须要处理,即必须被检查,不然直接不能编译通过了
。编译时提示我们可能会出现这样的异常必须处理。- 类找不到ClassNotFoundException(反射Class.forName("java.lang.String"))、文件找不到FileNotFoundException(读取文件时根本找不到)、IO异常IOException
- 运行时期异常(runtime、unchecked、非受检):java.lang.RuntimeException类及其子类。代码编译阶段不检查,执行java.exe命令时发生的异常。只有等代码运行起来并确实发生了xx异常,才能被发现。由代码编写不当引起的,通过判断、仔细检查可避免。
数组下标越界
ArrayIndexOutOfBoundsException、空指针异常
NullPointerException、类型转换异常
ClassCastException(String转换为Data类型)、数值格式错误
NumberFormatException(非数值型的字符串转为int类型)、输入不匹配
InputMismatchException(Scanner接受控制台输入的类型不匹配)、算术异常ArithmeticException(除0)
- 编译时期异常(checked受检):【Maybe可能】在代码编译阶段,执行javac.exe命令时出现的异常,不处理直接编译失败,根本不能生成字节码文件。这种异常不是由于代码引起的,无法避免。
二、异常的处理&生成异常对象
生成异常对象:自动/手动抛出异常对象→处理异常:try-catch捕获、throws抛出异常
准则:运行时异常可以不处理,编译时异常(会产生编译时异常对象的代码)必须要被处理
异常对象在哪生成的?有异常的代码地方会自动生成/可以手动创建异常对象。自动生成异常对象的本质也是throw生成对象。
有哪些异常类?java提供的运行时/编译时异常、自定义异常类(必须要继承java提供的异常类)
运行时异常:开发中,通常就不进行显示的处理。一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可
编译时异常:定要进行处理。否则编译不通过,
1、java的异常处理机制:将异常处理的程序代码集中在一起
,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
2、异常处理方式:捕获try-catch-finally、抛出throws+异常类型【抛出让别的来处理】。运行时异常可以不处理!
- 过程1:"抛"
- "自动抛":程序在执行的过程当中,一旦出现异常,就会在出现异常的代码处,自动生成对应异常类的对象,并将此对象抛出。一旦抛出,此程序就不执行其后的代码了。【源码本质上也是使用throw抛出异常对象】
- "手动抛":程序在执行的过程当中,不满足指定条件的情况下,我们主动的使用"throw + 异常类的对象"方式抛出异常
- 过程2:"抓"
- 狭义上讲:try-catch的方式捕获异常,并处理。
- 广义上讲:把"抓"理解为"处理"。则此时对应着异常处理的两种方式:① try-catch-finally ② throws
3、异常对象怎么来?在有异常代码处,可以自动抛出,也可以自己手动抛出异常对象!try-catch、throws都是处理异常的方式
2.1 try-catch异常处理
1、语法格式
try-catch语言格式:finally语句和catch语句是可选的,但finally不能单独使用。
java
try{
...... //可能产生异常的代码
}
catch( 异常类型1 e ){
...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
...... //当产生异常类型2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
}
2、使用细节:
- try:可能出现异常的代码,一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。注意try中声明的变量,出了try结构之后,就不可以进行调用了。
- catch:针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。一旦处理结束,代码就可继续向下执行。如果多个catch结构子类在上父类在下,如果不存在子父类关系没有顺序要求
- ① 自己编写输出的语句。
- ② public String getMessage():打印异常的详细信息。(推荐)
- ③ public void printStackTrace()`::获取发生异常的原因。
- finally:一定要执行的代码,无论try/catch中是否存在任未被处理的异常、return语句,这个中的语句都一定会被执行。如果finally中有return语句,那么try/catch中的return就不起作用了。
- 作用:有一些资源(比如:数据库连接、输入流输出流、Socket连接、Lock锁等资源),在使用完后必须显示的关闭操作,否则GC不会自动回收,导致内存泄漏。保证是否出现了违背处理的异常情况下,都能被关闭。
- 例外: System.exit(0) 来终止当前正在运行的 Java 虚拟机
优先finally中return语句,但是try/catch中的retrun也会执行,只不过会暂存,执行完finally中语句后再执行,但是如果finally中youreturn,那么try/catch中的return就不会执行了。
java
int num = 10;
try{
num /= 0;
return num++;
}catch(NumberFormatException e){
return num++;
}finally{
return num;//num为10
}
2.2 声明抛出异常类型throws
1、定义:方法声明处
throws语法格式:最好不要在main方法中throws
throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
java
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2...{
//可能出现编译时异常的代码
}
//举例
public void readFile(String file) throws FileNotFoundException,IOException {
...
// 读文件的操作可能产生FileNotFoundException或IOException类型的异常
FileInputStream fis = new FileInputStream(file);
//...
}
2、是否真正处理了异常?
从编译是否能通过的角度看,看成是给出了异常万一要是出现时候的解决方案。此方案就是,继续向上抛出(throws)但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者仍然需要考虑如何处理相关异常。从这个角度来看,throws的方式不算是真正意义上处理了异常。
3、方法重写中throws的要求:(针对于编译时异常,throws 运行时异常类型没有要求)
- 子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类。
- 如果父类被重写方法的方法签名后面没有 "throws 编译时异常类型",那么重写方法时,方法签名后面也不能出现"throws 编译时异常类型"。此时子类中只能try-catch
方法间的调用也是同理
原因:理解m1、父类中可以捕获异常,那么捕获的异常类型必须要大于m2或者子类中的,由于多态性才能接收m2、子类抛出的异常。
4、两种异常处理方式选择:
前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常。
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须使用try-catch-finally来处理,保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能使用try-catch-finally进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。
2.3 手动抛出异常对象(生成异常对象)
1、为什么需要手动抛出异常对象?
在实际开发中,如果出现不满足具体场景的代码问题,就有必要手动抛出一个指定类型的异常对象。比如不能除0会自动抛出,但是有具体要求不能除-5,那么此时就需要手动抛出异常对象。
抛出的异常必须是Throwable或其子类的实例,否则编译时会产生语法错误。
2、如何手动抛出异常对象
在方法内部满足指定的条件情况下,使用"throw 异常类的对象"方式抛出!它下面的代码将不会执行
。
理解:在有异常代码处,可以自动抛出,也可以自己手动抛出异常对象!try-catch、throws都是处理异常的方式
手动抛出异常对象
java
public static int max(int... nums){
if(nums == null || nums.length==0){
throw new IllegalArgumentException("没有传入任何整数,无法获取最大值");
}
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}
5、使用注意点
- 如果是编译时异常类型的对象,同样需要使用throws或者try...catch处理,否则编译不通过。
- 如果是运行时异常类型的对象,编译器不提示。
- 可以抛出的异常必须是Throwable或其子类的实例。
- 无论是编译时异常类型的对象,还是运行时异常类型的对象,如果没有被try...catch合理的处理,都会导致程序崩溃
- throw语句会导致程序执行流程被改变,throw语句是明确抛出一个异常对象,因此它
下面的代码将不会执行
- 如果当前方法没有try...catch处理这个异常对象,throw语句就会
代替return语句
提前终止当前方法的执行,并返回一个异常对象给调用者。
2.4 自定义异常
上面提到:如果抛出异常对象必须是异常类的子类(❌throw new String("ssdf")),但是现在想自定义一个怎么办呢?
1、为什么需要自定义异常类
Java中不同的异常类,分别表示着某一种具体的异常情况。那么在开发中总是有些异常情况是核心类库中没有定义好的,此时我们需要根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题,某员工已在团队中等。------可以通过异常名称就能直接判断此异常出现的原因,因此有必要不满指定条件时,通过此异常类判断出具体的异常问题
2、如何自定义异常类
- 继承于现有的异常体系:
java.lang.Exception
、java.lang.RuntimeException
- 通常提供几个重载的构造器:无参构造、(String message)构造器
- 提供一个全局常量:声明为static final long serialVersionUID
3、如何使用自定义异常类
- 在具体代码中,满足指定条件的情况下,只能手动使用"throw 自定义异常类的对象"方式,将异常对象抛出【生成异常对象,手动抛】
- 如果是非运行时异常,必须要处理:try-catch、throws
自定义异常类,并手动抛出异常对象→处理异常
java
//自定义异常类
class MyException extends Exception {
static final long serialVersionUID = 23423423435L;
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
java
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人数为负值,不合理", 3);//手动抛出异常对象
else
System.out.println("登记人数" + num);
}
public void manager() {
try {
regist(100);//执行会产生异常对象的代码,就需要进行异常处理
} catch (MyException e) {
System.out.print("登记失败,出错种类" + e.getId());
}
System.out.print("本次登记操作结束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}
三、异常总结&企业真题
异常处理的5个关键字
1、Java的异常体系简单介绍下&异常的顶级接口是什么&异常类的继承关系
Throwable类,Error:jvm的异常,堆溢出栈溢出
Exception:运行时(数组、空指针)、编译时异常(反射中找不到类、找不到文件、IO)
2、Java异常处理机制
两种处理方案:try-catch-finally ;throws
Java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象
,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。
3、运行时异常与一般异常有何异同?
运行时异常:RuntimeException:编译可以通过。在运行时可能抛出。出现的概率高一些;一般针对于运行时异常,都不处理。
一般异常:Exception:编译不能通过。要求必须在编译之前,考虑异常的处理。不处理编译不通过。
4、说说final、finally、finalize的区别
5、如果不使用try-catch,程序出现异常会如何?
对于当前方法来讲,如果不使用try-catch,则在出现异常对象以后会抛出此对象。如果没有处理方案,就会终止程序的执行。
6、try ... catch捕捉的是什么异常?
Exception。非Error
7、如果执行finally代码块之前方法返回了结果或者jvm退出了,这时finally块中的代码还会执行吗?
特别的:System.exit(0);
8、在try语句中有return语句,最后写finally语句,finally语句中的code会不会执行?何时执行?如果执行是在return前还是后
finally必然会执行,在return之前执行
9、捕获异常在catch块里一定会进入finally吗?catch里能return吗?catch里return还会进finally吗?在try里return是什么情况?
finally可选,catch中的return也会进入finally
10、throw与throws?"上游排污,下游治污"
声明位置:一个声明方法内,后面是异常对象、一个在方法签名处
解决的问题:一个是产生异常对象,一个是用来处理异常对象的。
11、子类重写父类抛出异常的方法,能否抛出比父类更高级别的异常类
不能!多态
12、如何自定义一个异常?
继承java异常类