'); } '); } java中的异常 | Journey to paradise

java中的异常


java中的异常

示例一

要点

1、什么是异常,java提供异常处理机制有什么用?

1)程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
2)java语言是很完善的语言,提供了异常的处理方式:
    JVM把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮。

异常的作用:增强程序的健壮性。

2、java语言中异常是以什么形式存在的呢?

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

3、异常对应的现实生活中是怎样的?

    火灾(异常类):
        2008年8月8日,小明家着火了(异常对象)
        2008年8月9日,小刚家着火了(异常对象)
        2008年9月8日,小红家着火了(异常对象)
    类是:模板。
    对象是:实际存在的个体。

实例

public static void main(String[] args){
    int a = 10;
    int b = 0;
    //实际上JVM在执行到此处的时候,会new异常对象:new ArithmeticException("/ by zero" );
    //并且JVM将new的异常对象抛出,打印输出信息到控制台了。
    int c = a / b;
    System.out.println(a + "/"+ b +"=" + c);

    //我观察到异常信息之后,对程序进行修改,更加健壮。
    /*
    int a = 10;
    int b = 2;
    if(b == 0){
        System.out.println("除数不能为0");
        return;
    }
    //程序执行到此处表示除数一定不是0
    int c = a / b;
    System.out.println(a + "/"+ b + "=" + c);
    */

    //通过“异常类”实例化“异常对象”
    NumberFormatException nfe = new NumberFormatException("数字格式化异常!");
    //java.Lang.NumberFormatException:数字格式化异常!
    System.out.println(nfe);
    //通过“异常类”创建异常对象
    NullPointerException npe = new NullPointerException("空指针异常发生了!");
    //java.Lang.NullPointerException:空指针异常发生了!
    System.out.println(npe);
    }

示例二

要点

1、异常的继承结构
Object
Object下有Throwable(可抛出的)
Throwable下有两个分支: 
Error(不可处理,直接退出JVM)和Exception(可处理的)
    Exception下有两个分支:
    Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进处理)
    RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管)

2、编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。

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

4、编译时异常和运行时异常的区别?
    编译时异常发生概率较高需要在运行之前进行预处理,运行时异常发生概率较低不需要在运行之前进行预处理。

5、编译时异常还有其他名字:  受检异常(CheckedException)、受控异常
    运行时异常还有其它名字:  未受检异常(UnCheckedException)、非受控异常

6、Java语言中对异常的处理包括两种方式:
    第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级(调用者)。
         异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。

    第二种方式:使用try..catch语句进行异常的捕捉并处理。
 

实例

public class ExceptionTest02 {
        public static void main(String[] args) {
        // 第一种处理方式:在方法声明的位置上继续使用: 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();
            }
        }  

        // main方法中调用doSome()方法
        //因为doSome()方法声明位置上有: throws ClassNotFoundException
        //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
        //如果不处理,编译器就报错。
        //编译器报错信息: Unhandled exception: java.lang.ClassNotFoundException
        // doSome();
    }
    /**
    * doSome 方法在方法声明的位置上使用了: throws ClassNotFoundException
    *这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。
    *这个异常直接父类是: Exception,所以ClassNotFoundException属于编译时异常。
    * @throws ClassNotFoundException
    */
    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!");
}

示例三(try...catch、throws)

要点

    1、只要异常没有捕捉,采用抛出异常的方式,此方法的后续代码不会执行
        另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行
    2、throws 后面也可以写多个异常,使用逗号隔开
    3、可以直接抛出该抛出异常类对象的父对象异常类对象

    4、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
       catch可以写多个。建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
       catch写多个的时候,从上到下,必须遵守从小到大。
    

实例

public class ExceptionTest03 {
        //一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM,JVM只有终止。
        //异常处理机制的作用就是增强程序的健壮性,做到异常发生也不影响程序的执行。
        // 一般main方法中的异常建议使用try...catch进行捕捉,main就不要继续上抛了。
        /*
        public static void main(String[] args) throws FileNotFoundException {
            System.out.println( "main begin");
            m1();
            System.out.println( "main over");
        }
        */

        public static void main(String[] args) {
            // 100/e 这是算术异常,这个异常是运行时异常,你在编译阶段,可以处理,也可以不处理,编译器不管。
            //System.out.println(100/0); //不处理编译器也不管
            //你处理也可以
            /*
            try {
                System.out.println(100/0);
            } catch(ArithmeticException e){
                System.out.println("算术异常了! ! ! !");
            */

            System.out.println("main begin");
            try {
                //try尝试
                m1();
                //以上代码出现异常,直接进catch语句块中执行。
                System.out.println("hello world!") ;
            } catch (FileNotFoundException e){ //catch后面的好像一个方法的形参。
                //这个分支中可以使用e引用, e引用保存的内存地址是那个new出来异常对象的内存地址。
                //catch是捕捉异常之后走的分支。
                //在catch分支中于什么?处理异常。
                System.out.println("文件不存在,可能路径错误,也可能该文件被删除了! ");
            } catch(IOException e){
                System.out.println("读文件报错了!");
            }
                //try...catch处理完异常后,这里的代码会继续执行
                System.out.println("main over");
            }

