《java学习笔记》之异常处理机制

异常处理机制

一.为什么要使用异常机制

当程序出现了不正常的情况

  • java把异常信息打印到了控制台,供程序员参考,程序员看到异常信息后,可以对程序进行修改
  • 让程序更加健壮,不会因为异常就停止

二.异常处理机制的基本语法

2.1 异常的形式

public class Test01 {
    public static void main(String[] args) {
        //异常在java以类的形式存在
        //通过"异常类"实例化"异常对象"
        NullPointerException nullPointerException =new NullPointerException("空指针异常");
        System.out.println(nullPointerException);//java.lang.NullPointerException: 空指针异常

        ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException =new ArrayIndexOutOfBoundsException("数组下标越界异常");
        System.out.println(arrayIndexOutOfBoundsException);//java.lang.ArrayIndexOutOfBoundsException: 数组下标越界异常

        //mian方法中调用dosome()方法
        //因为dosome()方法上声明有:throws ClassNotFoundException
        //我们在调用dosom()方法时必须对这种异常进行预先处理
        //如果不处理:编译器报错
        //编译器报错信息:Error:(19, 15) java: 未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
        //dosome();
        dosome();
        //有两种方法:
        //第一种:继续往上抛,因为是main方法调用的,所以在main方法上声明
        //第二种方法:使用try-catch对异常进行捕捉
    }

    //这个方法抛出了一个异常
    /*
     * dosom方法在方法声明的时候使用了 throws ClassNotFoundException
     * 这个代码表示dosome()方法在执行的时候,有可能出现ClassNotFoundException异常
     * 叫做类没找到异常,这个异常的父类是Exception,所以ClassNotFoundException是编译时异常
     * */
    public static void dosome() throws ClassNotFoundException{

    }
}

2.2 怎么处理异常

public class Test02 {

