Day14.异常 、可变参数

1 异常

  1. 概述:
    客观角度:不符合现实生活的各种情况,都可以理解为是异常
    Java语言角度:在代码的运行过程中,出现的各种错误导致程序停止运行,那么这些错 误就是异常。
  2. 注意:
    在程序中,异常并不是一个一个表示信息的字符串。异常是一个对象来表示,是JVM 虚拟机创建的异常对象,如果程序中出现了异常对象,就会抛出这个异常对象的各种信 息。
  3. 异常类型:
    Throwable:抛出。 每一个异常类型的顶层父类
    子类:
        Error:错误 错误一般表示比较严重的问题,一旦出现错误无法通过代码解决。
        Exception:异常 异常一般表示比较轻微的问题,如果程序中出现了异常,可以通过代码解决这个异常。
    异常中有两种分支:
            编译时异常:除了RuntimeException类型之外的其他类型
            运行时异常:RuntimeException 和他的子类类型

1.1 编译时异常和运行时异常

  1. 编译时异常:在代码编译阶段,系统会检查代码的语法格式等情况,如果在检查的过程 中出现了问题,就提示一个错误,这些问题就属于编译时异常。
  2. 运行时异常:在代码编译阶段不对代码进行检查,但是在代码运行阶段,如果出现了一 些逻辑等会导致程序意外终止的问题,这些问题就属于运行时异常。
  3. 注意:
    不管是编译时异常,还是运行时异常,都是在运行阶段才会出错。

1.2 JAVA虚拟机默认处理异常的方式

  1. 如果在代码中的某个方法内出现了错误情况,系统会将这个错误发生的原因,发生异常 类型,发生的路径封装到异常对象中。
  2. 如果当前方法中没有处理这个异常对象,就将异常往上抛出,抛给调用该方法的方法。
  3. 如果调用的方法也没有处理异常,那么就一层一层往上抛出,直到抛给main方法,main 方法再抛给虚拟机
  4. 虚拟机将当前异常对象通过标准错误流,打印到控制台,并结束自己。

代码

package demos2_err;

public class Demo01 {
    public static void main(String[] args) {
        //接收到test01抛出的异常对象之后,main也无法处理异常
        //往上抛出该异常,抛给虚拟机
        //虚拟机接收到异常对象之后,就通过错误输出流将异常对象中的信息打印到控制台,并结束自己
        test01();
    }
    private static void test01() {
        //接收到test02方法抛出的异常对象之后,本身也无法处理
        //只能将该异常对象抛出,抛给main方法
        test02();
    }
    private static void test02() {
        //出现一个错误
        //将这个错误发生的各种信息封装到一个异常对象中
        //该方法就将这个异常对象抛给test01();
        int i = 10 / 0;
        System.out.println(i);
    }
}

2 手动处理异常的方式

  1. 异常声明
  2. 异常捕获

2.1 异常声明

  1. 异常的声明:如果在某个方法中出现了编译时异常,可以在当前方法上声明这个异常的类型,声明之后编译时异常就会消失。
    总结:异常的声明只能处理编译时异常
               异常的声明,声明之后,编译时异常就会消失
  2. 声明异常的格式:
	修饰符 返回值类型 方法名称 (参数列表)throws 异常类型1,异常类型2…{
        方法体语句;
    }
  1. 注意事项:
    (1)异常的声明,不能从本质上解决问题,只能在编译阶段不检查这段代码
    如果后续传入一些错误的数据,在运行阶段也可能会发生错误。
    (2)如果方法1中进行了异常的声明,方法2调用了方法1,那么方法2需要对该异 常进行捕获或者处理。
    (3)在声明异常的时候,尽量声明小的异常类型

代码

package demos2_err;


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

public class Demo02 {
    //在方法上声明方法中出现的编译时异常
    //声明之后,该编译时异常就会在编译阶段不做检查
    //在运行阶段,还有可能出现问题
    public static void main(String[] args)throws ParseException {

        SimpleDateFormat sim = new SimpleDateFormat("yy年");
        String str = "20年";
        Date d = sim.parse(str);
        System.out.println(d);

        System.out.println("你好你好");
    }
}