            private static void m1() throws FileNotFoundException {
                System.out.print1n("m1 begin");
                m2();
                //以上代码出异常,这里是无法执行的。
                System.out.println("m1 over") ;
            }

            //抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理
            //private static void m2() throws ClassCastException{
            //抛FileNotFoundException的父对象I0Exception,这样是可以的,因为IOException包括FileNotfoundException
            //private static void m2() throws IOException {
            //这样也可以,因为Exception包括所有的异常。
            //private static void m2() throws Exception{
            //throws 后面也可以写多个异常,可以使用逗号隔开。
            //private static void m2() throws ClassCastException, FileNotFoundException{
            
            private static void m2() throws FileNotFoundException {
                System.out.println("m2 begin");
                //编译器报错原因是:m3()方法声明位置上有: throws FileNotFoundException
                //我们在这里调用m3()没有对异常进行预处理,所以编译报错。

                m3();
                //以上如果出现异常,这里是无法执行的!
                System.out.println("m2 over"); 
            }

            private static void m3() throws FileNotFoundException {
                //调用SUN jdk中某个类的构造方法。
                //创建一个输入流对象,该流指向一个文件。
                /*
                编译报错的原因是什么?
                    第一:这里调用了一个构造方法:FileInputStream(String name)
                    第二:这个构造方法的声明位置上有: throws FileNotFoundException
                    第三:通过类的继承结构看到:FileNotFoundException父类是I0Exception,IOException的父类是Exception
                    最终得知,FileNotFoundException是编译时异常。
                    错误原因?编译时异常要求程序员编写程序阶段必须对它进行处理,不处理编译器就报错。
                */

                new FileInputStream( "D: \\course\\\\javaSE.txt");
                System.out.println("如果以上代码出异常,这里会执行吗????????????????不会!!! ");
            }
        }

示例四(getMessage()、printStackTrace())

要点

1、异常对象有两个非常重要的方法:
    获取异常简单的描述信息:
    String msg = exception.getMessage(); 

    打印异常追踪的堆栈信息:
    exception.printStackTrace();(一般都是使用它)

2、我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢?
    异常信息追踪信息,从上往下一行一行看。
    但是需要注意的是: SUN写的代码就不用看了(看包名就知道是自己的还是SUN的)。
    主要的问题是出现在自己编写的代码上。

实例

pub1ic class ExceptionTest04 {
    public static void main(String[] args) {
    //这里只是为了测试getMessage()方法和printStackTrace()方法。
    //这里只是new了异常对象,但是没有将异常对象抛出,JVM会认为这是一个普通的java对象。
    NullPointerException e = new NullPointerException( "空指针异常fdsafdsafdsafds");
    
    //获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。
    String msg = e.getMessage(); // 空指针异常fdsafdsafdsafds
    System.out.println(msg);
    
    //打印异常堆栈信息
    // java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
    e.printStackTrace();

    try {
        m1();
        } catch (FileNotFoundException e) {
        //打印异常堆栈追踪信息! ! !
        //在实际的开发中,建议使用这个。养成好习惯!
        //e.printStackTrace(); 

        /*
        java.io.FileNotFoundException: C:\jetns-agent.jar(系统找不到指定的文件。)
        at java.base/java.io.FileInputStream.open(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
        at java.base/java.io.FilenputStream.<init>(FileInputStream.java:155)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110)
        at com.bjpowernode.javase.exception.ExceptionTest09.m3(Except ionTest09.java:31)
        at com.bjpowernode.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:27)
        at com.bjpowernode.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:23)
        at com. bipowernode.javase.exception.ExceptionTest09.main(ExceptionTest09.java:14)
        因为31行出问题导致了27行
        27行出问题导致23行
        23行出问题导致14行。
        应该先查看31行的代码。31行是代码错误的根源。
        */
    }

    //这里程序继续执行,程序try...catch处理了异常,很健壮,服务器不会因为遇到异常而宕机。
    System.out.println("He11o Wor1d!");
}

    private static void m1() throws FileNotFoundException {
            m2();
        }
    private static void m2() throws FileNotFoundException {
        m3();
    }
    private static void m3() throws FileNotFoundException {
        new FileInputStream(name: "C:\\jetns-agent.jar");
    }
}

示例五(finally)

要点

关try.. catch中的finally子句:
1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
    finally子句必须和try一起出现,不能单独编写。
2、finally语句通常使用在哪些情况下呢?
    通常在finally语句块中完成资源的释放/关闭。因为finally中的代码比较有保障。
    即使try语句块中的代码出现异常, finally中代码也会正常执行,除非退出JVM虚拟机。
3、try finally,没有catch可以吗?可以。
    try不能单独使用。
    try finally可以联合使用。
    finally中的代码一般会执行,除非退出JVM虚拟机,return语句最后执行,return语句执行方法必然结束。
4、System.exit(0) 退出JVM虚拟机,程序执行到此就结束了。

实例

