[Java][华为云Java编程创造营][学习笔记][第三阶段][01_Java异常处理实战]

举报
John2021 发表于 2021/11/28 11:37:59 2021/11/28
【摘要】 1,Java异常处理实战 1.1,错误和异常 异常概述异常:就是指是程序执行过程中,出现的非正常情况,最终导致JVM非正常停止。异常体系Error:严重错误问题,无法处理的错误,只能事先避免,比如内存溢出。Exception:称为异常类,它代表程序本身可以处理的问题,比如数组下标越界。 1.2,异常分类编译时异常也称为检查异常,必须显示处理,否则程序就会发生错误,无法通过编译。运行时异常也...

1,Java异常处理实战

1.1,错误和异常

异常概述

  • 异常:就是指是程序执行过程中,出现的非正常情况,最终导致JVM非正常停止。

  • 异常体系

  • Error:严重错误问题,无法处理的错误,只能事先避免,比如内存溢出。

  • Exception:称为异常类,它代表程序本身可以处理的问题,比如数组下标越界。

1.2,异常分类

  • 编译时异常也称为检查异常,必须显示处理,否则程序就会发生错误,无法通过编译。

  • 运行时异常也称为非检查异常,无需显示处理,也可以和编译时异常一样处理。

  • RuntimeException所有的类及其子类被称为运行时异常,其他的异常都是编译时异常。

  • RuntimeException:在编译期是不检查的,出现问题后,需要我们回来修改代码。

  • 非RuntimeException:编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了。

1.3,异常处理机制

异常处理机制(1)

  • 当程序中抛出一个异常后,从程序中导致异常的代码处跳出,Java虚拟机检测寻找和try关键字匹配的处理该异常的catch块。
  • 如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。
  • 如果没有找到处理该异常的catch块,最终运行时报错。

异常处理机制(2)

异常处理之try…catch…

//格式
/**
 * try
 * {
 *     可能出现异常的代码;
 * }catch(异常名 变量名)
 * {
 *     异常的处理代码;
 * }
 * */

执行流程

  • 程序从try里面的代码开始执行。
  • 出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统。
  • 当运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理。
  • 执行完毕之后,程序还可以继续往下执行。

异常处理机制(3)

异常处理之多个…catch…

  • 每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。
  • 捕获的异常类型由上至下的捕获异常类型的顺序应是子类到父类的。
/**
 * 子类型异常在前,父类型异常在后,这样的顺序依次捕获,否则编译不通过
 * try
 * {
 *     ...
 * }catch(NullPointException e)
 * {
 *     ...
 * }catch(RuntimeException e)
 * {
 *     ...
 * }catch(Exception e)
 * {
 *     ...
 * }
 * */
/**
 * try
 * {
 * 可能出异常的代码
 * }catch(异常类 变量名)
 * {
 * 异常处理
 * }
 */
public class ExceptionDemo1
{
    public static void main(String[] args)
    {
        //定义一个数组
        try
        {
            int[] arr = {12, 56, 2};
            System.out.println(arr[3]);
        } catch (ArrayIndexOutOfBoundsException e)
        {
            System.out.println("数组下标越界异常处理");
            System.out.println("catch语句往下执行");
        } catch (RuntimeException e)
        {
            System.out.println("父类异常");
        } catch (Exception e)
        {
            System.out.println("总异常");
        }
        /**
         * 输出结果:
         * 数组下标越界异常处理
         * catch语句往下执行
         * */
    }
}

1.4,finally

  • finally语句为异常处理提供的一个统一的出口,使得在控制流程到程序其他部分之前,能够对程序的状态作统一的管理。
  • 无论try所指的程序块中是否抛出异常,finally所制定的代码都要被执行。
  • 通常finally在语句中可以进行资源的释放工作,如关闭打开的文件,关闭数据库连接等。

语法

/**
 * try
 * {
 *     可能出现异常的代码;
 * }catch(异常名 变量名)
 * {
 *     异常的处理代码;
 * }finally
 * {
 *     必须执行的代码;
 * }
 * */
public class ExceptionDemo2
{
    public static void main(String[] args)
    {
        //定义一个数组
        try
        {
            int[] arr = {12, 56, 2};
            System.out.println(arr[3]);
        } catch (NullPointerException e)
        {
            System.out.println("数组下标越界异常处理");
            System.out.println("catch语句往下执行");
        } catch (Exception e)
        {
            System.out.println("总异常");
        } finally
        {
            //不管try和catch中的代码块是否执行,finally里面的代码永远都会执行
            //注意:finally不能单独使用
            System.out.println("释放资源");
        }
        /**
         * 输出结果
         * 总异常
         * 释放资源
         * */
    }
}

