异常处理&枚举
学习目标:
1. 熟练掌握java的异常处理机制
2. 熟练掌握枚举数据类型
一、异常处理
1.1 什么是异常?
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持 通畅等等。
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误不是异常)
1.2 java的异常体系
java的异常体系如下:
Throwable是异常体系的根类,提供了大量的异常处理方法,子类有Exception和Error,各自含义如下:
-
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:
StackOverflowError
一般不编写异常处理代码,程序员应该从程序的逻辑上尽量避免这种情况。 -
Exception: 因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理;Exception异常又分为编译时异常和运行时异常
-
编译时异常:既在编译阶段出现的异常,是指程序员必须处理的异常,否则会对程序产生难以意料的后果;对于这类异常要求程序员必须进行异常处理或者异常抛出。
-
运行时异常:是指在编译阶段不报异常,在运行阶段报出的异常;这类异常一般是指编程时的逻辑错误,如数组越界异常,空指针异常等,这时候要求程序员从编程逻辑上避免这些问题。当然也有一些不是程序编程引起的运行时异常,如算术异常分母为0,而分母是客户输入的,那么这个异常就不是程序员编程引起的运行时异常,需要异常处理。
-
1.3 常见异常介绍
常见的运行时异常有:
- java.lang.RuntimeException
- ClassCastException
- ArrayIndexOutOfBoundsException
- NullPointerException
- ArithmeticException
常见的编译时异常有:
- java.io.IOExeption
- FileNotFoundException
1.4 异常处理 throws
在定义方法的过程中,方法体的语句可能出现某种异常,但是并不能确定如何处理这种异常,则方法应显示的声明抛出异常,表明该方法可能出现这些异常,但是没有做出处理,交给方法的调用者处理。
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
示例1:定义数学类并指出可能出现的异常
//1.定义接口
public interface MyMath{
//1.定义除法规范 告诉实现者可能出现异常
public int div(int i,int j) throws Exception;
}
//2.定义数学类类
class MyMathImpl implements MyMath{
@Override // 3.定义数学类 是给别人用的告诉调用者有风险。
public int div(int i, int j) throws Exception {
return i/j;
}
}
注意: 子类抛出的异常不能大于父类异常
1.5 异常处理try … catch … finally
在调用有异常的方法时,我们可根据是否出现异常状况做出相应的异常处理,语法如下:
示例:对上述的数学类中方法调用,并处理异常
public interface MyMath{
//1.定义除法规范 告诉实现者可能出现异常
public int div(int i,int j) throws Exception;
/*程序入口*/
public static void main(String[] args) {
//创建对象
MyMath myMath = new MyMathImpl();
try {
//可能出现异常的代码
myMath.div(1,0);
} catch (Exception e) {
//出现异常运行的代码
e.printStackTrace();
} finally {
//一定会执行的代码
System.out.println("最要要执行的代码");
}
}
}
//2.定义数学类类
class MyMathImpl implements MyMath{
@Override // 3.定义数学类 是给别人用的告诉调用者有风险。
public int div(int i, int j) throws Exception {
return i/j;
}
}
1.6 手动抛出异常 throw
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出;手动抛出的异常必须是Throwable
或其子类的实例。
示例1:手动抛出异常
/**
* 手动抛出异常
*/
public class ThrowDemo {
//定义一个工资校验方法,工资不能为负数
public static int sal(int sal) throws Exception{
//工资是负数报异常
if(sal<0) {
//3.创建一个Throwable 子类异常
Exception exception = new Exception("工资不能是负数");
//RuntimeException exception = new RuntimeException("工资不能是负数");
//4. 手动抛出异常
throw exception;
}
return sal;
}
public static void main(String[] args) {
try {
sal(-5);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
1.7 自定义异常
自定义异常类过程如下:
- 一般地,用户自定义异常类都是
RuntimeException
的子类。 - 自定义异常类通常需要编写几个重载的构造器。
- 自定义异常需要提供serialVersionUID
如何使用自定义异常:
- 自定义的异常通过throw抛出。
- 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型
示例1:自定义负数异常
public class FuShuException extends RuntimeException {
private static final long serialVersionUID = 1L;
public FuShuException() {
}
public FuShuException(String message) {
super(message);
}
}
public class FuShuExceptionTest {
public static double getSalary(double money) {
if(money<0) {
throw new FuShuException("工资不能为负数");
}
return money;
}
public static void main(String[] args) {
getSalary(-10);
}
}
1.8 小结
-
java的异常系统
-
异常处理机制throws与try … catch … finally
-
编译时异常和运行时异常的区别
-
手动抛出异常throw
-
自定义异常
二、枚举
2.1 枚举入门案例
JDK5中才出现枚举技术,并独立出关键字Enum
什么是枚举? 枚举就是一一列举固定值;例如:季节只能用春、夏、秋、冬;再比如月份1-12月;在比如星期1-7。
为什么需要枚举数据类型? 我们发现开发中往往有这么一类需求:需要一一列举某几个固定值情况,不能超出这几个固定值范围。
示例1:学生等级
/**
* 需求:学生类的成绩等级只能是A、B、C
*/
public class Student {
private String name;
private String grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
if(grade.equals("A") || grade.equals("B") || grade.equals("C")){
this.grade = grade;
}else {
throw new RuntimeException("不是合法的等级");
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade='" + grade + '\'' +
'}';
}
public static void main(String[] args) {
Student xiaoMing = new Student();
xiaoMing.setGrade("D");
xiaoMing.setName("小明");
System.out.println(xiaoMing);
}
}
类似上面的问题可以使用枚举技术解决
**枚举:类的对象只有有限个,确定的。举例如下: **
- 星期:Monday(星期一)、…、Sunday(星期天)
- 季节:Spring(春节)…Winter(冬天)
- 线程状态:创建、就绪、运行、阻塞、死亡
枚举类的实现:JDK5
之前需要自定义枚举类 ;JDK5
新增的 enum
关键字用于定义枚举类
2.2 手动实现枚举
如何手动实现枚举类?
- 私有化类的构造器,保证不能在类的外部创建其对象
- 在类的内部创建枚举类的实例。声明为:public static final
示例1:自定义等级枚举,并应用
public class Student {
private String name;
private Grade grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
public static void main(String[] args) {
Student xiaoMing = new Student();
xiaoMing.setName("小明");
xiaoMing.setGrade(Grade.A);
System.out.println(xiaoMing);
}
}
//自定义枚举类
class Grade{
//1.私有构造器
private Grade(){
}
//2.公有静态常亮枚举实例
public static final Grade A = new Grade();
public static final Grade B = new Grade();
public static final Grade C = new Grade();
}
2.3 使用enum定义枚举
使用 enum定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类
- 枚举类的构造器只能使用 private 权限修饰符
- 枚举类的所有实例必须在枚举类中显式列出(, 分隔; 结尾)。列出的实例系统会自动添加 public static final 修饰
- 必须在枚举类的第一行声明枚举类对象
示例1:使用enum定义枚举
public enum Grade {
/*A(),B(),C(); 全写模式*/
A,B,C; //简写模式
}
//测试使用
public class Student {
private String name;
private Grade grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student [name=" + name + ", grade=" + grade + "]";
}
public static void main(String[] args) {
Student anqila = new Student();
anqila.setGrade(Grade.A);
anqila.setName("安其拉");
System.out.println(anqila);
}
}
示例2:定义构造器有参数的枚举
public enum Grade {
//3.定义枚举固定值
A("85-100"),B("75-84"),C("60-74");
//1.定义属性
private String score;
//2.通过构造方法赋值
private Grade(String score){
this.score = score;
}
//4.可以给枚举类的属性添加getter和setter方法
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
}
public class Student {
private String name;
private Grade grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
public static void main(String[] args) {
Student xiaoMing = new Student();
xiaoMing.setName("小明");
xiaoMing.setGrade(Grade.A);
System.out.println(xiaoMing);
System.out.println(Grade.A.getScore());
}
}
和普通 Java 类一样,枚举类可以实现一个或多个接口。若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式, 则可以让每个枚举值分别来实现该方法。
示例3:实现接口的枚举定义与使用
//1.定义接口
interface GradeInter{
public abstract String getStr();
}
//2.枚举是特殊的类可以实现接口
public enum Grade implements GradeInter{
//A("85-100"),B("75-84"),C("60-74");
A("85-100"){
@Override
public String getStr() {
return "优秀";
}
},
B("75-84"){
@Override
public String getStr() {
return "良好";
}
},
C("60-74"){
@Override
public String getStr() {
return "及格";
}
};
//定义属性
private String score;
//定义私有构造器
private Grade5(String score){this.score=score;}
//统一实现抽象方法
/* @Override
public String getStr() {
return "考试通过";
}*/
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
}
public class Student {
private String name;
private Grade grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
public static void main(String[] args) {
Student xiaoMing = new Student();
xiaoMing.setName("小明");
xiaoMing.setGrade(Grade.A);
//1.查看A等级的分数
System.out.println(Grade.A.getScore());
//2.查看A等级的描述情况
System.out.println(Grade.A.getStr());
}
}
2.4 枚举在switch结构中应用
JDK5
中可以在 switch 表达式中使用enum
定义的枚举类的对象作为表达式, case 子句可以直接使用枚举值的名字, 无需添加枚举类作为限定。
示例1:在switch中使用季节枚举
public enum Season {
//1.春季、夏季、秋季、冬季、
SPRINGTIME,SUMMER,AUTUMN,WINTER;
}
public class SeasonDemo {
public static void main(String[] args) {
switch(Season.SPRINGTIME){
case SPRINGTIME:
System.out.println("春天");
break;
case SUMMER :
System.out.println("夏天");
break;
case AUTUMN :
System.out.println("秋天");
break;
case WINTER :
System.out.println("冬天");
break;
}
}
}
2.5 枚举的常用方法
枚举的常用方法如下:
示例1:枚举的常用方法
public class EnumMethod {
//1.枚举常用方法
public static void main(String[] args) {
//1.获得枚举名字
String name = Grade.A.name();
System.out.println(name);
//2.将字符串的枚举名字转为枚举值
Grade a = Grade.valueOf("A");
System.out.println(a);
//3.返回枚举值所在数组中的下标值
int index = Grade.B.ordinal();
System.out.println(index);
//4.返回枚举实例的数组容器
Grade[] values = Grade.values();
for(int i=0;i<values.length;i++){
System.out.println(values[i]);
}
}
}
2.6 小结
- 枚举是一种特殊的类, 枚举类 。
- 使用enum定义的枚举默认继承了java.lang.Enum。所以可以直接使用父类Enum的方法。
- 枚举声明的每一个枚举值 都是静态常量。
- 枚举可以实现接口,可以统一实现抽象方法,也可以单独实现抽象方法
jdk5
以后 switch 支持枚举。
三、总结
- 异常体系
- 异常处理 throws
- 异常处理try … catch … finally
- 手动抛出异常 throw
- 自定义异常
- 枚举的基本使用
- 枚举实现接口
- 枚举常用方法