Java 笔记 15:异常

异常

异常:

  • 不正常
  • 在某些情况下,因为用户的一些原因,例如:配置问题、错误输入的问题、用户磁盘空间满了等因素,导致程序无法正常运行。
  • 不是异常:(1)语法错误(2)逻辑错误
  • Java的程序如果出现异常,并且在代码中,没有做相应的处理,那么程序会“崩溃”,“挂了”
  • Java如何处理异常?或Java异常处理的过程?
  • (1)当程序运行到某一句时,发生了异常,那么程序会先停下来
  • (2)程序会在这句代码处,查看原因,生成一个合理“异常对象”,然后“抛”出
  • (3)JVM会检测在这句代码的外围,是否有try…catch结构,可以“捕获”它,
  • 如果可以捕获,那么程序再处理完异常后,继续下面的运行,不会崩溃;
  • 如果不能捕获,那么会把这个异常继续抛给“上级”,如果“上级”能处理,那么程序从“上级"处理完的代码后面继续运行;
  • 如果上级也不能处理,那么继续往上抛,一直到达JVM,那么就“崩溃”

所有类的根父类是Object。

  • 枚举的公共父类是Enum,根父类仍然是Object
  • 异常的公共父类是Throwable,根父类仍然是Object
  • 1、异常的公共父类:java.lang.Throwable
  • (1)只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句“抛”出。
  • (2)只有此类或其子类之一才可以是 catch 子句中的参数类型。
  • 2、Throwable又分为两大派别:
  • (1)Error:错误
  •  一般指严重错误,一般合理的应用程序不应该试图去捕获它。
    
  •  如果出现这个问题,要么需要升级程序,要么需要升级架构,要么需要升级硬件。
    
  •  例如:报了一个OutOfMemoryError
    
  •  经典代表:VirtualMachineError(堆内存溢出OutOfMemoryError,栈内存溢出StackOverflowError)
    
  • (2)Exception:异常
  •  一般异常,合理的应用程序应该试图去捕获它。
    
  • 3、Exception还可以分为两大类:
  • (1)运行时异常(RuntimeException或它子类):又称为非受检异常
  •  编译时,编译器是不会提醒你做处理的,只有运行期间,才会发生。
    
  •  运行时异常是不建议用try...catch,因为它发生频率太高,而且一般都是很不应该发生的问题。
    
  •  例如:空指针异常,数组下标越界异常,类型转换异常等,这些异常完全可以避免掉。
    
  •  但是如果实在没有考虑到,也可以通过try...catch处理。
    
  • (2)编译时异常,除了RuntimeException系列以外的,都是编译时异常。又称为受检异常。
  •  编译时,编译器会强制要求程序员编写处理的代码,如果你不编写,那么就编译不通过。
    
  •  例如:FileNotFoundException,IOException等
    

try…catch

一、异常的处理方式:try…catch

  • 1、语法格式:
  • try{
  •  可能发生异常的代码
    
  • }catch(异常类型1 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }catch(异常类型2 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }catch(异常类型3 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }
  • 。。。。
  • 2、异常对象的常用方法
  • (1)e.printStackTrace();
  • 打印异常的详细信息,包括追踪跟踪信息,即这个异常对象一路经过了哪些方法
  • (2)e.getMessage();
  • 返回异常对象中简单的错误信息提示
  • 3、打印异常/错误信息
  • System.err.println(xx);打印错误信息
  • System.out.println(xx);打印正常信息
  • 4、多个catch分支,如何匹配和执行的?
  • 从上到下依次判断,一旦有一个满足,后面就不看了。
  • 建议:如果多个catch中的异常类型有大小包含关系,那么小的在上,大的在下,如果没有大小包含关系,顺序随意。
  • 5、如果catch,可以捕获try中发生的异常,那么程序,会从try…catch下面的代码继续运行 ,不会崩溃。
  • 如果catch无法捕获try中发生的异常,那么就会导致当前方法结束,并把异常对象抛出调用者,
  • 如果调用者可以处理,那么从调用者处理代码的后面继续运行,否则继续往上抛,最终到达JVM,程序就崩溃了。

finally

