day14【排序算法、异常】

今日内容

  • 选择排序
  • 二分查找
  • 异常处理

教学目标

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

第一章 选择排序

1.1 选择排序概述

  • 另外一种排序的方式,选中数组的某个元素,其后面的元素依次和选中的元素进行两两比较,将较大的数据放在后面,依次从前到后选中每个元素,直至所有数据按要求完成排序
  • 如果有n个数据进行排序,总共需要比较n-1次
  • 每一次比较完毕,下一次的比较就会少一个数据参与

1.2 选择排序图解

需求:将数组中的数据76543做升序排序。

在这里插入图片描述

针对当前的数组:

第一次:

先选择第一个空间,使用第一个空间和身后所有的空间进行比较,在比较的过程中,有哪个空间中的值比第一个空间中的小,就进行交换。直到把最后一个空间比较完之后,那么第一个空间就一定是最小值。

第二次:

选择第二个空间,从身后第三个空间开始依次比较,然后找到一个当前最小的最终保存在第二个空间中。

1.3 选择排序代码实现

选择排序代码实现得思路和步骤:

1)定义一个数组。

2)定义一个函数将选择排序的功能封装到函数中。

3)如果两个空间中需要交换数据,这时需要定义一个临时变量temp来保存选中空间的数据。

4)使用for循环对选中空间的下标进行遍历,这个for循环作为外层循环。

5)使用for循环对选中空间后面的空间的下标进行遍历,这个for循环作为内层循环。

6)使用判断结构对选中空间中的数据和后面的空间中的数据进行比较,如果选中空间中数据大于后面空间中的数据,则交换空间中的数据。如果选中空间中数据小于后面空间中的数据,则不交换空间中的数据。

7)在定义一个函数用来打印数组中的数据。

/*
    选择排序:
        另外一种排序的方式,选中数组的某个元素,其后面的元素依次和选中的元素进行两两比较,将较大的数据放在后面,依次从前到后选中每个元素,直至所有数据按要求完成排序
 */
public class ArrayDemo {
    public static void main(String[] args) {
        //定义一个数组
        int[] arr = {7, 6, 5, 4, 3};
        System.out.println("排序前:" + Arrays.toString(arr));
  		// 这里减1,是控制比较的轮数
        //外循环提供的是当前需要选择的那个空间的下标
        for (int i = 0; i < arr.length - 1; i++) {
            // 从i+1开始,直到最后一个元素
            //内循环提供的是 外循环提供的某个空间身后剩余的所有空间下标
            for (int j = i+1; j < arr.length; j++) {
                //对选中的空间和后面的某个空间进行比较
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}

第二章 二分查找

2.1 普通查找和二分查找

普通查找

原理:遍历数组,获取每一个元素,然后判断当前遍历的元素是否和要查找的元素相同,如果相同就返回该元素的索引。如果没有找到,就返回一个负数作为标识(一般是-1)

二分查找

注意:二分查找对数组是有要求的,数组必须已经排好序

使用二分查找法需要记录数组中三个不同的角标:

​ 1)起始角标:0角标 :start

​ 2)结束角标: 数组.length-1尾角标 :end

​ 3)中间角标: (起始角标+结束角标)/2 : min

对于升序的数组使用折半查找法方法:使用查找的值(key)和中间角标对应的值进行比较,存在三种情况:

1)如果key等于中间角标对应的值,说明已经找到key值,将找到的key值对应的下标返回给调用者

2)如果key大于中间角标对应的值,说明key值在中间角标对应的值后面,所以起始角标向后移动,结束角标不动,即start=min+1,重新计算中间角标,中间角标min=(start+end)/2。

3)如果key小于中间角标对应的值,说明key值在中间角标对应的值前面,所以结束角标向前移动,起始角标不动,即end=min-1,重新计算中间角标,中间角标min=(start+end)/2。

注意,以上三种情况是一个重复的过程,需要使用循环控制循环次数,循环条件是start<=end

2.2 二分查找(折半查找法)图解

折半查找分析图:

需求:数据如下,使用二分查找法查找指定数据在数组中的索引。

2 4 7 9 11 44 60 88 90

在这里插入图片描述

核心代码:

//定义三个变量并给初始化值
int start = 0;//头角标
int end =arr.length-1;//尾角标
int min=(start+end)/2;//中间角标

//假设要找的值x=11 88 4 55
//是一个循环过程,条件是start<=end
while(start<=end){
    1.if(x == arr[min])
    return min;
    2.if(x > arr[min])
      {
        //要找的值在后面,所以start向后移动
        start = min +1;
      }
    3.if(x < arr[min])
      {
         //要找的值在前面,所以end向前移动
        end = min - 1;
      }
    4.//无论是执行第2步还是第3步,都需要更新中间索引
       min = (start+end)/2;
}

2.3 二分查找代码实现

/*
	折半查找
*/
class Demo6 
{
	public static void main(String[] args) 
	{
		//定义数组
		int[] arr={2,4,7,9,11,44,60,88,90};
		//调用自定义的折半查找的函数
		int index=binarySearch(arr,55);
		System.out.println("index="+index);
	}
	/*
		折半查找的方法
		1.有没有返回值?
			有,想要查找数组中某个数据的下标,所以返回要查找数据的下标
			如果数组中没有该数据,则返回-1
		2.有没有参数?
		    有,数组和想要查找的数据
	*/
	//自定义折半查找的函数
	public static int binarySearch(int[] arr,int key)
	{
		//定义三个变量并给初始化值
		int start = 0;//头角标
		int end =arr.length-1;//尾角标
		int min=(start+end)/2;//中间角标
		//使用循环往复控制折半
		while (start<=end)
		{
			//对中间的值和指定的值进行比较
			if (key==arr[min])
			{
				return min;
			}
			//指定的值大于中间的值
			if (key>arr[min])
			{
				//头角标向后移动
				start=min+1;
			}
			//指定的值小于中间的值
			if (key<arr[min])
			{
				//尾角标向前移动
				end=min-1;
			}
			//如果头角标和尾角标发生变化,要时刻更新中间角标
			min=(start+end)/2;
		}
		//循环结束,说明没有找到指定的数据
		return -1;
	}
}

第三章 异常

3.1 异常概念

异常:不正常。

生活中的异常:

例:在上课时,突然间停电,造成上课终止。 处理:等待来电、使用备用发电机。

程序中的异常:

程序在运行的过程,出现了一些突发状况,造成程序无法继续运行。我们把上述的突发状况,无法正常运行的这些状态称为Java中的异常。

Java中的异常:就是程序中出现的错误(bug),或者不正常现象。而我们在开发程序的时候,就需要对这些问题进行预先的判断和处理。

学习Java中的异常,我们需要研究:

1、什么是异常;

2、异常问题怎么去解决和处理;

3、我们自己怎么把问题报告给程序的使用者;

我们编写程序的时候,在程序中肯定会有问题(bug)出现,而sun公司把开发中最最常见的一些问题,进行总结和抽取,形成了一个体系,这个体系就是我们要学习的异常体系。

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

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

需求:根据用户指定的下标,输出该下标对应的数据。

1)定义一个类ExceptionDemo;

2)定义一个主方法,在主方法中定义一个int类型的数组,在这个数组中存放int类型的数据;

3)在这个类中自定义一个函数getValue,在这个函数中根据调用者传递过来的下标和数组返回给调用者

对应的数据;

4)在主方法中调用自定义函数getValue,接收自定义函数返回回来的数据,并将数据打印到屏幕上;

在这里插入图片描述

说明:上述代码发生异常的过程:

1)jvm先去调用main函数,在main函数中调用了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需要对这个问题进行处理,即将这个问题显示到屏幕上,让程序的使用者看到。

3.3 异常简单应用举例

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

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

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

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

/*
	针对发生的异常进行简单的处理
*/
class ExceptionDemo1
{
	public static void main(String[] args) 
	{
		//定义数组
		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 NullPointerException("对不起,数组引用变量的值不能为null");
		}
		if(index<0 || index>=arr.length)
		{
			throw new ArrayIndexOutOfBoundsException("下标越界了。。。");
		}
		return arr[index];
	}
}

3.4 异常体系

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

异常的根类是java.lang.Throwable,Throwable这个类描述的是Java中所有异常和错误的共性内容。其下有两个子类:java.lang.Errorjava.util.Exception,平常所说的异常指java.util.Exception

