异常

1 java的异常处理机制
1.1 异常在java中以类和对象的形式存在。那么异常的继承结构是怎么样的?
我们可以使用UML图来描述一下结构继承。
画UML图有很多工具,例如:Rational Rose(收费的)、startUML等...
Object
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理的,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常。(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)
RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)

1.2 编译时异常和运行是异常,都是发生在运行阶段。编译阶段异常是不会发生的。
编译时异常因为什么而得名?
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。
所有一掺个都是在运行阶段发生的,因为只有程序运行阶段才可以new对象。
因为异常的发生就是new异常对象。

1.3 编译时异常和运行时异常的区别?
编译时异常一般发生的概率比较高。
运行时异常一般发生的概率比较低。
举个例子:
你看到外面下雨了,倾盆大雨的。
你出门之前会预料到:如果不打伞,我可能会生病(生病是一种异常)。
而且这个异常发生的概率很高,所以我们出门之前要拿一把伞。
“拿一把伞”就是对“生病异常”发生之前的一种处理方式。

对于一些发生概率较高的异常,需要在运行之前对其进行预处理。

运行时异常一般发生的概率比较低。
举个例子:
小明走在大街上,可能会被天上的飞机轮子砸到。
被飞机轮子砸到也算一种异常。
但是这种异常发生概率较低。
再出门之前你没必要提前对这种发生概率较低的异常进行预处理。
如果你预处理这种异常,你将活的很累。

假设你再出门之前,你把能够发生的异常都预先处理,你这个人会更加的安全,但是你这个人活的很累。

假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常。
所有的异常都需要在编写阶段对其进行预处理,将是怎样的效果呢?
首先,如果这样的话,程序肯定是绝对安全的。
但是程序员编写程序太累,代码到处都是处理异常的代码。

1.4 编译时异常还有其他名字:
受检异常:CheckdException
受控异常

1.5 运行时异常还有其他名字:
未受控异常:UnCheckdException
非受控异常。

1.6 再次强调:所有的异常都是发生在运行阶段的。

1.7 java语言中对异常的处理包括两种方式:
第一种方式:在方法生命的位置上,使用throws关键字,抛给上一级。
谁调用我,我就抛给谁。抛给上一级。

第二种方式:使用try..catch语句进行异常的捕捉。
这件事发生了,谁也不知道,因为我给抓住了。

举个例子:
我是某集团的一个销售员,因为我得失误,导致公司损失了1000元,
“损失1000元”这可以看做是一个异常发生了。我有两种处理方式,
第一种方式:我把这件事情告诉我的领导【异常上抛】
第二种方式:我自己掏腰包把这个钱补上。【异常的捕捉】

 

什么是异常,java提供异常机制有什么用?

package com.javase.Exception;
/*
1 什么是异常,java提供异常机制有什么用?
    以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
    java语言是很完善的语言,提供了异常的处理方式,以下程序执行过程中出现了不正常情况,
    java把该异常信息打印输出到控制台,供程序员参考。
    程序员看到以后,可以对程序进行修改,让程序更加的健壮。
    
    什么是异常:程序执行过程中的不正常情况。
    异常的作用:增强程序的健壮性。
    
2 以下程序执行控制台出现了:
    Exception in thread "main" java.lang.ArithmeticException: / by zero
   at com.javae.Exception.ExceptionTest01.main(ExceptionTest01.java:17)
   这个信息被我们称作:异常信息。这个信息是JVM打印的。
*/
public class ExceptionTest01 {
    public static void main(String[] args) {
        
        int a= 10;
        int b = 1;
        // 实际上JVM在执行到此处的时候,会new异常对象。 new ArithmeticException("/ by zero");对象
        // 并且JVM将new的异常对象抛出,打印输出信息到控制台了。
        int c = a / b;
        System.out.println(a +" / "+ b + " = " + c);
        
        // 此处运行也会创建一个:ArithmeticException类型的对象。
        System.out.println(100 / 0);
        
        // 我观察到异常信息之后,对程序进行修改,更加健壮。
        /*int a = 10;
        int b = 0;
        if(b == 0){
            System.out.println("除数不能为0");
            return;
        }
        // 程序执行到此处表示除数一定不是0
        int c = a / b;
        System.out.println(a +" / "+ b + " = " + c);*/
    }
}

 

