程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?。
Java提供了更加优秀的解决办法:异常处理机制。
异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。
Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。JDK中内建了一些常用的异常类,我们也可以自定义异常。

Java异常

什么是java异常

在引言中已经介绍了什么是java异常,但是看这些可能不太清晰明白,这里说个小故事:

        案例小故事(此案例仅教学使用,生活中若有雷同,概不
        负责)
        举例:今天天气很好,班长想去爬大蜀山,骑哈罗单车
              去,去山里呼吸新鲜空气
            问题1:山路突然崩塌了,还好班长及时停住,但是过不
            去了,这属于严重问题
            问题2:班长开着一辆哈罗单车,开到一半突然发现单车
            没电了,路途中换一辆开,然后就顺利地
                达到大蜀山。这样的问题应该是在出发之前考虑到
                的。
            问题3:班长正在骑车欣赏风景,发现前面的路不太好
            走,都是小石子,旁边有一个平坦的路
                他偏偏不走平坦的路,就喜欢走小石子上面,结果车
                倒了。
                这样的问题是骑车过程中出现了问题。

Java中的异常

1. 严重的问题

1. 严重的问题:Error,一般情况下,我们不做这样的处理。
             举例今后可能会遇到的这样的问题
	         OOM,Out Of Memory,内存溢出的问题。
2. 异常:Exception
		运行时异常:RuntimeException,这样的问题我们也不处理,因为这样类似的问题一般情况下都是代码不够严谨造成
		的。
		编译时异常:除了不是RuntimeException,都是编译时期
		异常,必须要处理,如果不处理,编译不通过无法运行。
		如果程序出现了问题,我们没有做任何出口i,最终JVM会
		给出一个默认的处理方式,
        把异常类的名称,相关的原因,以及出现问题的相关信息和
        位置信息输出在控制台,同时
         java程序会结束,后面的代码不会执行。
public class ExpectionDemo1{
	public static void main(Stirng[] args){
	//这里会输出当前的时间
		Data data = new Date();
		System.out.println(data);
		//但是我们想转换一下格式,我们需要把格式转成一般的格式
		//在开发中我们经常去实现日期的转换
		2021-12-24 10:57:00

		//HH大小的代表的是24小时刻度
		//hh小写的代表的是12小时刻度
		SimpleDataFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System,out.println(s);
		

		String s1 = “2021-12-24”;
		SimpleDateFormat sdfq = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date1 = null;
		//java.text.ParseException: Unparseable date: "2021-12-24"
		try{
			date1 = sdf1.parse(s1);
		}catch(ParseExpection e){
			e.printStackTrace();
		}
		System.out.println(date1);
	}
}

异常处理的方式

1. try…catch…finally
使用格式
格式1:
try{
	可能会出现的问题的代码
}catch(异常的类名 变量名){
	针对问题的处理
}finally{
无论报不报错都会执行代码
(一般情况下,这里都是放资源的代码)
}

格式2:
try{
	可能会出现问题的代码;
}catch(异常的类名 变量名){
  针对问题的一些处理;
}

格式3:处理多个异常
try{
	可能会出现问题的代码1;
	可能会出现问题的代码2;
	...
}catch(异常的类名1 变量名1){
	针对问题的一些处理;
}catch(异常的类名2 变量名2){
	针对问题的一些处理;
}

多个catch注意事项:

1、能明确异常类型的时候,尽量明确类型,不要用父类大的做处理
2、catch与catch之间的异常是平级关系,多个catch异常之间没有先后顺序关系,一旦出现了一个
    父类继承关系,父必须在最后
3、一旦try里面的代码出现了问题,就会去匹配catch里面的异常,
    继续执行程序try...catch...后面的代码,try里面的代码就停在了报错的那一步。

代码演示:

public class ExceptionDemo2{
	public static void main(String[] args){
		int a =10;
		int b = 0;
		if(b==0){
			System.out.println("除数不能为0");
		}else{
			System.out.println(a/b);
		}
	}