1.5,throws和throw关键字

  • 程序中会声明很多方法,这些方法中可能会因某些错误而引发异常,但不希望直接在这个方法中处理这些异常,而希望调用它的方法统一处理,这时候就需要使用"throws"关键字声明在这个方法上抛出异常。
  • 声明throw异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名…{}
  • 编译时异常必须要进行处理,两种处理方法:try…catch…或者throws,如果采用throws这种方案,将来谁调用谁处理。
  • 运行时异常可以不处理,出现问题后,需要我们回来修改代码。
/**
 * throws关键字:接在方法参数后,该方法不处理异常,谁调用谁处理
 * throws抛编译时异常:调用时要try...catch...或者再throws
 * throws抛运行时异常:要修改代码
 * */
public class ExceptionDemo3
{
    public static void main(String[] args)
    {
        //throwDemo();//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
        //throwDemo2();//java: 未报告的异常错误java.text.ParseException; 必须对其进行捕获或声明以便抛出
        // 以上方式会增加JVM负担(在main方法中throws)
        try
        {
            throwDemo2();//编译时异常处理
        } catch (ParseException e)
        {
            System.out.println("编译时异常处理");
        }
    }

    /**
     * throws抛运行时异常
     * */
    public static void throwDemo() throws ArrayIndexOutOfBoundsException
    {
        //定义一个数组
        int[] arr = {12, 34, 56};
        System.out.println(arr[3]);//数组下标越界异常
    }

    /**
     * throws抛运行时异常
     * */
    public static void throwDemo2() throws ParseException
    {
        //将字符串时间改为Date类型的时间
        String data = "20211127";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse(data);
        System.out.println(date);
    }
}
  • 当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时候,想要自行抛出异常,此时需要用到throw关键字,并生成指定的异常对象后抛出。
  • throw定义在方法内,用来抛出一个异常对象。
  • 格式:throw new 异常类名(参数)
public class ExceptionDemo4
{
    public static void main(String[] args)
    {
        int[] arr = null;
        throwDemo(arr);//Exception in thread "main" java.lang.NullPointerException: 传进来的数组对象为空
    }

    /**
     * throw关键字
     */
    public static void throwDemo(int[] arr)
    {
        if (arr == null)
        {
            //告诉用户此时数组为null
            throw new NullPointerException("传进来的数组对象为空");
        }
    }
}
  • throws和throw的区别
throws throw
1,用在方法声明后面,跟的是异常类名 1,用在方法体内,跟的是异常对象名
2,表示抛出异常,由该方法的调用者来处理 2,表示抛出异常,由方法体内的语句处理
3,表示出现异常的一种可能性,并不一定会发生这些异常 3,执行throw一定抛出了某种异常

1.6,父类和子类构造函数中使用throws关键字

父类构造函数中使用throws抛出编译时异常

  • 子类构造方法中throws抛出的异常必须大于等于父类throws抛出的异常,并且只能抛出不能捕获
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 父类构造函数中抛出编译时异常
 */
public class Father
{
    public Father() throws ParseException
    {
        //字符串时间类型转成Date类型
        String dateStr = "2021-11-27";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse(dateStr);
        System.out.println(date);
    }
}
import java.text.ParseException;

public class Son extends Father
{
    public Son() throws ParseException
    {
        super();//调用父类构造函数,如果没有写默认提供,并且该方法只能位于第一行
    }
}

父类构造函数中使用throws抛出运行时异常

  • 当父类构造函数中throws抛出运行时异常,子类构造方法中可以声明异常也可以不声明异常,如果声明,子类构造函数中抛出的异常要大于或者等于父类中抛出的异常。
//父类
public class Father
{
    public Father() throws NullPointerException
    {

    }
}
public class Son extends Father
{
    /*public Son() throws NullPointerException
    {

    }*/

    /*public Son() throws Exception
    {

    }*/

    public Son()
    {
        super();
    }
}

1.7,重写方法中使用throws关键字

方法重写中使用throws关键字(1)

使用继承时,在父类某个方法上声明了异常,子类重写这个方法时

  • 不处理异常(重新定义时不设定throws)
import java.text.ParseException;

//父类
public class Sub
{
    public void show() throws ParseException
    {

    }
}
//子类方法实现
public class Base extends Sub
{
    @Override
    public void show()
    {

    }
}

方法重写中使用throws关键字(2)

使用继承时,在父类某个方法上声明了异常,子类重写这个方法时

  • 可仅throws父类方法中声明的部分异常