二、try…catch的形式二

  • try{
  •  可能发生异常的代码
    
  • }catch(异常类型1 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }catch(异常类型2 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }catch(异常类型3 异常对象名){//异常对象名绝大多数都是写e
  •  处理这个异常的代码
    
  • }
  • 。。。。
  • finally{
  •  不管try中是否发生异常,也不管catch是否可以捕获异常,这里代码都必须执行
    
  • }
  • 一般用于编写释放资源,断开连接等代码
  • 特殊情况:可以没有catch部分
  • try{
  • }finally{
  • }

finally与return混用:
*

  • (1)不管try中是否发生异常,也不管catch是否可以捕获异常,也无论try或catch中是否有return。
  • finally中的代码都必须执行
  • (2)如果finally中有return,就从finally块的的return回去。

finally与return混用:
*

  • (1)不管try中是否发生异常,也不管catch是否可以捕获异常,也无论try或catch中是否有return。
  • finally中的代码都必须执行
  • (2)如果finally中有return,就从finally块的的return回去。
  • (3)如果finally中没有return,那么先把try或catch中该执行的执行完,
  • 在return结束当前方法之前,先走一下finally,然后回去结束当前方法

finally与return混用:
*

  • (1)不管try中是否发生异常,也不管catch是否可以捕获异常,也无论try或catch中是否有return。
  • finally中的代码都必须执行
  • (2)如果finally中有return,就从finally块的的return回去。
  • (3)如果finally中没有return,那么先把try或catch中该执行的执行完(包括把返回值的结果放到要带回调用处的操作数栈的位置)
  • 在return结束当前方法之前,先走一下finally,然后回去结束当前方法.
  • 结论,如果finally中没有return,finally中的代码不影响返回值。
public class TestFinallyNoReturn2 {
	public static void main(String[] args) {
		int num = getNum(4);
		System.out.println(num);//0
	}
	
	public static int getNum(int a){
		int result = 10;
		try{
			System.out.println(a/0);
			if(a > 0){
				result = 20;
				return result;
			}else if(a < 0){
				result = -20;
				return result;
			}else{
				return result;
			}
		}catch(Exception e){
			System.out.println("exception");
			result = 0;
			return result;
		}finally{
			result = 30;
			System.out.println("finally");
//			return result;//如果有这句,结果就变成30
		}
	}
}

实际开发当中,不在finally里面加return

/*
 * 1、练习1
从键盘输入两个整数a,b,求两个数的和、差、积、商
并且尝试使用try...catch处理可能发生的异常

快捷键:Alt +Shift  + Z  弹出surround with xxx
 */
public class TestExer {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		try {
			System.out.print("请输入一个整数:");
			int a = input.nextInt();
			
			System.out.print("请输入另一个整数:");
			int b = input.nextInt();
			
			System.out.println("和:" + (a+b));
			System.out.println("差:" + (a-b));
			System.out.println("积:" + (a*b));
			System.out.println("商:" + (a/b));
		} catch (InputMismatchException e) {
			System.err.println("输入错误,应该输入整数");
		} catch (ArithmeticException e) {
			System.err.println("除数不能为0");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			input.close();//资源关闭
		}
	}
}
/*
 * 1、练习1
从键盘输入两个整数a,b,求两个数的和、差、积、商
并且尝试使用try...catch处理可能发生的异常

快捷键:Alt +Shift  + Z  弹出surround with xxx

增加要求:如果输入有问题,提示错误信息后重新输入,如果没问题,就不重新输入
 */
public class TestExer2 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		while(true){
			try {
				System.out.print("请输入一个整数:");
				int a = input.nextInt();//"a"
				
				System.out.print("请输入另一个整数:");
				int b = input.nextInt();
				
				System.out.println("和:" + (a+b));
				System.out.println("差:" + (a-b));
				System.out.println("积:" + (a*b));
				System.out.println("商:" + (a/b));
				break;
			} catch (InputMismatchException e) {
				System.err.println("输入错误,应该输入整数");
				String str = input.nextLine();//读取当前行所有内容,我没有接受,就表示丢弃了
				System.out.println(str);
			} catch (ArithmeticException e) {
				System.err.println("除数不能为0");
			} catch (Exception e) {
				e.printStackTrace();
			} 
		}
		
		input.close();
	}
}