java语言中异常是以什么形式存在的呢?
package com.javase.Exception;
/*
java语言中异常是以什么形式存在的呢?
    1 异常在java中以类的形式存在,每一个异常都是一个类。
    2 异常对应的现实生活中是怎样的?
        火灾(异常类):
            2008年8月8日小明家着火了(异常对象)
            2008年8月18日小刚家着火了(异常对象)
            2008年8月28日小红家着火了(异常对象)
        类是:模板。
        对象是:实际存在的个体。
        
        钱包丢了(异常类):
            2008年1月8日:小明的钱包丢了(异常对象)
            2008年1月9日:小明的钱包又丢了(异常对象)
            ...
*/
public class ExceptionTest02 {
    public static void main(String[] args) {
        // 通过"异常类"实例化"异常对象"
        NumberFormatException numberFormatException = new NumberFormatException("数字格式化异常");
        
        // java.lang.NumberFormatException: 数字格式化异常
        System.out.println(numberFormatException);
        
        // 通过“异常类”创建“异常对象”
        NullPointerException nullPointerException = new NullPointerException("空指针异常发生了");
        // java.lang.NullPointerException: 空指针异常发生了
        System.out.println(nullPointerException);
    }
}

 

ArithmeticException异常案例:
package com.javase.Exception;


public class ExceptionTest03 {
    public static void main(String[] args) {
        /*
        程序执行到此处发生了ArithmeticException: / by zero异常。
        底层new了一个ArithmeticException异常对象。
        然后抛出了,由于是main方法调用了100/0
        所以这个异常ArithmeticException抛给了main方法
        main方法没有处理,将这个异常抛给了JVM。
        JVM最终终止程序的执行。
        
        ArithmeticException继承了RunTimeException,属于运行时异常。
        在编写程序阶段不需要对这种异常进行预先的处理。
         */
        System.out.println(100 / 0);
        
        // 这里的Hello World没有输出,没有执行。
        System.out.println("Hello World");
    }
}

 

分析代码异常报错原因案例:

package com.javase.Exception;
/*
以下代码报错的原因是什么?
    因为doSome()方法声明位置上使用了throws ClassNotFoundException
    而ClassNotFoundException是编译时异常。必须编写代码时处理,没有处理编译器报错
*/
public class ExceptionTest04 {
    public static void main(String[] args) {
        // 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方法执行了");
    }
}

 

异常的捕捉和上抛:
package com.javase.Exception;


public class ExceptionTest05 {
    // 第一种处理方式:在声明的位置上继续使用: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();
        }
    }
    
    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome方法执行了");
    }
}

 

异常的上报和捕捉:

package com.javase.Exception;


import java.io.FileNotFoundException;
import java.io.FileOutputStream;


/*
处理异常的第一种方式:在方法声明的位置使用throws关键字抛出,谁调用我这个方法,我就抛给谁,抛给调用者处理。
    这种处理异常的态度:上报。
    
处理异常的第二种方式:
    使用try..catch语句对异常进行捕捉。
    这个异常不会上报,自己把这个事处理了。
    异常抛到此处为止,不在上抛了。
    
注意:
    只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
    另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行。
    try..catch执行完成后,该方法后面的java语句会执行。
*/
public class ExceptionTest06 {
    // 一般不建议在main方法上使用throws,因为这个异常如果真正发生了,一定会抛给JVM、JVM只有终止。
    // 异常处理机制的作用就是增强程序的健壮性,怎么能做到、异常发生了也不影响程序的执行。
    // 所以一般main方法中的异常建议使用try..catch进行捕捉,main就不要继续向上抛了。
    public static void main(String[] args) {
        System.out.println("main begin");
        // try尝试
        try {
            m1();
        // 以上代码出现异常,直接进入catch语句块中执行。
        } catch (FileNotFoundException e) { // catch后面的好像一个方法的形参
            // 这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址。
            // catch是捕捉 异常之后走的分支
            // 在catch分支中干什么?处理异常。
            System.out.println("文件不存在,可能路径错误!也可能该文件被删除了。");
            System.out.println(e);// java.io.FileNotFoundException: C:哇哈哈哈哈哈哈哈哈.txt (拒绝访问。)
        }
        // try..catch把异常抓住以后,这里的代码会继续执行。
        System.out.println("main end");
        
    }
    
