枚举类Enumeration
1)枚举对应英文(enumeration,简写enum)
2)枚举是一组常量的集合
3)可以理解为:枚举属于一种特殊的类,里面只包含一组 有限的 特定的 对象
1、自定义类实现枚举
-
不需要提供setXxx方法,因为枚举对象值通常为只读
-
对枚举对象/属性使用final+static修饰,实现底层优化(类不用加载)
-
枚举对象名通常使用全部大写,常量的命名规范
-
枚举对象根据需要,也可以有多个属性
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
System.out.println(Season.SPRING);
}
}
//演示自定义枚举实现
class Season {
private String name;
private String desc; //描述
//定义了四个对象
//public final static:类不用被加载,在外部其他类可以直接调用自定义枚举
public final static Season SPRING = new Season("春天","温暖");//多个属性(2个)
public final static Season SUMMER = new Season("夏天","炎热");
public final static Season AUTUMN = new Season("秋天","凉爽");
public final static Season WINTER = new Season("冬天","寒冷");
//1. 构造器私有化-》防止直接new
//2. 去掉set相关方法-》防止属性被修改(只读不能写)
//3. 在Season内部,直接创建固定的对象
private Season(String name, String desc){
this.name = name;
this.desc = desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
2、enum关键字实现枚举
1)使用注意事项
-
使用关键字enum替代class
-
SPRING(“春天”,“温暖”); ==> 常量名(实参列表) =》根据实参列表判断调用的是哪个构造器
-
如果有多个常量(对象),使用,号间隔。最后一个用;号结束
-
如果使用enum来实现枚举,要求将定义常量对象写在类体的最前面
-
当使用enum关键字开发一个枚举类时,默认会继承Enum类(用javap反编译证明),并且是一个final类
-
enum后不能再继承其他类(不能有extends关键字),因为底层隐式继承了Enum类
-
如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
public class Enumeration03 {
public static void main(String[] args) {
System.out.println(Season2.AUTUMN);
System.out.println(Season2.SPRING);
}
}
//使用enum关键字来实现枚举类
enum Season2 {
//使用enum来实现枚举类
//1. 使用关键字enum替代class
//2. SPRING("春天","温暖"); ==> 常量名(实参列表)
//3. 如果有多个常量(对象),使用,号间隔。最后一个用;号结束
//4. 如果使用enum来实现枚举,要求将定义常量对象写在类体的最前面
SPRING("春天","温暖"),
SUMMER("夏天","炎热"),
AUTUMN("秋天","凉爽"),
WINTER("冬天","寒冷"),
OTHER(); //调用的无参构造器,还可以直接简化成OTHER
private String name;
private String desc; //描述
private Season2(){ //无参构造器
}
private Season2(String name, String desc){
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
2)enum常用方法说明
说明:使用enum时,会隐式继承Enum类,这样我们可以使用Enum类的相关方法
public class EnumMethod {
public static void main(String[] args) {
//使用Season2 枚举类来演示(只有春夏秋冬四个枚举)
Season2 autumn = Season2.AUTUMN;
//【对象调用】name:输出枚举对象的名称
System.out.println(autumn.name() + "\n"); //AUTUMN
//【对象调用】ordinal: 输出该美剧对象的次序/编号(从0开始编号)
System.out.println(autumn.ordinal()+ "\n"); //2
//【类调用】values: 返回枚举数组,包含定义的所有枚举对象
Season2[] values = Season2.values();
for (Season2 season : values) {
System.out.println(season);
}
System.out.println();
//【类调用】valueOf: 将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
//功能:根据枚举名找到枚举,获得完整的枚举(包括枚举类属性)
Season2 autumn1 = Season2.valueOf("AUTUMN");
System.out.println("autumn1 = " + autumn1);
System.out.println(autumn == autumn1); //true
//compareTo:比较两个枚举常量,比较的是编号,①当==0,编号相等 ②当<0,前面的编号比后面的编号小
//底层:return self.ordinal - other.ordinal;
//Season2.AUTUMN - Season2.SUMMER编号
System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER)); //1
//toString:Enum类已经重写过,返回的是当前对象的名字(枚举名),如果自己再在枚举类中重写,则调用自己写的toString方法
System.out.println(autumn);
}
}
3)课堂练习
第一题
Enum的toString方法返回name,就是枚举的名字
输出:
BOY
true
第二题
public class Test {
public static void main(String[] args) {
Week[] weeks = Week.values();
for(Week week : weeks){
System.out.println(week);
}
}
}
enum Week {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNSDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private String desc;
private Week(String desc){
this.desc = desc;
}
@Override
public String toString() {
return desc;
}
}
4)enum实现接口
枚举和普通类一样,可以实现接口,如下形式:
enum 类名 implements 接口1,接口2{}
使用实现接口的方法:枚举类名.枚举名.方法名()
public class Test {
public static void main(String[] args) {
Music.CLASSICMUSIC.playing(); //使用实现接口的方法,枚举类名.枚举名.方法名()
}
}
interface IAA {
public void playing();
}
enum Music implements IAA{
CLASSICMUSIC;
@Override
public void playing() {
System.out.println("播放音乐...");
}
}
注解Annotation
1、JDK内置的基本注解类型
1)@Override
限定某个方法,是重写父类方法。该注解只能用于方法
功能:语法校验
如果写了@Override,编译器就会去检查该方法是否真的重写了父类的方法。
如果的确重写了,则编译通过
如果没有构成重写,则编译错误
@Override定义
@interface:表示一个注解类
@Target:可以修饰的类型,这里只能修饰METHOD方法
@Target是修饰注解的注解 ==》元注解
2)@Deprecated
用于表示某个程序元素已过时
功能:做一个过渡,版本升级时的兼容过渡
3)@SuppressWarnings
抑制编译器警告
功能:当我们不希望看到警告信息时,可以使用SuppressWarnings抑制警告信息
用法:
@SuppressWarnings({""})
在{""}
中,可以写入希望抑制的警告信息
一般方便就是@SuppressWarnings({"all"})
注意:
SuppressWarnings作用范围和你放置的位置相关
比如:放在main方法头上,作用域就只有main方法,对其他类成员无效
2、元注解(了解):对注解进行注解
Retention注解
Target注解
Documented注解
Inherited注解
内部类、枚举、注解练习题
第一题
考点:static变量,对不同的对象是共享的
输出:
9.0 red
100 red
第二题
考点:匿名内部类,调用
第一种方法:匿名内部类作为对象,直接调用方法
public class Test {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.testWork();
}
}
interface Computer {
void work();
}
class CellPhone {
public void testWork(){
new Computer(){
@Override
public void work() {
System.out.println("手机在工作");
}
}.work();
}
}
第二种方法:匿名内部类作为参数传递
public class Test {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.testWork(new Computer() {
@Override
public void work() {
System.out.println("手机在工作");
}
});
}
}
interface Computer {
void work();
}
class CellPhone {
public void testWork(Computer computer){
computer.work();
}
}
第三题
考点:局部内部类,重名属性
public class Test {
public static void main(String[] args) {
A a = new A();
a.m1();
}
}
class A {
private final String NAME = "李三";
public void m1(){
class B {
private final String NAME = "张三";
public void show(){
System.out.println(NAME);
System.out.println(A.this.NAME);
}
}
//下面这段代码不能放在class B{}前面,因为classB也是一个局部变量
//局部变量还没有创建的时候,不能使用它
B b = new B();
b.show();
}
}
第四题
版本一:通过设置isRiver来判断是否过河,输出只有一句
public class Test {
public static void main(String[] args) {
Boolean isRiver = false;
Person person = new Person("唐僧", null);
person.work(isRiver);
}
}
interface Vehicles {
void work();
}
class Horse implements Vehicles {
@Override
public void work() {
System.out.println("骑马走...");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("坐船过...");
}
}
class Factory {
//这里,将方法做成static比较方便(不用创建对象)
public static Horse getHorse(){
return new Horse();
}
public static Boat getBoat(){
return new Boat();
}
}
class Person {
private String name;
private Vehicles vehicles;
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
//这里涉及到一个编程思路,就是可以把具体的要求,封装成方法 -> 这里就是编程思想
//如何不浪费属性Vehicle
public void work(Boolean isRiver){
//向上转型
if(isRiver){
vehicles = Factory.getBoat();
} else {
vehicles = Factory.getHorse();
}
vehicles.work(); //动态绑定
}
public String getName(){
return name;
}
}
题目增加条件:唐僧过火焰山,用飞机
版本二:分别调用过河和一般情况,参数额外判断
public class Test {
public static void main(String[] args) {
Boolean isRiver = false;
Person person = new Person("唐僧", null);
person.Common();
person.passRiver();
person.passMountain();
person.passRiver();
person.Common();
}
}
interface Vehicles {
void work();
}
class Horse implements Vehicles {
@Override
public void work() {
System.out.println("Common:一般情况,骑马走...");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("PassRiver:遇到河流,坐船过...");
}
}
class Plane implements Vehicles {
@Override
public void work() {
System.out.println("PassMountain: 遇到火山,用飞机...");
}
}
class Factory {
//存储马:static是公共共享空间
//饿汉式
private static Horse horse = new Horse();
private Factory(){};
//这里,将方法做成static比较方便(不用创建对象)
public static Horse getHorse(){
//返回的马应该是只有一匹马,第一次取到马之后就固定了
return horse;
}
public static Boat getBoat(){
return new Boat();
}
public static Plane getPlane() {
return new Plane();
}
}
class Person {
private String name;
private Vehicles vehicles;
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
//这里涉及到一个编程思路,就是可以把具体的要求,封装成方法 -> 这里就是编程思想
//如何不浪费属性Vehicle
//传入的参数vehicles有三种情况:null、Boat、Horse
public void passRiver() { //当传入null和Horse时,需要将vehicles改为Boat
if(!(vehicles instanceof Boat)){
vehicles = Factory.getBoat();
}vehicles.work();
}
//过火焰山用飞机
public void passMountain() {
if(!(vehicles instanceof Plane)){
vehicles = Factory.getPlane();
}vehicles.work();
}
public void Common() {
if(!(vehicles instanceof Horse)){
vehicles = Factory.getHorse();
}vehicles.work();
}
public String getName(){
return name;
}
}
第五题
考点:成员内部类的调用
public class Test {
public static void main(String[] args) {
Car car1 = new Car(40.6);
Car car2 = new Car(41.6);
Car car3 = new Car(25.6);
Car car4 = new Car(-3.6);
car1.m(); //或者car1.getAir().flow();
car2.m();
car3.m();
car4.m();
}
}
class Car {
private double temperature;
public Car(double temperature) {
this.temperature = temperature;
}
class Air {
public void flow(){
if(temperature > 40) {
System.out.println("吹冷气");
} else if(temperature < 0){
System.out.println("吹暖气");
} else {
System.out.println("关空调");
}
}
}
public Air getAir(){
return new Air();
}
public void m(){
Air air = new Air();
air.flow();
}
}
第六题
考点:枚举、枚举再switch中的使用
switch(枚举对象){
case 枚举名1:
...
break;
case 枚举名2:
...
break;
}
public class Test {
public static void main(String[] args) {
Color[] colors = Color.values();
for(Color color : colors){
switch (color){
case RED:
System.out.println("匹配到红色");
break;
case BLUE:
System.out.println("匹配到蓝色");
break;
case BLACK:
System.out.println("匹配到黑色");
break;
case YELLOW:
System.out.println("匹配到黄色");
break;
case GREEN:
System.out.println("匹配到绿色");
break;
default:
System.out.println("没有匹配到");
break;
}
}
}
}
enum Color implements IA{
RED(255,0,0),
BLUE(0,0,255),
BLACK(0,0,0),
YELLOW(255,255,0),
GREEN(0,255,0);
private int redValue;
private int greenValue;
private int blueValue;
Color(int redValue, int greenValue, int blueValue) {
this.redValue = redValue;
this.greenValue = greenValue;
this.blueValue = blueValue;
}
@Override
public void show() {
System.out.println("redValue=" + redValue + " greenValue=" + greenValue + " blueValue=" + blueValue);
}
}
interface IA {
void show();
}
异常Exception
1、异常的概念
-
异常的出现:
程序中出现了一个不算致命的问题,就导致整个程序抛出异常后退出(崩溃),这不合理。
所以在发现异常后,程序仍然能够继续执行,这就是java异常处理机制 -
异常基本概念:
java中,将程序执行过程发生的不正常情况称为“异常”(语法错误和逻辑错误和不是异常)
-
异常分为两类
- Error(错误):Java虚拟机无法解决的严重错误。如StackOverflowError栈溢出,OOM(out of memory)程序会崩溃。
- Exception :其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码处理。例如空指针访问,网络连接中断等等。
- Exception分为两大类:
- 运行时异常【程序运行时发生的异常】
- 编译时异常【编程时,编译器检查出的异常】
2、异常体系图⭐⭐⭐
类的异常体系图,体现了继承和实现关系
小结
3、常见的异常
① 常见的运行时异常
1)NullPointerException
空指针异常
当应用程序试图在需要对象的地方使用null时,抛出该异常
public class Test {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
2)ArithmeticException
数学运算异常
当出现异常的运算条件时,抛出此异常,比如除以0
3)ArrayIndexOutOfBoundsException
数组下标越界异常
用非法所哟i你访问数组时抛出的异常,如果索引为负或者大于等于数组大小,则该索引为非法索引
4)ClassCastException
类型转换异常
当试图将对象强制转换为不是实例运行类型或者其子类时,抛出该异常。(向下向上转型错误)
class A{}
class B extends A{}
class C extends A{}
A a = new B(); //向上转型
B b = (B)a; //向下转型
C c = (C)a; //转型错误,因为a的运行类型是B,和C无关
5)NumberFormatException
数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能抓换位适当格式时,抛出该异常 =》 使用异常我们可以确保输入的是 满足条件的 数字
String name = "你好";
int num = Integer.parseInt(name);
② 常见的编译异常
1)SQLException
//操作数据库时,查询表可能发生异常
2)IOException
//操作文件时,发生的异常
3)FILENotFoundException
//当操作一个不存在的文件时,发生异常
4)ClassNotFoundException
//加载类时,该类不存在,发生异常
5)EOFException
//操作文件,到文件末尾,发生异常
6)IllegalArgumentException
//参数异常
4、异常处理概念和处理机制图
- 基本介绍
异常处理就是当异常发生时,对异常处理的方式
-
异常处理的方式
1)try-catch-finally
程序员在代码中捕获发生的异常,自行处理
2)throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
不用try-catch时,默认用throws(隐式的)
5、异常处理分类
1)try-catch
-
如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
-
如果异常没有发生,则顺序执行try代码块,不会进入到catch
-
如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用fianlly{}
public class TryCatchDetail {
public static void main(String[] args) {
try {
String str = "你好";
int a = Integer.parseInt(str);
System.out.println("数字:" + a); //1. 一旦发生异常,这句代码不会执行,而是直接进入到catch块
} catch (NumberFormatException e) {
System.out.println("异常信息:" + e.getMessage());
} finally {
System.out.println("finally代码块被执行...");
}
System.out.println("程序继续...");
}
}
- 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch
public class TryCatchDetail {
public static void main(String[] args) {
try {
Person person = new Person();
person = null;
System.out.println(person.getName()); //NullPointerException
int n1 = 10;
int n2 = 0;
int res = n1 / n2; //ArithmeticException
//↓下面捕获三种异常,子类在前,父类在后(否则父类异常包含子类异常,子类异常永远用不到)
} catch (NullPointerException e) {
System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println("父类异常=" + e.getMessage());
} finally {
}
}
}
class Person {
private String name = "jack";
public String getName() {
return name;
}
}
- 可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。【应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑(检测到异常时并不马上崩掉,而是执行完这段finally代码后再崩)】
public class TryCatchDetail {
public static void main(String[] args) {
try {
int n1 = 10;
int n2 = 0;
System.out.println(n1 / n2);
} finally {
System.out.println("执行了finally..");
}
System.out.println("程序继续执行..");
}
}
输出:
2)throws
注意事项
3)如下图,NullPointerException是RuntimeException的子类
4)抛出的(编译)异常有两种处理方式:调用者也抛出异常,或者调用者内部使用try-catch处理异常
抛出运行异常可以不做处理
6、自定义异常
基本概念
自定义异常的步骤
public class CustomException {
public static void main(String[] args) /* throws AgeException */{
int age = 80;
if(!(age >= 18 && age <= 120)){
//这里通过构造器,设置信息
throw new AgeException("年龄需要在18 ~ 120之间");
//throw和throws的区别看后文
}
}
}
//一般继承运行时异常
//如果是继承编译异常Exception,调用者需要throws AgeException
class AgeException extends RuntimeException {
public AgeException(String message) { //构造器
super(message);
}
}
7、throw和throws的对比
8、异常练习
【第一题】
输出:
i=4
3
【第二题】
如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
public class Test {
public static void main(String[] args) {
int n = 0;
Scanner scanner = new Scanner(System.in);
while(true){
System.out.print("请输入一个整数:");
try {
n = Integer.parseInt(scanner.next()); //容易有NumberFormatException
System.out.println("你输入的整数为:" + n);
break;
} catch (NumberFormatException e) {
System.out.println("你输入的不是一个整数,请重新输入!");
}
}
}
}
【第三题】
下面的测试输出什么
当try中存在return或者throw时,finally优先于return和throw运行
当存在catch时,catch优先于finally运行
输出:
【第四题】
public class EcmDef {
public static void main(String[] args) {
try {
//ArrayIndexOutOfBoundsException
if(args.length != 2) {
throw new ArrayIndexOutOfBoundsException("缺少命令行参数或参数个数不对");
}
//NumberFormatException
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
//ArithmeticException
double res = cal(n1, n2);
//三个异常都通过,则执行下面代码(输出结果)
System.out.println("n1/n2 => " + n1 + "/" + n2 + "=" + cal(n1, n2));
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("缺少命令行参数");//注意,空指针异常是有数组元素,只不过没有初始化,而不是缺少元素(命令行参数)
} catch(NumberFormatException e) {
System.out.println("数据格式不正确");
} catch(ArithmeticException e) {
System.out.println("不能除0");
}
}
public static double cal(int n1, int n2) {
return n1 / n2;
}
}
【第五题】
【第六题】
写出运行结果