	int[] arr = null;
	
	//String --Date
	String s =  "2021-12-24 14";
	SimpleDateFormat sdf = new SimpleDateDFormat("yyyy-MM-dd HH:mm:ss");
	try{
		Date date = sdf.parse(s);
	}catch (ParseException e){
		System.out.println("日期转换出错了!!!");
	}

	try{
		System.out.println(arr.length);
	}catch(NullPointerException e){
		System.out.println("空指针异常");
	}





}
JDK1.7之后针对多个异常处理新的处理方式:
    try{
        可能会出现问题的代码;
    }catch(异常类名1 | 异常类名2 | ...  变量名){
        处理异常的提示;
    }
    注意事项:
    1、处理方式是一致的,这个方法虽然比较简洁,但是不够好,针多种类型的问题,
        只给出了一种解决方案
    2、catch中多个异常类型之间的关系必须是平级关系,不能存在继承关系
public class ExceptionDemo3{
	public static void main(String[] args){
		int[] arr = null;
			
		//String -- date
		String s = "2021-12-24 14";
		SImplleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try{
			Date date = sdf.parse(s);
			//如果在try里面的代码中间报错了
			//会直接匹配catch里面的异常
			//try中剩下的代码不会执行
			System.out.println(date);
			
			System.out.println("world");
			System.out.println(arr.length);
		
		}catch (ParseException | NullpointerException e){
			System.out.println("报错了");
		}	
		System.out,println("hello");
	}
}
异常处理掌握的方法
  1. getMessage():获取异常信息,返回字符串
  2. toString():获取异常类名和异常信息,返回字符串
  3. printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置,返回值是void
public class ExceptionDemo4{
	public static void main(String[] args){
		int a = 10;
		int b = 0;

		//ArithmeticException
		try{
			System.out.println(a/b);
		}catch(ArithmeticException e){
			//ArithmeticException e = new ArithmeticException();
            //打印的是出现异常的原因
//            System.out.println(e.getMessage());

            //java.lang.ArithmeticException: / by zero
            //异常的类名: 产生问题的原因
//            System.out.println(e.toString());

            //通过观察发现,和我们之前不做任何处理的时候,JVM自动默认处理的打印异常结果是一样的
            //我们不能不处理,如果不处理一旦发生异常,后面的代码不会运行
            //我们做了处理之后,后面的代码才会正常运行
            e.printStackTrace();
		}
		String s = "2021-12-24 14";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date date = sdf.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println("hello");	}
}
2.throws
在今后的开发过程中,有些情况我们根本没有权限去做一些处理异常
    或者说,我们根本就处理不了,干脆就不处理。
    为了解决这样的问题,并且还能保证程序正常运行的情况下
    Java针对这种情况,提供了另外一个解决异常的方式:throws抛出(跟在方法后面)
格式
throws 异常类名
写在方法小括号后面,大括号之前

注意事项:

1、main方法上个尽量不要进行异常抛出,因为程序会停止,后面代码不会执行
            但是,我上课的时候为了方便你们看代码,我就直接抛出,
            做开发的时候,不要在main方法。

        2、编译时期的异常抛出,方法内部不需要做处理,是由将来调用该方法的调用者处理
        3、运行时期异常可以不抛出,但是一旦调用,出错后,后面的代码依旧不会执行
        4、最好抛出一个具体的异常类型,也是推荐这么做的,可以抛出多个异常,用逗号隔开
public class ExceptionDemo{
	public static void main(String[] args){
		        //方法中没有处理异常,调用者必须处理
//        try {
//            fun();
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }

//        fun();

		try {
            fun1();
        }catch (NullPointerException e){
            e.printStackTrace();
        }


        System.out.println("hello");



