java中的异常

异常的基本概念
异常的分类
getMessage和printstackTrace
finally关键字
final、finalize和finally
如何自定义异常及使用

异常的基本概念
  • 异常概述
    1、什么是异常?
    程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。
    2、java提供异常处理机制有什么用?
    java语言是很完善的语言,提供了异常的处理方式。程序执行过程中出现了不正常情况,java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮。
    3、java语言中异常是以什么形式存在的呢?
    异常在java中以类的形式存在,每一个异常类都可以创建异常对象。如:
NumberFormatException nfe = new NumberFormatException("数字格式化异常");
        System.out.println(nfe);

4、编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错。所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。异常的发生就是new异常对象。

异常的分类
  • 编译时异常和运行时异常的区别
    1、编译时异常一般发生的概率比较高。运行时异常一般发生的概率比较低。
    2、对于一些发生概率较高的异常,需要在运行之前对其进行预处理。
    3、编译时异常还有其他名字:
    受检异常:CheckedException
    受控异常
    运行时异常还有其它名字:
    未受检异常:UnCheckedException
    非受控异制
  • 异常的两种处理方式
    1、
    第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
    第二种方式:使用try...catch语句进行异常的捕捉。
    2、例子:
    某集团的一个销售员,因为失误,导致公司损失了1000元。
    “损失1000元"这可以看做是一个异常发生。
    有两种处理方式。第一种方式:把这件事告诉领导(异常上抛)第二种方式:自己掏腰包把这个钱补上。(异常的捕捉)
    3、注意:
    Java中异常发生之后如果一直上抛,最终抛给了main方法, main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止Java程序的执行。
  • 运行时异常编译时可以不处理
    代码示例:
public class Demo{
    public static void main(String[] args){
        /*程序执行到此处发生了ArithmeticException异常
        底层new了—个ArithmeticException异常对象,然后抛出了。
        由于是main方法调用了10/0,
        所以这个异常ArithmeticException抛给了main方法。
        main方法没有处理,将这个异常自动抛给了JVM。
        JVM最终终止程序的执行。
        ArithmeticException继承RuntimeException,
        属于运行时异常。在编写程序阶段不需要对这种异常进行预先的处理。*/
        System.out.println(10/0);
        /*这里的hello world没有输出,没有执行。*/
        System.out.println("hello world!");
    }
}
  • 方法声明位置上使用throws
    1、代码示例:
public class Demo{
    public static void main(String[] args){
        /**
         * main方法中调用doSome()方法。
         * 因为doSome()方法声明位置上有:
         * throws ClassNotFoundException,
         * 我们在调用doSome()方法的时候必须
         * 对这种异常进行预先的处理。
         * 如果不处理,编译器就报错。
         * 编译器报错信息:
         * Unhandled exception: java.Lang.CLassNotFoundException
         * 报错原因:
         * 因为doSome()方法声明位置上使用了:
         * throws ClassNotFoundException
         * 而CLassNotFoundException是编译时异常。
         * */
        doSome();//报错
    }
    /**
    * doSome方法在方法声明的位置上使用了:
    * throws classNotFoundException。
    * 这个代码表示doSome()方法在执行过程中,
    * 有可能会出现CLassNotFoundException异常。
    * 这个异常直接父类是:Exception,
    * 所以CLassNotFoundException属于编译时异常。
    * @throws ClassNotFoundException
    */
    public static void doSome() throws ClassNotFoundException{
        System.out.println(111);
    }
}

2、处理
第一种处理方式:
在方法声明的位置上继续使用:throws来完成异常的继续上抛。抛给调用者。
(上抛类似于推卸责任,继续把异常传递给调用者)

public static void main(String[] args) throws ClassNotFoundException {
        doSome();
    }

第二种处理方式:
try...catch进行捕捉
(捕捉等于把异常拦下了,异常真正的解决了。调用者是不知道的。)