在这里插入图片描述
Throwable体系:

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

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

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

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

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

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

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

3.5 异常分类

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

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

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会让编译器检测(不报错)。

在这里插入图片描述

小结:

 RuntimeException和Exception有什么区别:

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

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

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

第四章 异常的处理

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

4.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[] args) {
        //创建一个数组 
        int[] arr = {2,4,52,2};
        //根据索引找对应的元素 
        int index = 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 ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
        }
        int element = arr[index];
        return element;
    }
}

注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。

那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。

4.2 声明异常throws(掌握)

声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

声明异常格式:

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

注意:

​ 1)throws后面可以跟多个异常类,使用逗号分隔;

    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函数通常是使用捕获。

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

4.3.1格式

就是遇到异常时,不再把异常交给他人处理,自己处理。

在程序中有异常,但这个异常我们不能继续使用throws声明,这时不处理,程序无法编译通过,那么在程序中只能使用捕获方式解决问题。

格式如下:

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

**try:**该代码块中编写可能产生异常的代码。

**catch:**用来进行某种异常的捕获,实现对捕获到的异常进行处理。

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

演示如下:

public class ThrowDemo {
	 /*
     * 一般在开发中我们不会在主函数上面抛异常,主函数一般是最后处理者,
     * 我们需要在主函数中对异常进行处理------》捕获
     */
    public static void main(String[] args){

        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) throws Exception {
        if (x == 0) {//如果x等于0
           
            throw new Exception("x不能等于0");
        }
        System.out.println(x);
    }
}

总结:不管自己是函数的定义者,还是函数的调用者,只要是在自己的函数中有异常发生, 那么自己都可以使用上述的两种方案对异常进行处理。

4.3.2如何获取异常信息:

Throwable类中定义了一些查看方法:

异常中的常用方法

方法说明
String getMessage()获取报错原因.
String toString()获取报错的类型和原因
printStackTrace()直接打印报错的类型、原因和位置.包含了异常的类型,异常的原因,还包括异常出现的位置.

代码演示:

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执行完程序会继续往后执行
    }
}

4.3.3捕获多个异常

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

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

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

try{
     编写可能会出现异常的代码
}catch(异常类型A  e){try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

4.4 finally 代码块(掌握)

finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

什么时候的代码必须最终执行?

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

finally的语法:

try…catch…finally:自身需要处理异常,最终还得关闭资源。

try{
	   可能发生异常的代码
   }catch(异常类名  变量名){
	  处理异常的代码。
	}finally{
	 程序中永远都能执行到的代码
  }

在程序中随时都有可能发生异常,一旦程序中发生了异常,这样就会导致在发生异常的地方之后的所有代码都不会运行。可是在程序中,有时有些代码不管程序中有没有问题都必须执行。这时这部分代码必须写在finally代码块中。

注意:finally不能单独使用。

比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

finally代码参考如下:

需求:

1)创建一个Test类;

2)定义一个Demo1类,在这个类中定义一个show函数,根据传递进来的参数x进行判断,如果x等于0,则使用throw关键字抛异常Exception;

3)对这个异常我们不使用声明处理,我们使用捕获进行处理,即使用try-catch-finally进行处理;

4)并分别在try-catch-finally的代码中使用return关键字返回给调用者1,2,3;

5)在Test类中创建Demo1对象,并使用对象调用类中的show函数,并打印返回来的值;

package cn.itcast.sh.b_excep_other;
class Demo1
{
	public int show(int x)
	{
		try
		{
			if(x==0){
				throw new Exception("x是零");
			}
			System.out.println("try.....");
			return 1;
		}catch(Exception e)
		{
			System.out.println("捕获异常");
			return 2;
		}finally
		{
			System.out.println("必须执行");
			return 3;
		}
	}
}
public class Test {
	public static void main(String[] args) {
		Demo1 d = new Demo1();
		int value=d.show(0);
		System.out.println(value);
	}
}

说明:在上述代码中,因为传入show函数中的值是0,所以发生异常,那么在jvm执行catch中的return 2代码的时候,jvm发现此时下面有finally关键字,那么jvm先不会执行return 2语句,jvm会先去执行finally中的代码,因为此时finally中有return 3语句,在函数中,遇见return关键字就会结束整个函数,此时jvm不会回来执行return2语句,如果在finally代码块中没有return语句,那么jvm在执行finally代码块里面的代码之后就又会回到catch中继续执行return 2语句。