		public static void fun() throws ParseExpection,NullPointerException{
			String s = "2021-12-24 14";
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date date = sdf.parse(s);
			System.out.println(date);
		}
		public static void fun() throws NullPointerException{
			int[] arr = null;
			System.out.println(arr.length);
		}
	}
}
throw方法
用在方法体内,跟的是 异常对象名
	只能抛出一个异常对象名
	表示抛出异常,由方法体内的语句处理
        throw则是抛出了异常,执行throw则一定抛出了某种异常。
二者区别
throws:
        用在方法的声明后面,跟的是异常的类名
        可以跟多个异常类名,用逗号隔开
        表示的是可能会发生的异常,抛出给调用者处理,表示的是一种可能性,
        不一定会发生这种异常
    throw:
        用在方法体内,跟的是异常对象名
        只能抛出一个异常对象名
        表示抛出异常,由方法体内的语句处理
        throw则是抛出了异常,执行throw则一定抛出了某种异常
public class ExceptionDemo6 {
    public static void main(String[] args) {
        try {
            fun();
        }catch (ArithmeticException e){ //ArithmeticException e = new ArithmeticException()
            e.printStackTrace();
        }


        System.out.println("hello");
    }

    public static void fun(){
        int a = 10;
        int b = 0;

        if(b==0){
            System.out.println("报错,除数不能为0");
            throw new ArithmeticException();
        }else {
            System.out.println(a/b);
        }
    }
}
finally: 最终的意思
        在try...处理异常的时候,末尾通常情况下会添加一个finally
        被它控制的语句体,一定会执行,一般情况下,里面放的是与释放资源相关的代码
    try...catch...finally
public class ExceptionDemo7 {
    public static void main(String[] args) {
        String s = "2021-12-24 14:32:12";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try {
            Date date = sdf.parse(s);
            System.out.println(date);
        }catch (ParseException e){
            e.printStackTrace();
        }finally {
            //无论报不报错都会执行
            System.out.println("这里的代码一定会执行!!!");
        }

    }
}
面试题
面试题:
        final,finally和finalize的区别

        final:最终的意思,可以修饰类,成员变量,成员方法
            修饰类:类不能被继承
            修饰成员变量:变量变常量
            修饰成员方法:方法不能被重写
        finally:是异常处理的一部分,一般情况下用于释放资源的作用,一般情况下都会执行
            特殊情况下: System.exit(0);
        finalize: 是Object类中的一个方法名,用于手动垃圾回收,但是不一定调用就开始回收。

        如果catch里面有return语句,请问finally的代码还会执行吗?
        如果会,请问是在return前还是return后。return之间
public class ExceptionDemo8 {
    public static void main(String[] args) throws ParseException{
//        int a = 10;
//        int b = 0;
//        int c = 0;
//
//        fun();
//
//        try {
//            //让程序停止
            System.exit(0);
//            c = a/b;
//        }catch (ArithmeticException e){
//            e.printStackTrace();
//        }finally {
//            System.out.println("这是finally中的内容");
//        }

        System.out.println(fun());

    }

    public static int fun() {

        int a = 10;
        String s = "2021-12-24 14";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            a =30;
            Date date = sdf.parse(s);
            System.out.println(date);
            a = 40;
        }catch (ParseException e){
            a = 50;
            return a;//50
        }finally {
//            System.out.println(a);
            a = 70;
            int b = 80;
//            return b;
//            return a;
        }

//        System.out.println(a);
//
        return a;


    }


}
异常注意事项:
        1、子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
        2、如果父类抛出了多个异常,子类重写父类时,
            只能抛出相同的异常或者是他的子集,子类不能单独抛出父类没有的异常
        3、如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,
            如果子类方法内有编译时期异常发生,那么子类只能try,不能throws
class A{
    public void fun(){

    }
}

class B extends A{
    @Override
    public void fun(){
        String s = "2021-12-24 14";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date date = sdf.parse(s);
        }catch (ParseException e){
            e.printStackTrace();
        }

    }
}

public class ExceptionDemo9 {
    public static void main(String[] args) throws NullPointerException,ParseException,ArithmeticException {
        B b = new B();
        b.fun();
    }
}