Javase——异常

异常

一、异常概述
  1. 什么是异常?

    程序执行过程中的不正常情况。

  2. 异常机制有什么用?

    增强程序的健壮性。


二、Java中异常以类和对象形式存在
package exception;

public class ExceptionTest01 {
    public static void main(String[] args) {
        //通过“异常类”实例化“异常对象”
        NumberFormatException n = new NumberFormatException("数字格式化异常");
        System.out.println(n);
    }
}
  1. 异常在java中以类的形式存在,每一个异常类都可以创建异常对象。实际上,JVM在执行到异常处会new异常对象,并且JVM将new的异常对象抛出打印输出信息到控制台。

  2. 异常对应的现实生活中是怎样的?

    火灾(异常类):

    xxxx年xx月xx日 A家发生火灾(异常对象)

    xxxx年xx月xx日 B家发生火灾(异常对象)

    xxxx年xx月xx日 C家发生火灾(异常对象)


三、java异常处理机制
  1. 异常的继承结构。

    我们可以使用UML图来描述一下继承结构。(UML是一种建模语言,画UML的工具有例如:Rational Rose,starUML……)

    Object

    Object下有Throwable(可抛出的)

    Throwable下有两个分支:Error(不可处理的,直接退出JVM)和Excepion(可处理的)

    Excepion下有两个分支:

    ​ Excepion的直接子类:编译时异常。(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错, 因此得名编译时异常。)

    ​ 编译时异常又可以称为受检异常:CheckedException 受控异常

    ​ RuntimeExcepion:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)

    ​ 运行时异常又可以称为未受检异常:UnCheckedException 非受控异常

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

    所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。因为异常的发生就是new异常对象。

  3. 编译时异常和运行时异常的区别

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

    运行时异常一般发生的概率比较低。

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

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

    第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。谁调用我,我就抛给谁。抛给上一级。

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

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

    **注意:**异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行。

    只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行,try…catch捕捉异常之后,后续代码可以执行。

  5. try catch的深入

    (1)catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类。

    (2)catch可以写多个。建议catch的时候,精确地一个一个处理。这样有利于程序的调试。

    (3)catch写多个的时候,从上到下,必须遵守从小到大。

package exception;

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

public class ExceptionTest03 {
    public static void main(String[] args) throws Exception, FileNotFoundException, NullPointerException {
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("D:\\Java\\007-API文档");
            //读文件
            fis.read();
            }catch(FileNotFoundException e) {
            System.out.println("文件不存在!");
            } catch(IOException e){
            System.out.println("读文件报错了!");
        }