import java.text.ParseException;

//父类
public class Sub
{
    public void show() throws ParseException, Exception
    {

    }
}
import java.text.ParseException;

//子类方法实现
public class Base extends Sub
{
    public void show() throws ParseException
    {

    }
}

方法重写中使用throws关键字(3)

使用继承时,在父类某个方法上声明了异常,子类重写这个方法时

  • 子类throws抛出的异常小于或者等于父类throws抛出的异常
//父类
public class Sub
{
    public void show() throws Exception
    {

    }

    public static void main(String[] args)
    {

    }
}
import java.text.ParseException;

//子类方法实现
public class Base extends Sub
{
    /*@Override
    public void show() throws ParseException
    {
        
    }*/

    @Override
    public void show() throws Exception
    {

    }
}

方法重写中使用throws关键字(4)

  • 子类重写父类该方法时抛出运行时异常,那么父类被重写的方法上可以不抛出异常
//父类
public class Sub
{
    public void show()
    {

    }
}
//父类方法实现
public class Base extends Sub
{
    @Override
    public void show() throws NullPointerException
    {
        super.show();
    }
}

方法重写中使用throws关键字(5)

如果父类方法没有抛出异常,子类重写父类该方法时抛出编译异常,那么子类重写的方法会报编译错误,此时解决办法有两种:

  • 1,父类被重写的方法上抛出的异常要大于或者等于子类方法上抛出的异常
//父类
public class Sub
{
    public void show() throws Exception
    {

    }
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

//父类方法实现
public class Base extends Sub
{
    @Override
    public void show() throws ParseException
    {
        String date = "2021-11-28";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = simpleDateFormat.parse(date);
        System.out.println(date1);
    }
}
  • 2,子类方法中直接捕获异常,不抛出
public class Sub
{
    public void show()
    {

    }
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

//子类方法直接捕获异常
public class Base extends Sub
{
    @Override
    public void show()
    {
        try
        {
            String date = "2021-11-28";
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date date1 = simpleDateFormat.parse(date);
            System.out.println(date1);
        } catch (ParseException e)
        {
            e.printStackTrace();
        }
    }
}

1.8,RuntimeException和自定义异常类

RuntimeException类

  • RuntimeException类属于非检查异常,因为普通JVM操作引起得运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在Java程序中会频繁出现。因此它们不受编译期检查与处理或者声明规则的限制。

常见RuntimeException

  • NullPointerException,当应用程序试图在需要对象的地方使用null时,抛出该异常。
  • ArrayIndexOutOfBoundsException,当使用的数组下标超出数组允许范围时,抛出该异常。
  • ClassCastException,当试图将对象强制转换为不是实例的子类时,抛出该异常。
  • NumberFormatException,当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当的格式时,抛出该异常。

Exception常用API

  • printStackTrace,Throwable定义了一个方法可以输出错误信息,用来跟踪异常事件发生时执行堆栈的内容,该方法定义为:void printStackTrace()
/**
 * try
 * {
 *
 * }catch(Exception e)
 * {
 *     e.printStackTrace();//输出执行堆栈信息
 * }
 * */
  • getMessage,Throwable定义了一个方法可以得到有关异常事件的信息,该方法定义为:String getMessage()
/**
 * try
 * {
 *
 * }catch(Exception e)
 * {
 *     System.out.println(e.getMessage());
 * }
 * */

自定义异常类的意义

  • 异常机制可以保证程序更安全和更健壮。虽然类库已经提供很多可以直接处理异常的类,但是有时候为了更加精准地捕获和处理异常已呈现更好的用户体验,需要开发者自定义异常。
  • 继承Exception自定义异常,格式为:
public class 自定义异常类名 extends Exception
{
    //无参构造
    //有参构造
}

1.9,垃圾回收机制

  • 垃圾回收机制简称GC是JVM自带的一个线程,用于回收没有任何引用指向地对象。
  • 程序员不用担心内存管理,因为垃圾收集器会自动进行回收管理。
  • 一般情况下,当我们需要GC线程即刻回收无用对象时,可以调用System.gc()方法。
  • System.gc()用于建议虚拟机马上调度GC线程回收资源,具体的实现由JVM决定。

finalize()

  • finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用。

内存泄漏

  • 不再使用的内存没有被及时回收,严重的内存泄漏会因过多的内存占用而导致程序的崩溃。
  • GC线程判断对象是否可以回收的依据是该对象是否有引用指向,因此,当确定该对象不再使用时,应该及时将其引用设置为null。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。