枚举概述
类的对象只有有限个,确定的。举例如下:
- 星期:Monday(星期一)、......、Sunday(星期天)
- 性别:Man(男)、Woman(女)
- 季节:Spring(春节)......Winter(冬天)
- 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)
- 就职状态:Busy、Free、Vocation、Dimission
- 订单状态:Nonpayment(未付款)、Paid(已付款)、Delivered(已发货)、Return(退货)、Checked(已确认)Fulfilled(已配货)、
- 线程状态:创建、就绪、运行、阻塞、死亡
枚举类的实现
JDK1.5之前需要自定义枚举类
- 私有化类的构造器,保证不能在类的外部创建其对象
- 在类的内部创建枚举类的实例。声明为:public static final
- 对象如果有实例变量,应该声明为private final,并在构造器中初始化
package Demo01enum; public class Season { private final String SEASONNAME;//季节的名称 private final String SEASONDESC;//季节的描述 //私有化类的构造器,保证不能在类的外部创建其对象 private Season(String seasonName, String seasonDesc) { //对象如果有实例变量,应该声明为private final,并在构造器中初始化 this.SEASONNAME = seasonName; this.SEASONDESC = seasonDesc; } //在类的内部创建枚举类的实例。声明为:public static final public static final Season SPRING = new Season("春天", "春暖花开"); public static final Season SUMMER = new Season("夏天", "夏日炎炎"); public static final Season AUTUMN = new Season("秋天", "秋高气爽"); public static final Season WINTER = new Season("冬天", "白雪皑皑"); }
JDK 1.5 新增的 enum 关键字用于定义枚举类
- 使用 enum 定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类
- 枚举类的构造器只能使用 private 权限修饰符
- 枚举类的所有实例必须在枚举类中显式列出(, 分隔; 结尾)。列出的
- 实例系统会自动添加 public static final 修饰
- 必须在枚举类的第一行声明枚举类对象
package Demo01enum; public enum SeasonEnum { SPRING("春天", "春风又绿江南岸"), SUMMER("夏天", "映日荷花别样红"), AUTUMN("秋天", "秋水共长天一色"), WINTER("冬天", "窗含西岭千秋雪"); private final String seasonName; private final String seasonDesc; private SeasonEnum(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } }
JDK 1.5 中可以在 switch 表达式中使用Enum定义的枚举类的对象作为表达式, case 子句可以直接使用枚举值的名字, 无需添加枚举类作为限定。
package Demo01enum; public enum Season { /* 春夏秋冬 */ SPRING, SUMMER, AUTUMN, WINTER }
定义测试类
package Demo01enum; public class SwitchTest { public static void main(String[] args) { // switch语句支持枚举类型 // switch也支持String、int // 低版本的JDK,只支持int // 高版本的JDK,支持int、String、枚举。 // byte short char也可以,因为存在自动类型转换。 switch (Season.SPRING) { // 必须省略Season. case SPRING: System.out.println("春天");//春天 break; case SUMMER: System.out.println("夏天"); break; case AUTUMN: System.out.println("秋天"); break; case WINTER: System.out.println("冬天"); break; } } }
Enum类的主要方法:
- values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
- valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
- toString():返回当前枚举类对象常量的名称
- 和普通 Java 类一样,枚举类可以实现一个或多个接口
- 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
- 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法
异常的概述
异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
异常机制的作用:
- 供程序员参考,利于代码的修改,让程序更加健壮。
Java中异常以类和对象的形式存在
package com.bjpowernode.javase.exception; /* java语言中异常是以什么形式存在的呢? 1、异常在java中以类的形式存在,每一个异常类都可以创建异常对象。 2、异常对应的现实生活中是怎样的? 火灾(异常类): 2008年8月8日,小明家着火了(异常对象) 2008年8月9日,小刚家着火了(异常对象) 2008年9月8日,小红家着火了(异常对象) 类是:模板。 对象是:实际存在的个体。 钱包丢了(异常类): 2008年1月8日,小明的钱包丢了(异常对象) 2008年1月9日,小芳的钱包丢了(异常对象) .... */ public class ExceptionTest02 { public static void main(String[] args) { // 通过“异常类”实例化“异常对象” NumberFormatException nfe = new NumberFormatException("数字格式化异常!"); // java.lang.NumberFormatException: 数字格式化异常! System.out.println(nfe); // 通过“异常类”创建“异常对象” NullPointerException npe = new NullPointerException("空指针异常发生了!"); //java.lang.NullPointerException: 空指针异常发生了! System.out.println(npe); } }
当程序发生异常的时候,JVM底层会创建异常对象并且将其抛出
int a = 10; int b = 0; // 实际上JVM在执行到此处的时候,会new异常对象:new ArithmeticException("/ by zero"); // 并且JVM将new的异常对象抛出,打印输出信息到控制台了。 int c = a / b; System.out.println(a + "/" + b + "=" + c);
异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是 java.lang.Throwable ,其下有两个子类:java.lang.Error 与 java.lang.Exception ,平常所说的异常指 java.lang.Exception
- Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:*Error和OOM。一般不编写针对性的代码进行处理。 严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
- Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:空指针访问丶试图读取不存在的文件丶网络连接中断丶数组角标越界
Throwable中的常用方法:
- public void printStackTrace() :打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
- public String getMessage() :获取发生异常的原因。提示给用户的时候,就提示错误原因。
- public String toString() :获取异常的类型和异常描述信息(不用)。
代码示例
import java.io.FileInputStream; import java.io.FileNotFoundException; /* 异常对象的两个方法: String msg = e.getMessage(); e.printStackTrace(); // 一般都是使用这个。 我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢? 异常信息追踪信息,从上往下一行一行看。 但是需要注意的是:SUN写的代码就不用看了(看包名就知道是自己的还是SUN的。)。 主要的问题是出现在自己编写的代码上。 */ public class ExceptionTest09 { public static void main(String[] args) { try { m1(); } catch (FileNotFoundException e) { // 获取异常的简单描述信息 String msg = e.getMessage(); System.out.println(msg); //C:\jetns-agent.jar (系统找不到指定的文件。) //打印异常堆栈追踪信息!!! //在实际的开发中,建议使用这个。养成好习惯! // 这行代码要写上,不然出问题你也不知道! //e.printStackTrace(); /* java.io.FileNotFoundException: C:\jetns-agent.jar (系统找不到指定的文件。) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:213) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110) at com.bjpowernode.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:31) at com.bjpowernode.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:27) at com.bjpowernode.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:23) at com.bjpowernode.javase.exception.ExceptionTest09.main(ExceptionTest09.java:14) 因为31行出问题导致了27行 27行出问题导致23行 23行出问题导致14行。 应该先查看31行的代码。31行是代码错误的根源。 */ } // 这里程序不耽误执行,很健壮。《服务器不会因为遇到异常而宕机。》 System.out.println("Hello World!"); } private static void m1() throws FileNotFoundException { m2(); } private static void m2() throws FileNotFoundException { m3(); } private static void m3() throws FileNotFoundException { new FileInputStream("C:\\jetns-agent.jar"); } }
异常体系结构
- 运行时异常:是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
- 编译时异常:是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。对于这类异常,如果程序不处理,可能会带来意想不到的结果。
译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。编译时异常因为什么而得名?因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。因为异常的发生就是new异常对象。
编译时异常和运行时异常的区别?
- 编译时异常可以称之为:受检异常(CheckedException)或者受控异常。编译时异常一般发生的概率比较高。
- 运行时异常可以称之为:未受检异常(UnCheckedException)或者非受控异常。运行时异常一般发生的概率比较低。
Java异常处理的方式之throws + 异常类型
Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。- 异常对象的生成方式:由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例
- 对象并抛出——自动抛出:由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样
声明异常throws
声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理。关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
声明异常格式:
异常的抛出机制
总结:
- 可以throws异常的父类,例如:抛FileNotFoundException的父对象IOException,这样是可以的。因为IOException包括FileNotFoundException
- throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
- 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。
- 如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。程序员通常只能处理Exception,而对Error无能为力。
代码示例
package Demo01enum; import java.io.FileInputStream; import java.io.FileNotFoundException; /* 处理异常的第一种方式: 在方法声明的位置上使用throws关键字抛出,谁调用我这个方法,我就抛给谁。抛给调用者来处理。 这种处理异常的态度:上报。 */ public class ThrowsDemo { //main方法调用了test方法,要么自己处理要么继续抛 public static void main(String[] args) throws FileNotFoundException { test(); } /* 编译报错的原因是什么? 第一:这里调用了一个构造方法:FileInputStream(String name) 第二:这个构造方法的声明位置上有:throws FileNotFoundException 第三:通过类的继承结构看到:FileNotFoundException父类是IOException,IOException的父类是Exception, 最终得知,FileNotFoundException是编译时异常。 错误原因?编译时异常要求程序员编写程序阶段必须对它进行处理,不处理编译器就报错。 */ public static void test() throws FileNotFoundException { FileInputStream fileInputStream = new FileInputStream("D:\\a.txt"); System.out.println(fileInputStream);//java.io.FileInputStream@4554617c } }
一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM。JVM只有终止。 异常处理机制的作用就是增强程序的健壮性。怎么能做到,异常发生了也不影响程序的执行。所以一般main方法中的异常建议使用try..catch进行捕捉。main就不要继续上抛了。
Java异常处理的方式之try-catch-finally
异常处理是通过try-catch-finally语句实现的。语法格式:
格式详解
try- 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
- 出现异常之后的代码不会执行,会根据异常的种类执行对象的catch中的代码
catch (Exceptiontype e)
- 在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
- 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)
- catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
- catch写多个的时候,从上到下,必须遵守从小到大。
- 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
- 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。 退出JVM之后,finally语句中的代码就不执行了!
/* finally语句: 放在finally语句块中的代码是一定会执行的【再次强调!!!】 */ public class ExceptionTest11 { public static void main(String[] args) { /* try和finally,没有catch可以吗?可以。 try不能单独使用。 try finally可以联合使用。 以下代码的执行顺序: 先执行try... 再执行finally... 最后执行 return (return语句只要执行方法必然结束。) */ try { System.out.println("try..."); return; } finally { // finally中的语句会执行。能执行到。 System.out.println("finally..."); } // 这里不能写语句,因为这个代码是无法执行到的。 //System.out.println("Hello World!"); } }
- finally语句和catch语句是任选的。finally子句必须和try一起出现,不能单独编写。
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /* 关于try..catch中的finally子句: 1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。 finally子句必须和try一起出现,不能单独编写。 2、finally语句通常使用在哪些情况下呢? 通常在finally语句块中完成资源的释放/关闭。 因为finally中的代码比较有保障。 即使try语句块中的代码出现异常,finally中代码也会正常执行。 */ public class ExceptionTest10 { public static void main(String[] args) { FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。 try { // 创建输入流对象 fis = new FileInputStream("D:\\course\\JavaSE进阶-01-面向对象.pdf"); // 开始读文件.... String s = null; // 这里一定会出现空指针异常! s.toString(); System.out.println("hello world!"); // 流使用完需要关闭,因为流是占用资源的。 // 即使以上程序出现异常,流也必须要关闭! // 放在这里有可能流关不了。 //fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } catch(NullPointerException e) { e.printStackTrace(); } finally { System.out.println("hello 浩克!"); // 流的关闭放在这里比较保险。 // finally中的代码是一定会执行的。 // 即使try中出现了异常! if (fis != null) { // 避免空指针异常! try { // close()方法有异常,采用捕捉的方式。 fis.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("hello kitty!"); } }
finally面试题
package com.bjpowernode.javase.exception; /* finally面试题 */ public class ExceptionTest13 { public static void main(String[] args) { int result = m(); System.out.println(result); //100 } /* java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!): java中有一条这样的规则: 方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!) java中海油一条语法规则: return语句一旦执行,整个方法必须结束(亘古不变的语法!) */ public static int m(){ int i = 100; try { // 这行代码出现在int i = 100;的下面,所以最终结果必须是返回100 // return语句还必须保证是最后执行的。一旦执行,整个方法结束。 return i; } finally { i++; } } } /* 反编译之后的效果 public static int m(){ int i = 100; int j = i; i++; return j; } */
代码执行流程图
代码示例
package Demo01enum; import java.io.FileInputStream; import java.io.FileNotFoundException; /* 处理异常的第二种方式: 使用try..catch语句对异常进行捕捉。 这个异常不会上报,自己把这个事儿处理了。 异常抛到此处为止,不再上抛了。 注意: 只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。 另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行。 try..catch捕捉异常之后,后续代码可以执行。 在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选? 如果希望调用者来处理,选择throws上报。 其它情况使用捕捉的方式。 */ public class ThrowsDemo { //main方法自己处理异常 public static void main(String[] args) { try { test(); //test()方法 出现异常,直接进入对应的catch语句块中执行。 System.out.println("我不会执行了"); } catch (FileNotFoundException e) { System.out.println("我在此处处理异常"); e.printStackTrace();//异常处理的方式,处理完成之后不耽误catch后面的代码执行 } System.out.println("我会执行"); } public static void test() throws FileNotFoundException { /* new FileInputStream("D:\\a.txt");出现异常,JVM会做2件事情 1: 创建异常对象,抛给此方法的调用者 2:结束此方法,其后面的代码不在执行 */ FileInputStream fileInputStream = new FileInputStream("D:\\a1.txt"); System.out.println(fileInputStream); } }
执行结果
在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选?
- 如果希望调用者来处理,选择throws上报。
- 其它情况使用捕捉的方式。
final finally finalize有什么区别?
/* final 关键字 final修饰的类无法继承 final修饰的方法无法覆盖 final修饰的变量不能重新赋值。 finally 关键字 和try一起联合使用。 finally语句块中的代码是必须执行的。 finalize 标识符 是一个Object类中的方法名。 这个方法是由垃圾回收器GC负责调用的。 */
抛出异常throw
在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?
- 创建一个异常对象。封装一些提示信息(信息可以自己编写)。
- 需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。
throw 异常对象。throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。如果new了异常 对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
使用格式:例如:
throw new NullPointerException("要访问的arr数组不存在"); throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
注意:
- 如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。
自定义异常
为什么需要自定义异常类?- 我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢?
什么是自定义异常类:?
- 在开发中根据自己业务的异常情况来定义异常类.
异常类如何定义?
- 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception 。
- 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException 。
如何使用自定义异常
package test; /* 1、SUN提供的JDK内置的异常肯定是不够的用的。在实际的开发中,有很多业务, 这些业务出现异常之后,JDK中都是没有的。和业务挂钩的。那么异常类我们 程序员可以自己定义吗? 可以。 2、Java中怎么自定义异常呢? 两步: 第一步:编写一个类继承Exception或者RuntimeException. 第二步:提供两个构造方法,一个无参数的,一个带有String参数的。 */ public class RegisterException extends Exception { /**** * @param message 表示异常提示 * * */ public RegisterException(String message) { super(message); } //空参 public RegisterException() { } }
使用自定义异常
package test; /* 要求:我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。 首先定义一个登陆异常类RegisterException: */ public class Demo { String[] data = {"张三", "李四", "王五", "赵柳"}; public static void main(String[] args) { try { new Demo().login("张三1"); System.out.println("注册成功"); } catch (RegisterException e) { e.printStackTrace(); } } public void login(String s) throws RegisterException { for (int i = 0; i < data.length; i++) { if (data[i].equals(s)) { throw new RegisterException("用户名已经存在"); } } } }
异常注意事项
一般我们是使用一次捕获多次处理方式,格式如下:
总结
- 这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 如果finally有return语句,永远返回finally中的结果,避免该情况.
- 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出