        // 编译报错。
        /*
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("D:\Java\007-API文档");
            //读文件
            fis.read();
            } catch(IOException e){
            System.out.println("读文件报错了!");
            } catch(FileNotFoundException e) {
            System.out.println("文件不存在!");
            }*/
    }
}
  1. JDK8新特性

    package exception;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class ExceptionTest04 {
        public static void main(String[] args) {
            try {
                //创建输入流
                FileInputStream fis = new FileInputStream("D:\\Java\\007-API文档");
                // 进行数学运算
                System.out.println(100 / 0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
            } catch(FileNotFoundException | ArithmeticException | NullPointerException e) {
                System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
            }
        }
    }
    
    1. 上报和捕捉怎么选择

      如果希望调用者来处理,选择throws。其它情况使用try…catch

    2. 异常对象的常用方法getMessage和printStackTrace();

      (1)String msg = exception.getMessage() 获取异常简单的描述信

      (2)exception.printStackTrace(); 打印异常追踪的堆栈信息

      package exception;
      
      public class ExceptionTest05 {
          public static void main(String[] args) {
              NullPointerException e = new NullPointerException("空指针异常");
              System.out.println(e.getMessage());
              e.printStackTrace();
              //这行代码会执行 java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的
              System.out.println("Hello world!");
          }
      }
      

      查看异常的追踪信息,从上往下一行一行看,sun公司不用看,看自己写的代码就行。


四、finally
  1. 在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。

    finally子句必须和try一起出现,不能单独编写。

  2. finally语句通常在finally语句块中完成资源的释放/关闭。因为finally中的代码比较有保障。

    package exception;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class ExceptionTest06 {
        public static void main(String[] args) {
            FileInputStream fis = null;
            try {
                //创建输入流对象
                 fis = new FileInputStream("D:\\Java\\007-API文档");
                //开始读文件
                String s = null;
                //这里一定会出现空指针异常
                s.toString();
                //流使用完需要关闭,因为流是占用资源的
                //即使以上程序出现异常,流也必须关闭,在这close也许关不掉,所以不能在这里关闭
                //fis.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException e){
                e.printStackTrace();
            } finally{
                //流的关闭放在这里比较保险
                if(fis != null){//fis 前面也许new对象不成功 避免空指针异常
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  3. try和finally没有catch也可以, try不能单独使用。
    try finally可以联合使用。

    package exception;
    
    public class ExceptionTest07 {
        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!");
        }
    }
    
  4. 退出JVM finally语句不执行

    package exception;
    
    public class ExceptionTest08 {
        public static void main(String[] args) {
            try {
                System.out.println("try...");
                // 退出JVM
                System.exit(0); // 退出JVM之后,finally语句中的代码就不执行了!
            } finally {
                System.out.println("finally...");
            }
        }
    }
    
  5. finally面试题

    package exception;
    
    public class ExceptionTest09 {
        public static void main(String[] args) {
           int result = m();
            System.out.println(result);//思考此时输出结果i的值是多少?
        }
        public static int m(){
            int i = 100;
            try{
                return i;
            }finally {
                i++;
            }
        }
    }
    

    以上输出结果是100。

    java语法规则:

    java中有一条这样的规则:
    方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
    java中还有一条语法规则:
    return语句一旦执行,整个方法必须结束(亘古不变的语法!)

    return i出现在int i = 100后面,所以最终结果必须返回100.

    //反编译之后的效果
    public static int m(){
        int i = 100;
        int j = i;
        i++;
        return j;
    }
    
  6. final finally finalize的区别

    final 关键字
    final修饰的类无法继承
    final修饰的方法无法覆盖
    final修饰的变量不能重新赋值。

    finally 关键字
    和try一起联合使用。
    finally语句块中的代码是必须执行的。

    finalize 标识符
    是一个Object类中的方法名。
    这个方法是由垃圾回收器GC负责调用的。

  7. 自定义异常

    步骤:

    第一步:编写一个类继承Exception或者RuntimeException。

    第二步:提供两个构造方法,一个无参数的,一个带有String参数的。

    package exception;
    
    public class ExceptionTest10 {
        public static void main(String[] args) {
            //只是new了异常对象,并没有手动抛出
            MyException e = new MyException("用户名为空异常!");
            //打印异常堆栈信息
            e.printStackTrace();
            //获取异常简单描述信息
            System.out.println(e.getMessage());
        }
    }
    
  8. 异常在实际开发中的作用

    改良之前的代码

    package exception;
    
    public class MyStackOperationException extends Exception{
        public MyStackOperationException() {
        }
    
        public MyStackOperationException(String s) {
            super(s);
        }
    }
    
    package exception;
    
    public class MyStack {
        private Object[] elements;
        private int index;
    
        public MyStack() {
            this.elements = new Object[10];
            this.index = -1;
        }
    
        /**
         * 压栈的方法
         * @param obj 被压入的元素
         */
        public void push(Object obj) throws MyStackOperationException {
            if(index >= elements.length - 1){
                throw new MyStackOperationException("压栈失败,栈已满!");
                //在这里不用捕捉的方式解决异常,异常是我们自己抛的,又自己捕捉没有意义,而且这个异常的信息我们需要传递出去让外界知道。
            }
    
            index++;
            elements[index] = obj;
    
            System.out.println("压栈" + obj + "元素成功,栈帧指向" + index);
        }
    
        /**
         * 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。
         * @return
         */
        public void pop() throws MyStackOperationException {
            if(index < 0){
                throw new MyStackOperationException("弹栈失败,栈已空!");
            }
    
            System.out.print("弹栈" + elements[index] + "元素成功,");
    
            index--;
            System.out.println("栈帧指向" + index);
        }
    
    
        public Object[] getElements() {
            return elements;
        }
    
        public void setElements(Object[] elements) {
            this.elements = elements;
        }
    
        public int getIndex() {
            return index;
        }
    
        public void setIndex(int index) {
            this.index = index;
        }
    }
    
    package exception;
    
    public class MyStackExceptionTest {
        public static void main(String[] args) {
            //创建栈对象
            MyStack stack = new MyStack();
            //压栈
            try {
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
            } catch (MyStackOperationException e) {
                System.out.println(e.getMessage());
            }
            //弹栈
            try {
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
            } catch (MyStackOperationException e) {
                System.out.println(e.getMessage());
            }
        }
    }
    
  9. 异常与方法覆盖

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

  10. 异常相关测试题

    编写程序模拟用户注册:
    ①程序开始执行时,提示用户输入“用户名”和“密码”信息。
    ②输入信息之后,后台java程序模拟用户注册。
    ③注册时用户名要求长度在[6-14]之间,小于或者大于都表示异常。

    ​ 注意:
    ​ 完成注册的方法放到一个单独的类中。
    ​ 异常类自定义即可。

    ​ class UserService {
    ​ public void register(String username,String password){
    ​ //这个方法中完成注册!
    ​ }
    ​ }

    ​ 编写main方法,在main方法中接收用户输入的信息,在main方法
    ​ 中调用UserService的register方法完成注册。

    package exception.Test;
    
    public class UserService {
        /**
         * 用户注册
         * @param username 用户名
         * @param password 密码
         * @throws MyException2 当用户名为null,或者用户名格式不正确,会出现该异常!
         */
        public void register(String username,String password) throws MyException2 {
            //null == username 这个判断最好放在所有条件的前面,并且这么写比username == null 更保险
            if(null == username || username.length() < 6 || username.length() > 14){
                throw new MyException2("用户名不合法,长度必须在6-14之间!");
            }
            System.out.println("注册成功,欢迎【" + username + "】");
        }
    }
    
    package exception.Test;
    
    public class MyException2 extends Exception{
        public MyException2() {
        }
    
        public MyException2(String message) {
            super(message);
        }
    }
    
    package exception.Test;
    
    public class Test2 {
        public static void main(String[] args) {
            UserService userService = new UserService();
            try {
                userService.register("jack","123");
            } catch (MyException2 e) {
                System.out.println(e.getMessage());
            }
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值