黑马高级班DAY08【异常,lambda和Stream】

day08【异常、lambda和Stream】

今日内容

  • 异常
  • 异常处理
  • 多线程

教学目标

  • 能够辨别程序中异常和错误的区别
  • 说出异常的分类
  • 列举出常见的三个运行期异常
  • 能够使用try…catch关键字处理异常
  • 能够使用throws关键字处理异常
  • 能够自定义并使用异常类

第一章 异常

1.1异常概念

异常:不正常
生活中的异常:
例:在上课时,突然间停电,造成上课终止。 处理:等待来电、使用备用发电机。
程序中的异常:
程序在运行的过程,出现了一些突发情况,造成程序无法继续运行。我们把上述的突发情况,无法正常运行的这些状态称为Java中的异常。
Java中的异常:就是程序中出现的错误(bug),或者不正常现象。而我们在开发程序的时候,就需要对这些问题进行预先的判断和处理。
学习Java中的异常,我们需要研究:
1.什么是异常;
2.异常问题怎么去解决和处理;
3.我们自己怎么把问题报告给程序和使用者;
我们编写程序的时候,在程序中肯定会有问题(bug)出现,而sun公司把开发中最常见的一些问题,进行总结和抽取,形成了一个体系,这个体系就是我们要学习的异常体系。

异常值得并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。

1.2 异常的产生过程解析(掌握)

需求:根据用户指定的下表,输出改下标对应的数据。
1)定义一个类ExceptionDemo:
2)定义一个主方法,在主方法中定义一个int类型的数组,在这个数组中存放int类型的数据:
3)在这个类中自定义一个函数getValue,在这个函数中根据调用者传递过来的下表和数组返回给调用者
对应的数据:
4)在主方法中调用自定义函数getValue,接受自定义函数返回回来的数据,并将数据打印到屏幕上;
请添加图片描述
说明:上述代码发生异常的过程:
1)jvm先去调用main函数,在main函数中调用了getValue函数,然后jvm将getValue函数,然后jvm将getValue函数加载到内存中;
2)Jvm在执行getValue函数的时候,由于数组下标index的值3超过了数组的最大下标的范围,所以在这里发生了异常问题,ArrayIndexOutOfBoundsException,这样导致程序就不会向下执行,jvm会在发生异常的地方停止,jvm会将发生异常信息(发生异常的位置、异常内容、异常类型等)封装到一个类中(Java的异常类),然后把这个封装了异常信息类的对象(new 异常类)丢给了当前调用函数的地方。
3)如果发生异常,jvm会自动创建封装了异常信息的异常类的对象,然后将对象使用throw关键字抛给调用getValue函数的地方。

4)由于getValue函数把问题抛给了main函数,所以导致了main函数中也有了异常,而main函数中的异常是被迫接收的,此时main函数中并没有解决此异常的解决方案,但是main函数是jvm调用的,所以main函数又将异常抛给了jvm虚拟机,jvm已经是底层了,不能再将异常抛出,jvm需要对这个问题进行处理,即将这个问题显示到屏幕上,让程序的使用者看到。

1.3异常简单应用举例

需求:代码和上述代码相同,我们要解决上述问题的发生。

1)由于在自定义函数中两个参数都是接收外界传递过来的,我们为了保证程序的健壮(合法)性,

所以我们要对传递过来的数据进行合法性的判断;

2)分别对下标和数组进行判断,如果不合法,将发生问题的异常抛出去给程序员看;

/*
	针对发生的异常进行简单的处理
*/
class ExceptionDemo1
{
   
   public static void main(String [] agrs)
   {
   
    //定义数组
    int  [] arr={
   1,2,5};
    //int value=getValue(arr,1);
    int value=getValue(arr,1);
    System.out.println(value);
    }
    //定义函数根据指定的下标返回对应的值
    public static int getValue(int [ ] arr,int index)
    {
   
           /*
               	以后在开发中,定义函数的时候,对外界传递过来的参数一定要
			合法性的判断
			这里需要对错误数据进行判断,然后将错误信息报告给调用者
			在实际开发中我们一般会给固定的文件写错误信息(错误文档)
		*/
		//System.out.println("haha");
		/*
		 * 发生空指针异常的地方,一定是使用了某个引用变量,而这个引用变量又不指向任何的对象
		 * 也就是说引用变量中保存的是null。这是使用空的引用调用属性或行为,而由于根本引用不指向
		 * 任何的对象,那么就没有属性和行为而言,更无法去调用了。肯定就发生空指针异常。
		 */
		 if (arr==null)
		 {
   
		     throw new NullPointException("对不起,数组引用变量的值不能为null")}
		   if(index<0||index>arr.length)
		   {
   
		         throw new ArrayIndexOutBoundsException("下标越界了。。。")}
		         return arr[index];
		         }
}

1.4 异常体系

我们书写程序,肯定会有问题的发生,这些问题统称为异常。而sun公司把最常见的一些异常进行类的描述和封装。然后我们如果在程序中遇到了这些问题,就可以直接通过这些描述异常的类进行错误信息的封装。然后把这些信息丢给程序的调用者。