    //方法一:继续往上抛,谁调用,谁声明,抛给调用者
    //一般不建议在main上方上使用throws,因为这个异常一旦发生了,一定会上抛给JVM,JVM只能终止
    //异常处理机制的作用就是增强程序的健壮性。做到异常发生了,也不影响程序的执行
    //所以建议main方法的异常建议使用try...catch进行捕捉,就不要上抛了
    //public static void main(String[] args) throws ClassNotFoundException{
    public static void main(String[] args) {

        //第二种:try-catch 捕捉
        //捕捉等于把异常拦下了,异常真正的解决了(调用者是不知道的)
        try {
            dosome();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void dosome() throws ClassNotFoundException{

    }
}

2.3 try-catch理解

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;

public class Test03 {
    public static void main(String[] args) {
        System.out.println("main begin");

        //这里使用了try - catch 所以main方法不用再抛异常了

        try {
            //先执行try,如果有异常就执行 catch
            m1();
        } catch (Exception e) {//e是一个变量名
            //这个分支可以使用e应用,e存储了new出来的异常的内存地址
            //catch捕捉后继续的分支
            e.printStackTrace();
        }
        
        //try...catch 把异常抓住后,这里的代码会继续执行
        System.out.println("main over");
    }

    public static void m1() throws Exception {
        System.out.println("m1 begin");
        //这里报错是因为m2抛出了异常,m1调用就要对异常进行处理
        m2();
        System.out.println("m1 over");
    }

    //抛下面几个异常可以 Exception 是 IOException的父类  IOException是 FileNotFoundException的父类
    public static void m2() throws Exception {
    //public static void m2() throws IOException {
    //public static void m2() throws FileNotFoundException {
        System.out.println("m2 begin");
        //这里报错是因为m3抛了异常,m2调用,要对异常进行处理
        m3();
        System.out.println("m2 over");
    }

    public static void m3 () throws FileNotFoundException {
        System.out.println("m3 begin");
        new FileInputStream("D:\\day25课堂笔记.txt");
        //如果上面一行代码抛异常了,这里是不会执行的
        System.out.println("m3 over");
    }
}

/*
 * 异常对象有两个重要的方法:
 *         String string = exception.getMessage;    获取异常简单描述学习
 *           exception.printStackTrace               打印异常追踪的堆栈信息
 */
public class Test04 {
    public static void main(String[] args) {
        //这里只是new了异常对象,没有抛出,JVM只是认为这是一个简单的对象类
        NullPointerException nullPointerException1 =new NullPointerException();
        NullPointerException nullPointerException2 =new NullPointerException("空指针异常");

        System.out.println(nullPointerException1.getMessage());//null
        System.out.println(nullPointerException2.getMessage());//空指针异常
        System.out.println(nullPointerException1);//java.lang.NullPointerException
        System.out.println(nullPointerException2);//java.lang.NullPointerException: 空指针异常


        //打印异常堆栈信息
        //java后台打印异常堆栈信息的时候,采用了异步线程的方式打印的

        //java.lang.NullPointerException
        //	at caopeng.javase.test.Test04.main(Test04.java:10)
        nullPointerException1.printStackTrace();

        //java.lang.NullPointerException: 空指针异常
        //	at caopeng.javase.test.Test04.main(Test04.java:11)
        nullPointerException2.printStackTrace();//

        //这个在异常信息前输出
        System.out.println("hahaha");


        
        try {
            m1();
        } catch (FileNotFoundException e) {
            //打印异常堆栈信息
            //在实际开发当中,建议使用这个
            //这行代码要写上,不然出错了都不知道
            
            e.printStackTrace();
            
            //怎么看异常信息,怎么快速调试?
            // 从上往下看,SUN写的就不用看了
            
            /*java.io.FileNotFoundException: C:\Users\A556U\Desktop\文\day34-作业.txt (系统找不到指定的路径。)
              at java.base/java.io.FileInputStream.open0(Native Method)
              at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
              at java.base/java.io.FileInputStream.(FileInputStream.java:157)
              at java.base/java.io.FileInputStream.(FileInputStream.java:112)
              at caopeng.javase.test.Test04.m3(Test04.java:56)
              at caopeng.javase.test.Test04.m2(Test04.java:52)
              at caopeng.javase.test.Test04.m1(Test04.java:48)
              at caopeng.javase.test.Test04.main(Test04.java:40)*/
        }
    }


    private static void m1() throws FileNotFoundException {
        m2();
    }

    private static void m2() throws FileNotFoundException {
        m3();
    }

    private static void m3() throws FileNotFoundException {
        new FileInputStream("C:\\Users\\A556U\\Desktop\\文\\day34-作业.txt");
    }
}

2.4 try…catch…finally

public class Test05 {
    public static void main(String[] args){
        FileInputStream fileInputStream = null;

        //捕捉异常
        try {
            fileInputStream =new FileInputStream("D:\\day25课堂笔记.txt");

            //开始读文件
            System.out.println("开始读文件");

            String s = null;
            //这里肯定会抛空指针异常
            s.toString();
            //那么这里的流就关闭不了了,这样会占用内存
            //fileInputStream.close();

            System.out.println("这里会执行吗");//不会输出
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (NullPointerException e){
            System.out.println("空指针异常");
            e.printStackTrace();
        }
        finally {
            //finally字句必须和try一起出现,不能单独编写
            //在finally后面的语句块一定会执行,即使try中出现异常也会正常运行
            System.out.println("finally 语句块执行");
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.5 关于finally

//关于finally
public class Test06 {
    public static void main(String[] args) {
        //没有catch,只有try 和finally
        try{
            System.out.println("try.....");
            return;
        }finally {
            System.out.println("finally");
        }

        //Unreachable statement
        //不会执行的语句
        //System.out.println("会执行吗");
            /*
            try.....
            finally
            */
            //这表明了finally一定会执行
        //顺序应该是先try,然后看到return,就跳到finally,finally执行完再到return
    }
}

三.自定义异常

3.1怎么自定义异常

/*怎么定义异常?
 * 两步:
 *   第一步:编写一个类继承Exception或者RunException
 *   第二步:提供两个构造方法,一个无参的,一个有参的*/
//死记硬背
public class Test07 extends Exception{
    //构造方法,无参
    public Test07() {

    }
    //构造方法,有参
    public Test07(String message) {
        super(message);
    }
}

public class Test08 {
    public static void main(String[] args) {
        //这里只是尝试new出自定义异常,未抛出

        /*Test07 test07 =new Test07();
        System.out.println(test07);//caopeng.javase.test.Test07
        //打印异常的简单消息
        System.out.println(test07.getMessage());//null
        //打印异常的堆栈信息
        test07.printStackTrace();
        //caopeng.javase.test.Test07
        //	at caopeng.javase.test.Test08.main(Test08.java:6)*/

        Test07 test07 =new Test07("自定义异常");
        System.out.println(test07);//caopeng.javase.test.Test07: 自定义异常
        //打印异常的简单消息
        System.out.println(test07.getMessage());//自定义异常
        //打印异常的堆栈信息
        test07.printStackTrace();
        //caopeng.javase.test.Test07: 自定义异常
        //	at caopeng.javase.test.Test08.main(Test08.java:16)
    }
}

3.2 抛自定义异常`
如果不自定义异常

/*
		1、这个栈可以存储java中的任何引用类型的数据。
		2、在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
		3、在栈中提供pop方法模拟弹栈。(栈空了,也有有提示信息。)
		4、编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈的动作。
		5、假设栈的默认容量为10.(注意无参构造方法的编写方式)
 */
public class MyStack {
    //这个栈可以存储java中的任何引用类型的数据。
    //Object类的数组
    //假设栈的默认容量为10
    private Object[] objects;

    //栈帧
    //每添加一个元素就+1
    //每弹出一个元素就-1
    private int index = -1;//表示栈帧指向了顶部元素,就是多加了一个头指针,让栈不为空,因为没有元素的时候,栈帧指向0是不太恰当的

    //构造方法
    public MyStack() {
        this.objects = new Object[10];
    }

    public MyStack(Object[] objects) {
        this.objects = objects;
    }

    //set 和 get 方法
    public Object[] getObjects() {
        return objects;
    }

    public void setObjects(Object[] objects) {
        this.objects = objects;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }



    //在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
    public void push(Object o){
        if (index + 1 > objects.length){
            System.out.println("压栈失败,栈已满");
            return;
        }else{
            //压栈
            index ++;
            objects[index] = o;
            System.out.println("压栈" + o + "成功,栈帧指向" + index);
        }
    }

    //在栈中提供pop方法模拟弹栈。(栈空了,也有有提示信息。)
    public void pop(){
        if (index == -1){
            System.out.println("弹栈失败,栈以空");
            return;
        }else{
            //弹栈
            System.out.println("弹栈" + objects[index] + "元素成功");
            objects[index] = null;
            index --;
            System.out.println("栈帧指向" + index);
        }
    }
}

自定义异常

//栈操作异常
public class MyStackException extends Exception{
    //构造方法
    public MyStackException() {

    }

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

/*
		1、这个栈可以存储java中的任何引用类型的数据。
		2、在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
		3、在栈中提供pop方法模拟弹栈。(栈空了,也有有提示信息。)
		4、编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈的动作。
		5、假设栈的默认容量为10.(注意无参构造方法的编写方式)
 */
public class MyStack {
    //这个栈可以存储java中的任何引用类型的数据。
    //Object类的数组
    //假设栈的默认容量为10
    private Object[] objects;

    //栈帧
    //每添加一个元素就+1
    //每弹出一个元素就-1
    private int index = -1;//表示栈帧指向了顶部元素,就是多加了一个头指针,让栈不为空,因为没有元素的时候,栈帧指向0是不太恰当的

    //构造方法
    public MyStack() {
        this.objects = new Object[10];
    }

    public MyStack(Object[] objects) {
        this.objects = objects;
    }

    //set 和 get 方法
    public Object[] getObjects() {
        return objects;
    }

    public void setObjects(Object[] objects) {
        this.objects = objects;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }



    //在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
    public void push(Object o) throws MyStackException {
        if (index + 1 >= objects.length){
            /*System.out.println("压栈失败,栈已满");
            return;*/
            //手动把异常抛出去
            throw new MyStackException("压栈失败,栈已满");
        }else{
            //压栈
            index ++;
            objects[index] = o;
            System.out.println("压栈" + o + "成功,栈帧指向" + index);
        }
    }

    //在栈中提供pop方法模拟弹栈。(栈空了,也有有提示信息。)
    public void pop() throws MyStackException {
        if (index == -1){
            /*System.out.println("弹栈失败,栈以空");
            return;*/
            //手动把异常抛出去
            throw new MyStackException("弹栈失败,栈以空");
        }else{
            //弹栈
            System.out.println("弹栈" + objects[index] + "元素成功");
            objects[index] = null;
            index --;
            System.out.println("栈帧指向" + index);
        }
    }
}

public class MyStackTest {
    public static void main(String[] args) {
        //创造一个stack对象
        MyStack myStack =new MyStack();
        //此时里面什么都没有,弹栈试一下
        try {
            myStack.pop();
        } catch (MyStackException e) {
            e.printStackTrace();
        }

        //压栈
        //调用push可能会满栈,这里起到了提醒的作业
        try {
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
            myStack.push(new Object());
        } catch (MyStackException e) {
            e.printStackTrace();
        }

        //弹栈
        try {
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
            myStack.pop();
        } catch (MyStackException e) {
            e.printStackTrace();
        }

    }
}

四.方法重写不能比父类抛出更多的异常

/*
* 之前讲解方法覆盖时
*   重写后的方法不能比重写之前抛出跟多(更宽泛)的异常,可以更少
* */
class Animal {
    public void dosome(){

    }

    public void doother() throws Exception{

    }
}
class Cat extends Animal{
    //编译报错
    /*public void dosome() throws Exception{

    }*/

    //编译正常
    /*public void doother() {

    }*/

    //编译正常
    /*public void doother() throws Exception {

    }*/

    //编译正常
    public void doother() throws NullPointerException{

    }
}
/*异常中的关键字
* try
* catch
* finally
*
* throws 在方法上声明,表示异常上抛给使用者
* throw   手动抛异常*/

你可能感兴趣的:(《java学习笔记》之异常处理机制)