finally代码块是永远都会被执行的代码,不管程序发生什么问题,最后JVM一定会把finally中的代码执行一遍。

在这里插入图片描述

使用场景

之后会学习IO流,IO流就相当于水龙头。

打开水龙头之后,中间不管是做了什么操作,最后都一定要关闭水龙头。(节约资源)

关闭水龙头(节约资源)的代码就可以写在finally

4.5 异常注意事项

  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。

  • 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

    需求:

    ​ 1)创建类Test2;

    ​ 2)定义一个Fu类和Zi类,在Fu类中定义一个method函数;

    ​ 3)在Zi类中复写method函数,在这个函数中抛Exception异常;

    ​ 4)在类Test2中创建Zi类对象,通过子类对象调用子类中的method函数;

    ​ 5)在Zi类中的method中捕获异常;

package cn.itcast.sh.b_excep_other;
class Fu
{
	public void method()
	{
		System.out.println("父类中的method");
	}
}
class Zi extends Fu
{
	public void method() //throws Exception
	{
		System.out.println("子类重写方法method");
		try {
			throw new Exception("异常");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Test2 {
	public static void main(String[] args) {
		Zi z=new Zi();
		z.method();
	}
}
  • 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。

  • 如果finally有return语句,永远返回finally中的结果,避免该情况.

    需求:

    1)创建一个Test类;

    2)定义一个Demo1类,在这个类中定义一个show函数,根据传递进来的参数x进行判断,如果x等于0,则使用throw关键字抛异常Exception;

    3)对这个异常我们不使用声明处理,我们使用捕获进行处理,即使用try-catch-finally进行处理;

    4)并分别在try-catch-finally的代码中使用return关键字返回给调用者1,2,3;

    5)在Test类中创建Demo1对象,并使用对象调用类中的show函数,并打印返回来的值;

    package cn.itcast.sh.b_excep_other;
    class Demo1
    {
    	public int show(int x)
    	{
    		try
    		{
    			if(x==0){
    				throw new Exception("x是零");
    			}
    			System.out.println("try.....");
    			return 1;
    		}catch(Exception e)
    		{
    			System.out.println("捕获异常");
    			return 2;
    		}finally
    		{
    			System.out.println("必须执行");
    			return 3;
    		}
    	}
    }
    public class Test {
    	public static void main(String[] args) {
    		Demo1 d = new Demo1();
    		int value=d.show(0);
    		System.out.println(value);
    	}
    }
    

第五章 自定义异常

5.1 概述

为什么需要自定义异常类:

我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。,例如年龄负数问题,考试成绩负数问题。

在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢?

什么是自定义异常类:

在开发中根据自己业务的异常情况来定义异常类.

自定义一个业务逻辑异常: LoginException。一个登陆异常类。

异常类如何定义:

  1. 自定义一个编译期异常: 自定义类 并继承于java.lang.Exception

  2. 自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException

    异常也是一个类,那么就和我们学习面向对象中定义类没有区别:

    格式:

    public class 异常类的名字  extends Exception / RuntimeException
    
    {
    
    	//不需要任何的属性和行为,仅仅只需要提供构造函数即可。
    	public 异常类的名字(){}
    	public 异常类的名字( String message ){
    	super(message);
    	}
    }
    

注意:不需要任何的属性和行为,仅仅只需要提供构造函数即可。

查看异常源码

public class NullPointerException extends RuntimeException {
    //版本号 没什么用
    private static final long serialVersionUID = 5162710183389028792L;
	//构造方法
    public NullPointerException() {
        super();
    }
	//构造方法
    public NullPointerException(String s) {
        super(s);
    }
}
  • 通过查看源码我们发现,异常类中什么都没有定义。

  • 不同的异常只是类名不同而已,内部并没有特殊的方法。类名不同方便程序员识别不同的错误。

  • 自定义异常的作用

    • 自定义异常的作用就是在出现异常时,让异常的名字更加直观。

5.2 自定义异常的练习

练习:

需求:定义一个类,描述矩形,提供计算面积的功能。要求对长和宽进行判断, 如果非法直接抛出长或宽非法异常。