异常的根类是java.lang.Throwable,Throwable这个类描述的是Java中所有异常和错误的共性内容。其下有两个子类:java.lang.Error与java.util.Exception,平常所说的异常指java.util.Exception。
请添加图片描述
Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。

    ​ 在程序运行时,会产生一些错误信息。java把这些错误信息使用Error或其子类进行描述。

    错误属于系统级别的,是由于JVM在操作内存时(JVM需要借助操作系统来实现内存的操作),出现了一些不正常的操作,造成内存错误,出现错误后操作系统就会把这个错误返回给JVM。

        在程序中,遇到错误时,java没有针对性的解决方案,只能通过修改源代码的方式来解决程序中的错误问题。
    
  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒。

    ​ 在程序运行时,也会出现一些异常状况。表示Java程序中存在的异常问题,而不是错误问题。这些异常问题,在程序中通过判断等形式是可以检测并且预防的。针对这些异常问题,程序员在写代码的时候一旦发生,必须给出有效的解决方案。

    java对于异常状况是有针对性的解决方案(异常处理),例:角标越界、空指针异常等。

    异常状况的发生,通常是JVM在操作一些数据时,出现的问题,java对于异常的发生,是可以通过一些手段(捕获)避免程序终止运行,保证让程序继续向下正常执行。

1.5 异常分类

我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。

异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会让编译器检测(不报错)。请添加图片描述
    小结:

    RuntimeException和Exception有什么区别:

    Exception属于编译时异常,编译器在编译时会检测该异常是否异常的处理方案 ,如果没有处理方案,编译不能通过。

    RuntimeException属于运行时异常,编译器不会检测该异常是否有异常的处理方案,不需要声明。

说明:在Exception的所有子类异常中,只有RuntimeException不是编译异常,是运行时异常,其他子类都是编译异常。

第二章 异常的处理

Java异常处理的五个关键字:try,catch,finally,throw,throws

2.1 抛出异常throw

在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。

在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?
1.创建一个异常对象。封装一些提示信息(信息可以自己编写)。
2.需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw异常对象。
throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
使用格式:

throw new 异常类名(参数);

例如:


throw new NullPointerException("要访问的arr数组不存在");

throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

学习完抛出异常的格式后,我们通过下面程序演示下throw的使用。

public class ThrowDemo{
   
  public static void main(String[ ] agrs){
   
  //创建一个数组
  int [ ] arr={
   2,4,52,2};
  //根据索引找对应的元素
  int intdex=4;
  int element=getElement(arr,index);
  System.out.println(element);
  System.out.println("over");
  }
  /*
  *根据 索引找到数组中对应的元素
  */
  public static int getElement(int [ ] arr,int index){
   
  //判断 索引是否越界
  if (index<0||index>arr.length-1){
   
  /*
             判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
             这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 
              */
              throw new ArrayInedexOutOfBoundException("哥们,角标越界了")}
              int element =arr[index];
              return element
              }
       }

注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。
那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throw声明处理。

2.2声明异常throw(掌握)

声明异常:
将问题表示出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throw进行声明,让调用者去处理。
关键字throw运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)。
声明异常格式:

修饰符 返回值类型 方法名(参数)throw 异常类名1,异常类2......
{
   
 方法体
 }

注意:
1)throw后面可以跟多个异常类,使用逗号分隔;
2)在函数中如果发生了异常,jvm会拿发生的异常和声明的异常类进行分配,如果匹配才会把发生的异常交给别人处理;
需求:声明的简单实用
1)定义一个ThrowDemo类;
2)在定义一个Demo类,在这个类中定义一个函数show,在show函数中对传入的参数X进行判断,如果x等于0,则使用throw关键字抛出Exception异常;
3)由于抛出的时编译时异常所以需要在show函数上使用throws关键字声明这个异常,告诉调用者,有异常;
4)在show函数中打印x的值;
5)在ThrowDemo类中创建Demo类的对象,使用对象调用函数;
声明异常的代码演示:

   public class ThrowsDemo {
   
    public static void main(String[] args) throws Exception {
   
        show(2);
    }
    // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
    public static void show(int x) throws Exception {
   
        if (x == 0) {
   //如果x等于0
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new Exception("x不能等于0");
        }
       System.out.println(x);
    }
}

注意:在开发中,main函数中不会出现声明,在main函数通常是使用捕获。

2.3捕获异常try…catch(掌握)

2.3.1格式

就是遇到异常时,不再把异常交给他人处理,自己处理。
在程序中有异常,但这个异常我们不能继续使用throw声明,这时不处理,程序无法编译通过,那么在程序中只能捕获方式解决问题。
格式如下:

try{
   
  编写可能会出现异常的代码
  }catch (异常类型 对象名){
   
  处理异常的代码
  //记录日志/打印异常信息/继续抛出异常
  }

**try:**该代码块中编写可能产生异常的代码。
**catch:**用来进行某种异常的捕获,实现对捕获到的异常进行处理。

注意:try和catch都不能单独使用,必须连用。

演示如下:

public class ThrowDemo {
   
