来源:https://www.cnblogs.com/Burning-youth/p/15582107.html
-
【JAVA】笔记(11)--- 精讲异常(附枚举);
异常:
一.什么是异常?异常处理机制有什么作用?
程序在执行时发生了我们不想发生的异常情况,这种情况叫做异常;java语言的特性之一 --- 健壮性的实现主要就是靠异常处理机制,当程序执行过程中发生了异常,系统将异常信息打印到控制台上,程序员根据异常信息就可以对程序进行进一步的修改,进而使程序更加的健壮;
注意:异常也是一种类,系统内置有很多不同的异常类对应不同的异常情况,我们同样可以根据我们的需求来定义不同的异常类,来处理不同的异常情况;
二.异常的体系结构:
Throwable:java 中所有异常和错误的父类;
Error:程序员无法解决处理的错误情况(跟咱们没关系,不用管);
Exception: 程序本身可以捕获并且可以处理的异常情况;其中又分为运行时异常(RuntimeException)(又称作不受检异常)和编译时异常(IOException)(又称作受检异常);
运行时异常:空指针异常,数组下标越界异常等;运行时异常我们一般不利用异常处理机制对其进行处理,解决办法就是直接修改代码;
编译时异常:编译时异常必须对其处理否则无法运行;
三.异常的抓抛:
1.异常的抛出:
如果程序在运行过程中执行某段代码时发生了异常,那么系统(JVM)将会根据异常的类型,在异常发生处创建对应的异常类型的对象并抛出,抛出给程序的调用者,并且本段代码所属的方法体中异常后的代码将不再运行;
注意:这里的异常有可能是系统内置的异常,也有可能是我们手动抛出(throw)的的异常(我们定义的异常类);
2.异常的抓取:
对于异常的发生,有俩种处理方式。第一种,调用“ try-catch-finally ”语句来抓取异常;第二种,上抛(throws)给异常发生的方法的调用者;
四.try-catch-finally:
1.try 方法体内放置可能会出现异常的代码,如果异常发生,就会 new 出一个异常类,此异常类会与 catch 的异常类型进行匹配,匹配成功,执行 catch 方法体内的代码(代码一般为打印异常的基本信息或打印异常的详细信息),匹配不成功,继续匹配后面不同的 catch ,最后finally 方法体内的代码最终一定会执行;
2.catch 多个异常类时,如果存在有继承关系的类,一定要按从子类到父类的顺序来 catch,因为如果第一个就 catch 最大的父类,那么可能执行的异常处理代码并非我们想要的;如果无继承关系,自然顺序就无所谓了;
3.catch 方法内常用的异常处理方法:
1) 异常 . getMessage ( ) ;
// 返回值为异常的基本信息(throw new 异常 ( "返回值就是这里的字符串哟" ) );一般搭配 System . out . println ( ) 打印输出信息;
2) 异常 . printSrackTrace ( ) ;
// 打印异常的详细信息(堆栈追踪信息),如:
com.bjpowernode.javase.day1.WeaponIndexOufException: 武器库装备已达上限,[Lcom.bjpowernode.javase.day1.Weapon;@7f31245a无法载入武器库系统
at com.bjpowernode.javase.day1.Army.addWeapon(Army.java:19)
at com.bjpowernode.javase.day1.武器系统.main(武器系统.java:12)
//第一行为异常的基本信息;
//第二行为异常出现的地方(是第三行中的异常出现的果);
//第三行也就是最后一行,为第一个异常出现的地方,是第二行异常出现的因;
注意:异常的详细信息要从下往上看,因为异常一般会存在因果关系;
4.finally 内的代码优先级仅次于 System . exit ( 0 ) ;
//强制关闭退出 JVM ,所以 finally 内的代码不会执行;即使try内有 return 语句,在try方法强制结束之前, 也会先执行 finally 内的代码;
注意:
1)在 try 内声明的变量,出了 try ,系统就不认识了,解决方法是在 try 外声明变量并初始化,在 try 内赋值,所以要理解 try 内装的是一个“ 模块 ”,一个可能会出现异常的代码模块;
2)try-catch-finally 也可以嵌套使用;
2)有 try ,必须有 finally / catch ,finally,catch 也不可以离开 try ;
五.throw -throws , try-catch-finally:
1.throw:手动抛出异常;一般这样使用:
throw new XXXException("异常基本信息");//XXX代表是什么异常
2.throws:将“ 抛出的异常 ”捕获并上交给方法的调用者;可以 throws 多个异常;一般这样使用;
public void fangFa () throws XXXException{ //throws 的异常即为 方法体内 throw的异常
throws new XXXException("异常基本信息");
}
3.对于“ 抛出(throw)的异常 ” 有俩种处理方式:
第一种,继续上抛给方法的调用者,但其实并没有真正的解决异常,如果一直上抛到 main 方法,都没有 try catch 异常,main 方法会继续上抛到JVM,然后程序到此结束;第二种,直接在异常发生的方法内进行try catch 处理;
那么在日常开发中,究竟该如何选择俩者的处理方式呢?
1)第一种情况,异常发生方法为子类重写父类的方法,然而对应的父类方法并没有 throws 异常,所以子类中重写的方法就不能选择 throws 了,只能被迫在此方法中就地解决(try - catch);
2)第二种情况,异常发生的方法属于被调用的方法,而且调用此方法的方法也是被调用的,上面还有更多更多的方法,一直到最初的执行方法,这种存在层层递进,相互关联的方法,一般选择将所有异常都 thows 给最初的调用者方法,然后在此方法中统一对所有异常进行处理;
3)注意: try-catch 和 throws 在方法中不要同时使用,因为只要使用 try-catch 就已经将异常处理掉了,再 throws 没有任何意义;
六.栗子老师:
public class pra {
public static void main(String[] args) {
People p=new People("王狗蛋");
Food food=new Food("粑粑");
try{
p.eat(food);
}catch (ErrorFoodException e){
System.out.println(e.getMessage());
}
}
}
class People{
String name;
public People() {
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat(Food food) throws ErrorFoodException{
if ("粑粑".equals(food.foodName)){
throw new ErrorFoodException("人不能吃"+food+"!");
//System.out.println("sdsas"); 异常抛出后不能写任何代码,因为根本到不了,没有意义
}
//if 语句外就不受异常抛出的影响了,因为系统认为 if() 语句不一定执行,所以if()外的代码是有意义的
System.out.println(name+"正在吃"+food);
}
}
class Food{
String foodName;
public Food() {
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public Food(String foodName) {
this.foodName = foodName;
}
public String toString() {
return foodName;
}
}
class ErrorFoodException extends Exception{
public ErrorFoodException() {
}
public ErrorFoodException(String message) {
super(message);
}
}
运行结果:
--------------------------------
人不能吃粑粑!
Process finished with exit code 0
枚举:
1)概念:枚举属于引用数据类型,当结果为两种情况的时候,用布尔型,当结果有3种或3种以上且可以一枚一枚将结果列举出来,就需要定义枚举来进行使用;
2)定义枚举:enum 枚举名 { 枚举值1,枚举值2,枚举值3,枚举值4,...... }
3)注意:每个枚举值都可以看作是一个常量,所以枚举值一般都用大写字母表示 ;
目前版本的 JDK,switch 语句支持 int,String,枚举,但我们一般不这么使用枚举;
4)栗子老师:(此程序写的有些笨重,目的是为了更好的理解枚举)
import java.util.Random;
public class pra{
public static void main(String[] args) {
Random random=new Random();
int i=random.nextInt(101);
Result result=power(i);
if (result==Result.S){
System.out.println("危险等级:S");
}else if (result==Result.A){
System.out.println("危险等级:A");
}else if (result==Result.B){
System.out.println("危险等级:B");
}else if (result==Result.C){
System.out.println("危险等级:C");
}else System.out.println("危险等级:D");
}
public static Result power(int i){
if (i>=95){
return Result.S;
}else if (i<95&&i>=85){
return Result.A;
}else if (i<85&&i>=70){
return Result.B;
}else if (i<70&&i>=50){
return Result.C;
}else return Result.D;
}
}
enum Result{
S,A,B,C,D
}
运行结果:
------------------------------
危险等级:D
Process finished with exit code 0
随笔:
面试题:输出几?
public class pra {
public static void main(String[] args) {
int Result=m();
System.out.println(Result);
}
public static int m(){
int i=100;
try {
return i;
}finally {
i++;
}
}
}
点击查看结果
结果是不是很意外?为什么不是 101 呢?这里接触到了 java 中的语法规则(语法规则是 java 里优先级最高的,任何代码都必须遵循规则)
原则一:方法里中的代码必须遵循自上而下的顺序依次执行;
原则二:return 语句一旦执行,整个方法必须结束;
所以综合原则一与原则二,既然系统识别到了 return 语句就必须执行,但是结束方法之前,必须先执行 finally 中的“ i++ ”,虽然此时 i 的值确实等于 101 ,但是 return 要遵循原则一,不能输出新 i ,只能输出老 i = 100 ;
其实就像于:
int i=100;
int j=i++; //i 确实等于101
return j; //但是系统被迫只能输出 j !!!