    public static void m1() throws FileNotFoundException {
        System.out.println("m1 begin");
        m2();
        // 出现异常以下代码不会被执行
        System.out.println("m1 end");
    }
    
    // 抛别的不行,抛ClassCastException说明你还是没有对FileNOtFoundException进行处理
    // 抛FileNotFoundException的父类对象IOException,这样是可以的。因为IOException包括FileNotFoundException
    // private static void m2() throws FileNotFoundException {
    // 这样也可以,因为Exception包括所有的异常。
    // private static void m2() throws Exception {
    // throws后面也可以写多个异常,使用逗号隔开
    // private static void m2() throws ClassNotFoundException,FileNotFoundException{
    private static void m2() throws FileNotFoundException{
        System.out.println("m2 begin");
        m3();
        // 出现异常以下代码不会被执行
        System.out.println("m2 end");
    }
    
    private static void m3() throws FileNotFoundException {
        // 调用SUN jdk中某个类的构造方法
        // 这个类还没有接触过,后期IO流的时候就知道了。
        // 我们只是借助这个类学习一下异常处理机制。
        // 创建一个输入流对象,该流指向一个文件。
        System.out.println("m3 begin");
        /*
        编译报错的原因是什么?
            1 第一:这里调用了一个构造方法:FileInputStream(String name)
            2 第二:这个构造方法的声明位置上有:throws FileNotFoundException
            3 第三:通过类的继承结构看到:FileNotFoundException的父类是IOException,IOException的父类是Exception
            最终得知,FileNotFoundException是编译时异常。
            
            错误原因?编译时异常要求程序员编写程序阶段必须对他进行处理,不处理编译器报错。
            
         */
        // 我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛。
        // new FileOutputStream("C:\\Users\\xlWu\\Desktop\\学习\\异常\\学习.txt");
        new FileOutputStream("C:哇哈哈哈哈哈哈哈哈.txt");
        // 出现异常以下代码不会被执行
        System.out.println("m1 end");
    }
}

 

深入try..catch:
package com.javase.Exception;


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


/*
深入try..catch
    1 catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
    2 catch可以写多个,建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
    3 catch写多个的时候,从上到下,必须遵循从小到大。
    
在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选?
    如果希望调用者来处理,选择throws上报。
    其它情况使用捕捉的方式。
*/
public class ExceptionTest07 {
    /*public static void main(String[] args) throws Exception, FileNotFoundException,NullPointerException,NumberFormatException {
    
    }*/
    
    /*public static void main(String[] args) throws Exception {
    
    }*/
    
    public static void main(String[] args) {
        /*try {
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            System.out.println("出现异常,这里无法执行!");
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        }
    
        System.out.println("hello world!");*/
    
        /*try {
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            System.out.println("出现异常,这里无法执行!");
        *//*} catch (IOException e) {// 多态 IOException e = new FileNotFoundException();*//*
        } catch (Exception e) {// 多态 Exception e = new FileNotFoundException();
            System.out.println("文件不存在!");
        }*/
    
        /*try {
            // 创建输入流
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            // 读文件
            fis.read();
        } catch (Exception e) { // 所有的异常都走这个分支
            System.out.println("文件不存在!");
        }*/
    
        /*try {
            // 创建输入流
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            // 读文件
            fis.read();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        } catch (IOException e) {
            System.out.println("读文件报错了!");
        }*/
        
        // 编译报错  自上而下执行  必须由小到大
        /*try {
            // 创建输入流
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            // 读文件
            fis.read();
        } catch (IOException e) {
            System.out.println("文件不存在!");
        } catch (FileNotFoundException e) {
            System.out.println("读文件报错了!");
        }*/
        
        // JDK8的新特性
        try {
            // 创建输入流
            FileInputStream fis =  new FileInputStream("D:\\Users\\xlWu\\Desktop\\学习\\异常\\异常的继承结构图.uml");
            // 进行数学运算
            System.out.println(10 / 0);// 这个是运行时异常,编写程序时可以处理,也可以不处理。
        } catch (FileNotFoundException | ArithmeticException | NullPointerException e) {
            System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
        }
    }
}

 

 
 
posted @ 2020-07-06 22:11  xlwu丶lz  阅读(147)  评论(0编辑  收藏  举报