这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战
异常体系图
Throwable是所有异常的父类,它有错误(Error)和异常(Exception)两个子类,下面对他们进行一个解释。
Error
Error往往是很严重的错误,是程序无法处理的异常,我们没办法通过程序进行捕获,比如内存溢出(OutOfMemoryError)、线程死亡(ThreadDeath),java虚拟机运行错误(Virtual MachineError)等。这些异常发生时, Java虚拟机(JVM)一般会选择线程终止。 这类错误只能我们自己改正回来,举个例子:
import java.util.ArrayList;
import java.util.List;
/*设置java堆的大小为20M -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError*/
public class Demo06 {
static class OOMObject{
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while(true){
list.add(new OOMObject());
}
}
}
运行结果
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid11496.hprof ...
Heap dump file created [28278423 bytes in 0.076 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at com.itsenfeng.staticDemo.Demo06.main(Demo06.java:13)
Process finished with exit code 1
上面我们通过设置java堆的大小限制为20M,然后不断往堆中创建对象,直到java堆内存溢出(OutOfMemoryError)。
Exception
Exception是程序本身可以处理的异常,可以通过catch捕捉。这种异常分两大类运行时异常和非运行时异常(不受检查异常和检查异常)。 程序中应当尽可能去处理这些异常。
运行时异常都是RuntimeException类及其子类异常,常见的异常有:空指针异常(NullPointerException)、下标越界异常(IndexOutOfBoundsException)、算术异常(ArithmaticException)、类型转换异常(ClassCastException) 等,这些异常一般是由程序逻辑错误引起的, 程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过,如IO异常(IOException)、SQL(SQLException)等以及用户自定义的Exception异常。面对这种异常,一般我们选择抛出或者将有这些异常的代码放在try.....catch中处理。
下面举个例子带大家直观体会一下这两种异常:
import java.io.FileNotFoundException;
public class Demo07 {
public static void main(String[] args){
int array[] = {1,9,9,0,7,6};
ddd a = new ddd();
System.out.println(a.arrayout(array, 2));
try {
//此方法抛出的异常属于检查异常,必须处理
a.findFile("f://t.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//运行结果
//9
//f://t.txt
}
}
class ddd{
public void findFile(String s) throws FileNotFoundException{
if(s == null){
/*下面的throw语句抛出的是检查异常,必须进行处理,否则编译不通过
所以在方法的声明中,可以捕捉此异常,也可以throws此异常或异常父类,
throws的话主函数调用该方法时,就要对该抛出的异常进行处理。
*/
throw new FileNotFoundException();
}else{
System.out.println(s);
}
}
public int arrayout(int a[],int index){
if(index > a.length){
/*ArrayIndexOutOfBoundsException异常属于非受检查异常,可以不处理
下面的语句抛出这个异常,我们不必在方法的声明中throws此异常
主函数调用此方法也可以不处理
*/
throw new ArrayIndexOutOfBoundsException();
}
return a[index];
}
}
throw和throws的说明:
1、throws出现在方法的声明中,表示该方法可能会抛出的异常,允许throws后面跟着多个异常类型
2、throw出现在方法体中,用于抛出异常,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw,throw出异常后,也就直接跳出该方法了。throw不写在try....catch里面方法后要加throws
异常处理方式
介绍完这些异常后,我们再来介绍一下该如何去解决他们。
第一、在方法中用try...catch 语句捕获并处理异常,catach 语句可以有多个,用来匹配多个异常。
try {
//可能产生的异常的代码
}catch (异常类型 异常变量) {
//捕获并处理try抛出的异常
//异常类型可以是其以及其父类
}catch (异常类型 异常变量){
//捕获并处理try抛出的异常
//异常类型可以是其以及其父类
}finally{
//无论是出现异常,finally块中的代码都将被执行
//finally可不写
}
try 块: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟⼀个 finally 块。
catch 块: 用于处理 try 捕获到的异常。
finally 块: 无论是否捕获或处理异常, finally 块里的语句都会被执行。当在 try 块或catch 块中遇到 return 语句时, finally 语句块将在方法返回之前被执行。
注意:当 try 语句和 finally 语句中都有 return 语句时,在⽅法返回之前,finally 语句的内容将被执⾏,并且 finally 语句的返回值将会覆盖原始的返回值。
public static int f(int value) {
try {
return value * value;
} finally {
if (value == 2) {
return 0;
}
}
}
//结果返回0
在以下 3 种特殊情况下, finally 块不会被执行:
1.在 try 或 finally 块中用了 System.exit(int) 退出程序。但是,如果System.exit(int)在异常语句之后, finally 还是会被执行
2. 程序所在的线程死亡。
3. 关闭 CPU。
第二、对于处理不了的异常,在方法的声明处通过throws 语句抛出异常
public void highLevelAccess() throws HighLevelException{
try {
................
}catch (Exception e) {
................
}
}
那么这两种方法有什么不同呢????
抛出异常:我们将这种称为渣男(具体方法)行为,遇到问题就把就把女孩子(异常)抛给前任(方法调用者),自己不负责,然后可能前任(方法调用者)也是渣男,又将女孩子(异常)抛给前前任(方法调用者的调用者),如果一直没有对其进行处理,那么最终抛到第一任(main方法)方法,第一任也没办法,第一任(main方法)再抛给jvm虚拟机,jvm虚拟机直接认定其没救了,终止她的生命。
捕获异常:这种行为是负责任的,我们直接将人家(异常)捕获,然后再catch解决掉。 对于捕获异常,当在try中发现异常语句后,异常语句后面的代码将不再执行,这里指的是try里面的代码。 一个try后面可以跟多个catch,但只会执行先匹配到的catch块。
自定义异常
在实际了开发中,jdk所拥有的异常种类是不够我们使用的,为此,我们有时需要自己自定义异常类:
步骤:
1、声明一个自定义异常类
2、将自定义类继承Exception(必须处理)或者RuntimeException(可以不处理)
3、编写两个构造器,一个空的,一个有参数的构造器(根据需要传输错误信息)
下面时自定义异常类的一个实例:
class FuShuException extends Exception
{
private int value;
FuShuException()
{
super();
}
FuShuException(String msg,int value)
{
super(msg);
this.value = value;
}
public int getValue()
{
return value;
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0) {
//手动通过throw关键字抛出一个自定义异常对象。
throw new FuShuException("出现了除数是负数的情况------ / by fushu", b);
}
return a/b;
}
}
public class Demo08
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-9);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());//输出异常发生时的详细信息
System.out.println("错误的负数是:"+e.getValue());
}
System.out.println("over");
/*运行结果
com.itsenfeng.staticDemo.FuShuException: 出现了除数是负数的情况------ / by fushu
错误的负数是:-9
over
*/
}
}
Throwable 类常用方法:
public string getMessage():返回异常发生时的简要描述
public string toString():返回异常发生时的详细信息
public string getLocalizedMessage():返回异常对象的本地化信息。使用Throwable 的⼦类覆盖这个方法,可以生成本地化信息。如果⼦类没有覆盖该方法,则该方法返回的信息与getMessage() 返回的结果相同
public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息
结语:
希望本篇博客对大家有帮助,如有不足与错误,欢迎大家指正!!!