分析和步骤:

1)定义一个类Rectangle描述矩形,在这个类中分别定义长length和宽width两个私有属性,并对外提供get和set方法;

2)在Rectangle类中定义一个构造函数分别给长length和宽width初始化值;

3)在这个类的构造函数中分别对length和width两个属性进行判断,如果长和宽不合法,分别对长和宽进行抛异常,同时构造函数要声明异常;

4)定义一个类IllegalWidthException对非法的宽进行异常处理,在这个类中分别定义无参构造函数和有一个参数的构造函数,同时这个类需要继承Exception类,这样在编译的时候就可以检测异常;

5)定义一个类IllegalLengthException对非法的长进行异常处理,在这个类中分别定义无参构造函数和有一个参数的构造函数,同时这个类需要继承Exception类,这样在编译的时候就可以检测异常;

6)在Rectangle类中定义一个计算矩形面积的函数getArea,将最终的面积值返回给调用者;

7)定义一个异常测试类ThrowTest,在这个测试类中创建矩形类Rectangle的对象,并通过对象调用计算圆的面积的函数getArea并打印,同时并对异常进行捕获处理;

/*
 * 需求:定义一个类,描述矩形,提供计算面积的功能。
 * 要求对长和宽进行判断, 如果非法直接抛出长或宽非法异常。
 */
//自己定义长和宽非法的异常
class IllegalWidthException extends Exception{

	public IllegalWidthException() {
		super();
	}

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

//非法的长度异常
class IllegalLengthException extends Exception{

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

//描述矩形
class Rectangle{
	//长和宽
	private double length;
	private double width;
	
	public Rectangle(double length, double width) throws IllegalLengthException, IllegalWidthException {
		//对长和宽进行合法的验证
		if( length <= 0 ){
			throw new IllegalLengthException("非法的矩形长度");
		}
		
		if( width <= 0 ){
			throw new IllegalWidthException("非法的矩形宽度");
		}
		this.length = length;
		this.width = width;
	}
	
	public double getLength() {
		return length;
	}
	public void setLength(double length) {
		this.length = length;
	}
	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	
	//计算面积
	public double getArea(){
		return this.length * this.width;
	}
	
}

public class ThrowTest {
	public static void main(String[] args) {
		
		//创建矩形对象
		try {
			Rectangle r = new Rectangle(-3,4);
			
			System.out.println(r.getArea());
			
		} catch ( IllegalWidthException e) {
			e.printStackTrace();
		} catch ( IllegalLengthException e) {
			e.printStackTrace();
		}
		
	}
}

5.3 关于捕获多个异常注意事项

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

try{
     编写可能会出现异常的代码
}catch(异常类型A  e){try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

关于捕获多个异常注意事项代码演示如下:

需求:

1)定义一个Demo类,在这个类中分别定义主函数和一个show函数;

2)在分别定义两个异常类A和B;

3)A类继承Exception类,并创建两个构造函数;

4)B类继承A类,并创建两个构造函数;

5)在Demo类中的主函数中调用show函数,并传参0;

6)在Demo类中的show函数中分别写三个判断语句,使用throw关键字分别抛出B、A和Exception类三个异常类的对象,同时在show函数声明三个异常;

7)这样在main函数中就得对异常语句(调用show函数语句)进行捕获异常处理;

package cn.itcast.sh.b_excep_other;
//自定义异常类
class A extends Exception
{
	public A() {
		super();
	}
	public A(String message) {
		super(message);
	}
}
class B extends A
{
	public B() {
		super();
	}
	public B(String message) {
		super(message);
	}
}
public class Demo {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try
		{
			show(0);//new Exception() new A() new B()
			//这里可能发生A B Exception异常中的某一个异常
			/*
			 * Exception e =new Exception() 
			 * Exception e=new A()
			 * Exception e=new B()
			 */
		}catch(B b)
		{
			
		}catch(A a)
		{
			
		}catch(Exception e)//
		{
			
		}
	}
	public static void show(int x) throws B,A,Exception
	{
		if(x==0)
		{
            throw new B();
		}
		if(x==1)
		{
			throw new A();
		}
		if(x==2)
		{
			throw new Exception();
		}
		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃 哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值