public class ExceptionTest05 {
    public static void main(String[] args) {
        FileInputStream fis = null; //声明位置放到try外面,这样在finally中才能用。
        try {
            //创建输入流对象
            fis = new FileInputStream(name: "D:\\course\\02-JavaSE\\document");
            //开始读文件....

            String s = null; .
            //这里一定会出现空指针异常!
            s.toString();
            System.out.println("he11o world!");

            //流使用完需要关闭,因为流是占用资源的。
            //即使以上程序出现异常,流也必须要关闭!
            //放在这里有可能流关不了。
            //fis.close();
        } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch(IOException e){
                e.printStackTrace();
            } catch(NullPointerException e) {
                e.printStackTrace();
            } finally {
            System.out.println("hello浩克! ");
            //流的关闭放在这里比较保险。
            // finally中的代码是一定会执行的。
            //即使try中出现了异常!
            if (fis != null) { //避免空指针异常!
                try {
                // close()方法有异常,采用捕捉的方式。
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            }
                System.out.println("hello kitty!");
    }
} 

实例

public class ExceptionTest06 {
    public static void main(String[] args) {
        /* 
        以下代码的执行顺序:
        先执行try...
        再执行finally...
        最后执行return(return语句只要执行方法必然结束。)

        try {
            System.out.println("try...");
            return;
        } finally {
            // finally中的语句会执行。能执行到。
            System.out.println("finally...");
        }
        
        //这里不能写语句,因为这个代码是无法执行到的。
        //System.out.println( "Hello world!");
}    

finally面试题

程序输出结果

/* 以下程序输出result是多少??? */ public class ExceptionTest07 { public static void main(String[] args) { int result = m(); System.out.println(result); //100 }
        /*
        java语法规则(有一些规则是不能破坏的):
        java中有两条这样的规则:
            方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法! )
            return语句一旦执行,整个方法必须结束(亘古不变的语法! )
        */
        public static int m(){
            int i = 100;
            try {
                // 这行代码出现在int i = 100;的下面, 所以最终结果必须是返回100
                // return语句还必须保证是最后执行的,一旦执行,整个方法结束。
                return i;
            } finally {
                i++;
            }
        }
    }
    /*
    反编译之后的效果
    public static int m(){
        int i = 100;
        int j=i;
        i++;
        return j; 
    }
*/

final、finally、finalize有什么区别??

final finally finalize有什么区别?
    final关键宇
        final修饰的类无法继承
        final修饰的方法无法覆盖
        final修饰的变量不能重新赋值。
    finally关键宇
        和try一起联合使用。
        finally语句块中的代码是必须执行的。
    finalize标识符
        是一个object类中的方法名。
        这个方法是由垃圾回收器GC负责调用的。

自定义异常

要点

1. SUN提供的JDK内置的异常是不够用的。在实际的开发中,很多业务出现异常之后,JDK中没有相应的异常来处理。
    所以需要程序员自定义异常。
2. Java中怎么自定义异常呢?
    第一步:编写一个类继承Exception或RuntimeException.
    第二步:提供两个构造方法,一个无参数的,一个带有String参数的。

实例

//自定义栈操作异常
public class StackOperationException extends Exception{
    public StackOperationException(){}
    public StackOperationException(String msg){
        super(msg);
    }
}

//栈:后进先出
public class Stack{
    
    //使用数组存储数据
    //栈可以存储多个引用类型的元素
    Object[] elements;
    
    //指向栈顶元素上方的一个帧.
    int index;
    
    //栈默认的初始化容量是5
    //Constructor
    Stack(){
        this(5);
    }
    
    Stack(int max){
        elements = new Object[max];
    }
    
    //栈应该对外提供一个压栈的方法
    public void push(Object element) throws StackOperationException{
        /*
        elements[index] = element;
        index++;
        */
        if(index==elements.length){
            //异常
            throw new StackOperationException("栈已满!");
        }
        
        elements[index++] = element;
    }
    
    //栈应该对外提供一个弹栈的方法
    public Object pop() throws StackOperationException{ //栈顶的元素往外弹
        /*
        index--;
        return elements[index];
        */
        if(index==0){
            throw new StackOperationException("栈已空!");
        }
        return elements[--index];
    }
    
}

异常与方法覆盖

重写之后的方法不能比重写之前的方法抛出更多或是更宽泛的异常,可以更少

class Animal {
    public void doSome(){
}
    public void doOther() throws Exception{
}

class Cat extends Animal {        
    //编译正常。
    public void doSome() throws RuntimeException{
}
    
    /*
    //编译报错。
    public void doSome() throws Exception{
    }
    //编译正常。
    public void doOther() {
    }
    //编泽正常。
    public void doOther() throws Exception{
    }
    */
    
    //编译正常。
    public void doOther() throws NullPointerException{
    }
}

    

文章作者: 涂爽
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 涂爽 !
评论
 上一篇
java中集合(Collection部分) java中集合(Collection部分)
java中集合(Collection部分) 集合的继承结构 要点 集合在java中是一个容器,一个对象。集合不能直接存储基本数据类型,也不能直接存储java对象, 集合当中存储的都是java
2021-10-18
下一篇 
java中常用类 java中常用类
java中常用类 日期类 示例一 public class DateTest01 { public static void main(String[] args)
2021-10-11
  目录