3 异常的捕获

  1. 异常的捕获:如果代码的某个位置会出现了错误情况,可以使用特定的格式,捕获这个 错误,捕获之后可以按照自己定义的方式去处理异常。
  2. 格式:

    try … catch
    try … catch … finally
    try … finally

3.1 try … catch

3.1.1 格式:

try{
   可能会出现错误的代码
}catch(异常类型 异常对象名称){
   处理异常的方式
}

3.1.2 执行流程:

  1. 先执行try中的代码,检测是否出现异常
  2. 如果try中的代码没有出现问题,trycatch直接结束,代码正常执行trycatch后面的 代码。
  3. 如果try中出现了异常,程序立即跳转到catch中查看出现异常所属的类型和catch中声明的类型是否一样,如果一样,就捕获该异常按照指定的方式去处理异常,处理之后,trycatch结束,程序继续运行。
  4. 如果try中出现了catch中没有声明的异常类型,就不能捕获该异常,这时虚拟机来处理这个异常(默认处理方式)。

3.1.3注意事项:

(1)如果在某行代码中,出现了异常,立即去catch块中去匹配异常类型,出现错误的代码后面的代码就不能执行了。

代码
package demos2_err;

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

public class Demo04 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        //执行try中代码
        //如果出现异常,立即执行catch块
        //如果没有出现异常,程序正常往下执行,不会执行catch块
        try{
            int i = 10 / sc.nextInt();//如果录入的数据为0:出现算术异常
            System.out.println(i);
            int[] arr = {1,2,3,4};
            System.out.println(arr[sc.nextInt()]);//如果录入的值不是0-3出现异常

            //如果try中出现的异常对象所属类型,和定义的类型相同,执行定义的处理方式
            //如果try中出现的异常对象和定义的类型不匹配,就由虚拟机默认处理
        }catch(ArithmeticException ae){
            System.out.println("算术异常发生了");
        }
        System.out.println("hello");

    }
}

3.2 try…catch格式的多种异常情况

3.2.1 格式:

try{
   可能出现错误的代码
}catch(异常类型1 对象名1){
    异常1的处理方式
}catch(异常类型2 对象名2){
   异常2的处理方式
}...

3.2.2 流程:

  1. 先执行try中的代码,检测是否出现异常
  2. 如果出现了异常, 就先和异常类型1匹配,如果能匹配上就执行异常1的处理方 式,处理之后,直接结束整个try…catch语句,执行之外的代码。
  3. 如果不能和异常类型1匹配,就继续和异常类型2匹配,如果能匹配上,就执行异 常类型2的处理方式,之后结束整个try…catch语句,执行之外的代码。
  4. 如果异常类型2不能匹配,依次类推,往后匹配。
  5. 如果出现的异常,catch中的类型都不能匹配,虚拟机默认处理

3.2.3 注意事项:

(1)如果定义的多个catch块中的类型,有子父类的关系,不能将父类的类型定义在 子类的类型前面。
(2)如果ctach块中定义的多个异常类型,要使用同一种处理方式,可以使用|来定义异常类型。

代码
package demos2_err;

import java.util.Scanner;

public class Demo05 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try{
            int i = 10 / sc.nextInt();//如果录入的数据为0:出现算术异常
            System.out.println(i);

            int[] arr = {1,2,3,4};
            System.out.println(arr[sc.nextInt()]);//如果录入的值不是0-3出现异常

            arr = null;
            System.out.println(arr[0]);
        }catch(ArithmeticException ae){
            System.out.println("算术异常发生了!");
        }catch (ArrayIndexOutOfBoundsException | NullPointerException ae){
            System.out.println("数组异常发生了!");
        }catch(Exception e) {
            System.out.println("异常发生了");
        }
        System.out.println("hello");
    }
}

3.3 try … catch … finally

  1. 格式:
try{
   可能会发生错误的代码
}catch(异常类型1 异常对象1){
    异常1的处理方式
}catch(异常类型2 异常对象2){
    异常2的处理方式
…
}finally{
   一定需要执行的代码
}
  1. finally:
    使用原因:
    (1)如果有某些代码一定要执行,将代码放在try中或者catch中或者trycatch外 都有可能执行不到
    (2)将这段代码放在finally块中,不管遇到什么情况,系统都会去执行finally中的 内容。
  2. 注意事项:
    (1)finally关键字不能单独使用,一般是用来定义一个代码块
    (2)finally代码块也不能单独使用,一定是和try代码块一起使用

代码

package demos2_exception;

import java.util.Scanner;

public class Demo06 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try{
            int i = 10 / sc.nextInt();//如果录入的数据为0:出现算术异常
            System.out.println(i);

            int[] arr = {1,2,3,4};
            System.out.println(arr[sc.nextInt()]);//如果录入的值不是0-3出现异常
        }catch(ArithmeticException a){
            System.out.println("算术异常发生了");
            //一定会执行的代码(不管遇到任何情况都会执行)
        }finally{
            System.out.println("一定要执行的代码");
        }
    }
}

3.4 try…finally格式

  1. 格式:
try{
   第一段代码
}finally{
   第二段代码
}
  1. 作用:
    如果在程序中,有多段代码,多段代码都需要有执行的机会,那么可以将这多段代码,分别放在try中和finally中,这样,多段代码之间就会互不影响,都有执行的机会。
    用来分隔代码,让代码之间互不影响,都有执行的机会。

代码

package demos2_exception;

import java.util.Scanner;

public class Demo07 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //如果两段代码都需要执行,可以使用tryfinally分隔两段代码
        //其中一段定义在try中先执行
        try{
            try{
                int i = 10 / sc.nextInt();//如果录入的数据为0:出现算术异常
                System.out.println(i);
            }catch(ArithmeticException ae){
                System.out.println("算术异常发生了!");
            }
            //另外一段定义在finally中,一定会被执行
        }finally{
            try{
                int[] arr = {1,2,3,4};
                System.out.println(arr[sc.nextInt()]);//如果录入的值不是0-3出现异常
            }catch(ArrayIndexOutOfBoundsException aioe){
                System.out.println("数组索引越界异常发生了");
            }
        }
    }
}

4 异常类型中的方法

  1. 概述:在异常的体系中,顶层父类Throwable中定义了一下操作异常对象方法
  2. 常用的方法:
方法名解释
getMessage()返回发生异常的原因
getCause()返回引起调用者异常对象发生的另一个异常对象
toString()返回该异常对象发生的原因及所属的类型
printStackTrace()返回该异常发生的路径,原因及类型

代码

package demos2_exception;

import java.util.Scanner;

public class Demo08 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        try{
            int i = 10 / sc.nextInt();
            System.out.println(i);

        }catch(ArithmeticException ae){
            System.out.println("算术异常发生了");
            //当前异常对象是否是被其他异常对象引起的
            //如果是由其他异常对象引起,就会返回引起的异常对象
            //如果不是由其他异常对象引起,返回null
            System.out.println(ae.getCause());
            //返回该异常对象发生的原因
            System.out.println(ae.getMessage());
            //返回该异常对象所属的类型  +  原因
            System.out.println(ae.toString());
            //调用错误流,输出异常对象发生的路径,原因和所属类型
            ae.printStackTrace();
        }
    }
}

5 异常中的构造方法:

  1. Throwable():创建一个没有任何属性值的异常对象
  2. Throwable(String message):创建一个有原因的异常对象

5.1 throw关键字

  1. throw:抛出异常
  2. 使用场景:如果在某个方法中出现了和正常生活不符合的情况,开发人员可以在该方法 中创建一个异常对象,但是创建的异常对象自己不会自动抛出,需要使用throw关键字 抛出异常。
  3. 注意事项:
    (1)在创建一个异常对象之后,一定要主动抛出,如果不抛出,该异常对象就不起作用。
    (2)如果抛出的是一个运行时异常,在编译阶段不做检查,在运行阶段如果传入的数据是错误的,就会执行该异常。
    (3)如果抛出的是一个编译时异常,在抛出之后,编译期间就会出现这个异常,可以声明也可以捕获处理。