public static void main(String[] args){
        try {
            doSome();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
  • 异常捕捉和上报的联合使用
    1、报错代码示例:
import java.io.FileInputStream;

public class Demo{
    public static void main(String[] args){
        System.out.println("main begin");
        m1();
        System.out.println("main over");
    }

    public static void m1(){
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }

    public static void m2(){
        /*
        * 编译报错的原因是什么?
        * 第一:这里调用了一个构造方法:
        * FileInputstream(String name)
        * 第二:这个构造方法的声明位置上有:
        * throws FileNotFoundException
        * 第三:通过类的继承结构看到:
        * FileNotFoundException父类是TOException,
        * IOException的父类是Exception
        * 最终得知,FileNotFoundException是编泽时异常。
        * 所以编译时异常要求程序员编写程序阶段必须对它进行处理,
        * 不处理编译器就会报错。
        * */
        new FileInputStream("D:\111.txt");
    }


}

2、第一种处理方式:在方法声明的位置上使用throws关键字抛出,谁调用这个方法,就抛给谁。抛给调用者来处理。
代码示例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo{
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("main begin");
        m1();
        System.out.println("main over");
    }

    public static void m1() throws FileNotFoundException {
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }

    /*
    * 只能抛本身或者其父类,抛其它的没用。
    * throws后面也可以写多个异常,可以使用逗号隔开。
    * */
    public static void m2() throws FileNotFoundException {
        new FileInputStream("D:\111.txt");
    }

}

一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会地给JVM,JVM只有终止。异常处理机制的作用就是增强程序的健壮性,要做到异常发生了也不影响程序的执行。所以一般main方法中的异常建议使用try...catch进行捕捉。main就不要继续上抛了。
3、第二种处理方式:使用try...catch语句对异常进行捕捉。

try {
    //try尝试
    //出现异常直接进入catch语句块
} catch (异常名 变量名) {
    //catch是捕捉异常之后走的分支。
}

代码示例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo{
    public static void main(String[] args){
        System.out.println("main begin");
        try {
            m1();
        } catch (FileNotFoundException e) {
            //e.printStackTrace();
            System.out.println("文件不存在!");
        }
        System.out.println("main over");
    }

    public static void m1() throws FileNotFoundException {
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }

    public static void m2() throws FileNotFoundException {
        new FileInputStream("D:\111.txt");
    }

}

输出:
在这里插入图片描述
或:

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo{
    public static void main(String[] args){
        System.out.println("main begin");
        m1();
        System.out.println("main over");
    }

    public static void m1(){
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }

    public static void m2(){
        try {
            new FileInputStream("D:\111.txt");
        } catch (FileNotFoundException e) {
            //e.printStackTrace();
            System.out.println("文件不存在!");
        }
    }

}

输出:
在这里插入图片描述

  • 深入try...catch
    1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
    2、catch可以写多个。建议catch的时候,精确地一个一个处理。这样有利于程序的调试。
    3、catch写多个的时候,从上到下,必须遵守从小到大。
    4、java8新特性
    代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo{
    public static void main(String[] args){
        try {
            FileInputStream fis = new FileInputStream("D:\111.txt");
            System.out.println(100 / 0);
        } catch (FileNotFoundException | ArithmeticException e) {
            //e.printStackTrace();
            System.out.println("文件不存在?数学异常?都有可能!");
        }
    }
}
  • 处理编译异常时,如何选择上报还是捕捉?
    如果希望调用者来处理,选择throws上报。
    其它情况使用捕捉的方式。
getMessage和printstackTrace
  • 异常对象的常用方法
    1、获取异常简单的描述信息:
    string msg = exception.getMessage();
    2、取得异常的堆栈信息(比较适合于程序调试阶段):
    exception.printstackTrace();
    (一般使用这种,try...catch里就是这种)
    代码示例:
public class Demo{
    public static void main(String[] args){
        NullPointerException n = new NullPointerException("空指针异常");
        String msg = n.getMessage();
        System.out.println(msg);
        n.printStackTrace();
    }
}

输出:
在这里插入图片描述

finally关键字
  • 关于try..catch中的finally子句
    1、finally子句中的代码是最后执行的,并且是一定会执行的。(即使try语句块中的代码出现了异常)
    finally子句必须和try一起出现,不能单独编写。
    2、finally语句通常使用在哪些情况下呢?
    通常在finally语句块中完成资源的释放/关闭。
    因为finally中的代码比较有保障。
    即使try语句块中的代码出现异常,finally 中代码也会正常执行。
    3、代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        FileInputStream fis = null;
        try {
            //创建输入流对象
            fis = new FileInputStream("D:\111.txt");
            //这里一定会出现空指针异常
            String s = null;
            s.toString();
            /*
            流使用完需要关闭,因为流是占用资源的。
            但是上面的程序出现异常,导致不能关闭流。
            */
            //fis.close();
            System.out.println(111);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NullPointerException e){
            e.printStackTrace();
        }
        finally {
            System.out.println(222);
            /*
            * 流的关闭放在这里比较保险。
            * 即使try中出现了异常。
            * finally中的代码是一定会执行的。
            * */
            if (fis != null){
                try {
                    //close()方法有异常,采用捕捉的方式。
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(333);
    }
}

输出:
在这里插入图片描述

  • 退出JVM,finally语句不执行
    代码示例:
public class DemoTest{
    public static void main(String[] args) {
        /*
        * 没有catch,
        * 只有try和finally也可以。
        * 以下代码的执行顺序:
        * 先执行try...
        * 再执行finally...
        * 最后执行return(return语句只要执行方法必然结束)
        * */
        try{
            System.out.println("try...");
            return;
            /*只有退出JVM,finally语句中的代码才不会执行。
            * System.exit(0);
            * */
        }finally {
            System.out.println("finally...");
        }
    }
}

输出:
在这里插入图片描述
将return替换为:

System.exit(0);

输出:
在这里插入图片描述

  • 关于finally的一道题
    代码示例:
public class Demo{
    public static void main(String[] args){
       int n = m();
        System.out.println(n);
    }
    public static int m(){
        int i = 100;
        try {
            return i;
        }finally {
            i++;
        }
    }
}

输出:
在这里插入图片描述
因为:

/*
反编译后的代码为:
*/
public static int m() {
        byte i = 100;

        byte var1;
        try {
            var1 = i;
        } finally {
            int i = i + 1;
            System.out.println(i);
        }

        return var1;
    }
final、finalize和finally
  • 代码示例
public class Demo{
    public static void main(String[] args){
        /*final是一个关键字,表示最终的。不变的。*/
        final int i = 10;
        /*
        finally也是一个关键字,和ry联合使用。
        使用在异常处理机制中,
        在finally语句块中的代码是一定会执行的。
        */
        try {

        }finally {
            System.out.println("finally...");
        }
        /*
        finalize()是object类中的一个方法。
        作为方法名出现,所以finalize是标识符。
        */
    }
}
如何自定义异常及使用
  • 自定义异常
    第一步:编写一个类继承Exception或者RuntimeException。
    第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
  • 继承Exception或者RuntimeException
    继承Exception:(编译时异常)
/*编译时异常*/
public class MyException extends Exception{
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

继承RuntimeException:(运行时异常)

/*运行时异常*/
public class MyExcption extends RuntimeException{
    public MyExcption() {
    }

    public MyExcption(String message) {
        super(message);
    }
}
  • 代码示例:
/*编译时异常*/
public class MyException extends Exception{
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}
public class MyExceptionTest {
    public static void main(String[] args) {
        //创建异常对象(只new了异常对象,并没有手动抛出)
        MyException e = new MyException("用户名不能为空!");
        //打印异常堆栈信息
        e.printStackTrace();
        //获取异常简单描述信息
        String msg = e.getMessage();
        System.out.println(msg);
    }
}

输出:
在这里插入图片描述

  • 异常在实际开发中的作用
    代码示例:
public class MyException extends Exception{//编译时异常
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}
public class Demo{
    public void Demo(int x) throws MyException {
        if(x < 0){
            /*改良前*/
            //System.out.println("请输入大于0的数!");
            //return;
            /*改良后*/
            throw new MyException("请输入大于0的数!");
        } else {
            System.out.println(x);
        }
    }
}
public class DemoTest{
    public static void main(String[] args) {
        Demo demo = new Demo();
        try {
            demo.Demo(1);
            demo.Demo(2);
            demo.Demo(-3);
            demo.Demo(4);
        } catch (MyException e) {
            //e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

输出:
在这里插入图片描述

  • 重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,但是可以更少。
原文地址:https://www.cnblogs.com/yu011/p/12716255.html