	 /*
     * 一般在开发中我们不会在主函数上面抛异常,主函数一般是最后处理者,
     * 我们需要在主函数中对异常进行处理------》捕获
     */
     public static void main (String [ ] agrs){
   
      try
      {
   
      show (0);//异常代码
      }catch(Exception e)//Exception e=new Exception("x不能等于0")
      {
   
       //处理异常的代码
            //System.out.println("hahhahaha");
			//System.out.println(e.getMessage());//x不能等于0
//			System.out.println(e);
           e.printStackTrace();
           }
       }
       public static void show(int x)throw Exception{
   
       if(x==0){
   //如果x等于0
       throw  new Exception("x不能等于0")}
       System.out.println(x);
       }
}
总结:不管自己是函数的定义者,还是函数的调用者,只要是在自己的函数中有异常发生,那么自己都可以使用上述的两种方案对异常进行处理。
### 2.3.2如何获取异常信息:
Throwable类中定义了一些查看方法:

异常中的常用方法
Throwable类中定义了一些查看方法:
| 方法                | 说明                                       |
| ----------------- | ---------------------------------------- |
| getMessage()      | 获取报错原因.获取异常的描述信息,原因(提示给用户的时候,就提示错误原因)|
| toString()        | 获取报错的类型和原因                               |
| printStackTrace() | 直接打印报错的类型、原因和位置.包含了异常的类型,异常的原因,还包括异常出现的位置. |
代码演示:
```java
public class  Test04 {
   
    public static void main(String[] args) {
   

        //解析异常(编译时期异常)
        String s = "2000-11-12";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");

        try {
   
            //解析
            Date date = sdf.parse(s);
            System.out.println(date);

        }catch (ParseException e){
   
            //e是一个异常对象
            //1.getMessage()获取报错原因
//            String ss = e.getMessage();
//            System.out.println(ss);      //Unparseable date: "2000-11-12"


            //2.toString()获取报错的类型和原因
//            String ss = e.toString();
//            System.out.println(ss);       //java.text.ParseException: Unparseable date: "2000-11-12"


            //3.printStackTrace()直接打印报错的类型、原因和位置
            /*
               java.text.ParseException: Unparseable date: "2000-11-12"
                at java.base/java.text.DateFormat.parse(DateFormat.java:388)
                at com.itheima_03.Demo05_tryCatch.main(Demo05_tryCatch.java:17)
            */
            e.printStackTrace();
        }

        //当trycatch执行完程序会继续往后执行
    }
}

2.3.3 捕获异常代码实践

定义一个方法接收一个生日日期字符串,计算生日日期到当前日期的天数并返回。

main方法中让用户输入一个生日日期字符串,调用设计好的方法计算在地球上活了多少天。

**要求:**如果解析发生异常,捕获异常,提示用户要重新输入生日日期字符串,直到输入正确的日期为止。

思考:设计代码的过程中想想什么时候捕获异常,什么时候声明异常?

package com.itheima.sh.k_date_11;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
    计算出生天数:
    步骤:
    1.创建键盘录入的对象
    2.获取录入的生日
    3.创建日期格式化解析类对象
    4.使用日期格式化解析类对象调用解析方法将录入的生日的字符串解析为Date
    5.将解析后的Date对象转换为毫秒
    6.获取当前系统时间
    7.将当前系统的Date时间转换为毫秒
    8.将当前系统时间毫秒和生日的毫秒进行差值运算然后转换为天数,并输出
 */
 public class DateFormatDemo05{
   
    public static void main (String [ ] agrs){
   
    //循环控制
    while (ture){
   
    try{
   
     //1.创建键盘录入的对象
     Scanner sc =new Scanner(System.in);
     //2.获取了录入的生日
     System.out.println("请输入您的生日(yyyy-MM-dd):")String inputBirthdayStr=sc.nextLine();
     //调用方法获取出生天数
     long day=getDays(inputBirthdayStr);
     //8.将当前系统时间毫秒和生日的毫秒进行差值运算然后转换为天数,并输出
     System.out.println("您出生了:"+day+"天")//停止死循环
     break;
     }catch(ParseException e){
   
     System.out.println("日期格式不对,请重新输入日期(yyyy-MM-dd"));
     }
     }
     }  
    public static long getDays(String inputBirthdayStr) throws ParseException {
   
        //3.创建日期格式化解析类对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //4.使用日期格式化解析类对象调用解析方法将录入的生日的字符串解析为Date
        Date birthdayDate = sdf.parse(inputBirthdayStr);
        //5.将解析后的Date对象转换为毫秒
        long birthdayTime = birthdayDate.getTime();
        //6.获取当前系统时间
        Date nowDate = new Date();
        //7.将当前系统的Date时间转换为毫秒
        long nowTime = nowDate.getTime();
        //计算天数
        long day = (nowTime - birthdayTime) / 1000 / 60 / 60 / 24;
        //返回天数
        return day;
    }
}

2.3.4捕获多个异常

多个异常使用捕获又该如何处理呢?

多个异常一次捕获,多次处理。

一般我们是使用一次捕获多次处理方式,格式如下:

try{
   
     编写可能会出现异常的代码
}catch(异常类型A  e)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值