代码

public void setAge(int age) throws Exception {
        if(age <= 0){
           //创建一个运行时异常对象
            RuntimeException r = new RuntimeException("年龄非法");
            //抛出一个异常对象
            throw r;
        }
        this.age = age;
}


public class Demo09 {
    public static void main(String[] args)throws Exception {
        Person p = new Person();

        Scanner sc = new Scanner(System.in);
        p.setName("张三");
        try{
            System.out.println("请录入您的年龄:");
            p.setAge(sc.nextInt());
        }catch(RuntimeException re){
            System.out.println("您的年龄录入错误,请重新录入");
            p.setAge(sc.nextInt());
        }

        System.out.println(p.getName() + "..." +p.getAge());
    }
}

5.2 throw和throws关键字的区别

  1. throw关键字是用来抛出一个创建的异常对象
    throws关键字使用来声明一个异常类型
  2. throw关键字在方法中使用
    throws关键字在方法的声明上(参数列表的后面)使用
  3. throw一次只能抛出一个异常对象
    throws可以一次抛出多个异常类型(抛出的多个类型之间使用逗号分隔)

6 自定义异常类型

  1. 使用原因:
    在异常的体系中官方定义了很多异常类型,但是大多类型都没有自己特殊的方法和属 性,目的就是为了使用不同的类型来区分各种问题,当看到对应的类型时,就知道代码中出现了什么问题,方便去开发者分析代码并解决问题。
  2. 步骤:
    (1)创建一个类型以Exception结尾
    (2)将这个异常类型继承Exception或者RunTimeException
    如果继承了Exception类型,当前定义的类型就是一个编译时异常
    如果继承了RuntimeException类型,当前定义的类型就是一个运行时异常类型
    (3)在该类中调用父类的构造方法,用于让子类创建对象

代码

package demos2_exception;

public class IllegeAgeException extends RuntimeException{
    public IllegeAgeException(){

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

7 可变参数

  1. 概述:
    可变参数又称参数个数可变,一般用来定义方法的形参
  2. 使用场景:
    如果某个方法的形式参数个数不确定,可以将该形参定义为可变参数
    定义可变参数之后,该方法可以接收任何个数参数
  3. 好处:
    由于参数数目不定,使用可变参数函数能缩短编码,灵活性和易用性较高
  4. 注意事项:
    (1)可变参数底层是使用数组实现的
    使用这个可变参数的方式,和使用数组的方式一模一样
    (2)如果方法中存在多个参数,包括可变参数,可变参数要放在最后
    (3)一个方法的参数列表中,只能存在一个可变参数
    (4)在传递实际参数时,数据类型要和可变参数的数据类型对应

代码

package demos3_xingcan;

public class Demo01 {
    public static void main(String[] args) {

        System.out.println(getSum(10.23,10,20,30));
    }

    //定义一个方法,可以获取若干整数和一个小数的和
    public static double getSum(double d,int... x){//
       //在方法中,可以将可变参数当做一个数组来使用
        int sum = 0;
        for (int i = 0; i < x.length; i++) {
            sum += x[i];
        }
        return sum + d;
    }
}

7.1 可变参数练习

定义一个方法,方法可以接收一个字符串(表示姓名),可以接收若干门学生成绩(int)
方法功能为打印传入的姓名,和若干成绩的总和(总成绩)

代码

public class Demo02 {
    public static void main(String[] args) {

        printMess("张三",90,80,60,40,50,10);
    }
//    1、定义一个方法,方法可以接收一个字符串(表示姓名),可以接收若干门学生成绩(int)
//    方法功能为打印传入的姓名,和若干成绩的总和(总成绩)

    public static void printMess(String name,int...course){
        int sum = 0;
        for (int i = 0; i < course.length; i++) {
            sum += course[i];
        }
        System.out.println("该学生的姓名为:" + name + ",总成绩为:" + sum);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值