(1)finally里面有return,就从finally的return回去了

  • (2)类初始化:main所在的类要先初始化,才能执行main方法
  • 由①静态变量的显示赋值(这里没有)②静态代码块
  • (3)实例初始化:必须要new对象才会有,这里没有创建TestExer4的对象,所以不走
  • 结果:a
  •    3
    
public class TestExer4 {
	{
		System.out.println("b");
	}
	static{
		System.out.println("a");
	}
	TestExer4(){
		System.out.println("c");
	}
	public static String getOut(){
		try{
			return "1";
		}catch(Exception e){
			return "2";
		}finally{
			return "3";
		}
	}
	public static void main(String[] args) {
		System.out.println(getOut());//3
	}
}

面试题:finally,final,finalize的区别
(1)finally:最终块
无论try中是否有异常,无论catch是否可以捕获异常,也不管try和catch中是否有return,
都一定要执行的部分
(2)final:是一个修饰符
修饰类:表示不能被继承
修饰方法:表示不能被重写
修饰变量:表示常量,值不能修改
(3)finalize:是一个方法名,在Object类中声明的,当对象被GC回收之前调用

Throws

异常处理的方式之一:

  •  在当前方法中直接用try...catch处理
    
  • 异常处理的方式之二:
  •  在当前方法中不处理,扔/抛给调用者处理
    
  • throws的好处:
  • (1)throws:告知被调用者,我这个方法可能会抛出哪些异常,使得调用者可以明确的知道应该catch什么异常
  • 如果没有throws,那么调用者就不清楚,只能按照Exception处理,或者根据错误经验来处理。
  • (2)编译时异常,如果在当前方法中不用try…catch处理,编译不通过,那么可以通过throws明确的说明,抛给调用者处理
  • throws的语法格式:
  • 【修饰符】 返回值类型 方法名(【形参列表】)throws 异常列表们{
  • }
  • 说明:throws后面可以跟好几个异常,顺序无所谓,每一个异常之间使用,分割
  • 关于方法重写时,对throws抛出的异常的要求:
  • 子类重写的方法抛出的异常类型必须<=父类被重写的方法抛出的异常类型。
  • 例如:Exception > RuntimeException > ArrayIndexOutOfBoundsException
  • 整理重写的要求:
  • (1)方法名:相同
  • (2)形参列表:相同
  • (3)返回值类型:
  •  基本数据类型与void:相同
    
  •  引用数据类型:<=
    
  • (4)修饰符
  • 权限修饰符:>=
  • 其他修饰符:不能是final,private,static
  • (5)抛出的异常类型:<=

Throw

异常的对象的创建和抛出有两种方式:

  • (1)JVM创建并抛出
  • (2)程序员new出来,然后由throw抛出。
  • Throwable:
  •  只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
    
  •  类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。 
    
  • throw:
  • 用于手动抛出异常对象。
  • 语法格式:
  • throw 程序员new的异常对象;
  • 可以代替return语句,结束当前的方法
  • 面试题:throw和throws什么区别?
  • (1)throw用于手动抛出异常对象,是个可执行的语句
  • (2)throws,在方法签名中,声明方法可能抛出什么异常,让调用者来处理这些异常。

自定义异常

自定义异常

  • 如果系统预定义的异常类型,
  •  例如:ArrayIndexOutOfBoundsException
    
  •  	ClassCastException
    
  •  	NullPointerException
    
  •  	ArithmeticException
    
  •  	InputMisMatchException
    
  •  	IllegalAugumentException
    
  •  	....
    
  • 发现不能准确的表达你当前的异常类型的意思时,你可以选择自定义。
  • 面试题:列出常见的异常类型,已经什么情况下会发生这个异常,你如何处理?
  • 至少5个
  • 1、自定义的要求:
  • (1)必须继承Throwable或它的子类
  • 但是实际开发中,一般继承RuntimeException和Exception
  • (2)建议大家保留两种构造器的形式
  • ①无参构造
  • ②带给父类的message属性赋值的构造器
  • 2、如何使用自定义异常
  • 只能使用throw语句进行手动抛出。它不能由JVM自动抛出。
  • 3、建议
  • 大家在自定义异常时,异常的类型名非常重要,见名知意。

猜你喜欢

转载自blog.csdn.net/qq_40473204/article/details/107573215