枚举
为什么要有枚举
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
枚举类:
public enum Action { TURN_LEFT, TURN_RIGHT, SHOOT }
调用枚举类
public class EnumDemo { public static void main(String[] args) { doAction(Action.TURN_RIGHT); } public static void doAction(Action action) { switch(action) { case TURN_LEFT: System.out.println("向左转"); break; case TURN_RIGHT: System.out.println("向右转"); break; case SHOOT: System.out.println("射击"); break; } } }
枚举的基本应用:
1、通过enum关键字定义枚举类,枚举类是一个特殊的类,每个元素都是该类的一个实例对象。
2、用枚举类规定值,如上面的WeekDay类。以后用此类型定义的值只能是这个类中规定好的那些值,若不是这些值,编译器不会通过。
3、好处:在编译时期就会发现错误,表明值不符合,减少了运行时期的错误。
4、如果调用者想打印枚举类中元素的信息,需由编写此类的人定义toString方法。
注:枚举类是一个class,而且是一个不可被继承的final类,其中的元素都是类静态常量。
5、常用方法:
构造器:
1.构造器只是在构造枚举值的时候被调用。
2.构造器只有私有private,绝不允许有public构造器。这样可以保证外部代码无法重新构造枚举类的实例。因为枚举值是public static final的常量,但是枚举类的方法和数据域是可以被外部访问的。
3.构造器可以有多个,调用哪个即初始化相应的值。
非静态方法:(所有的枚举类都继承了Enum方法)
String toString() ;//返回枚举量的名称
int ordinal() ;//返回枚举值在枚举类中的顺序,按定义的顺序排
Class getClass() ;//获取对应的类名
String name();//返回此枚举常量的名称,在其枚举声明中对其进行声明。
静态方法:
valueOf(String e) ;//转为对应的枚举对象,即将字符串转为对象
values() ;//获取所有的枚举对象元素
枚举的高级应用
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法
枚举只有一个成员时,就可以作为一种单例的实现方式。
public class Test { public enum TrafficLamp{ RED(30){ public TrafficLamp nextLamp(){ return GREEN; } }, GREEN(30){ public TrafficLamp nextLamp(){ return YELLOW; } }, YELLOW(5){ public TrafficLamp nextLamp(){ return RED; } }; private int time; //构造器 private TrafficLamp(int time){ this.time=time;} //抽象方法 public abstract TrafficLamp nextLamp(); } }
代理
代理(proxy)模式 ——跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
生活中的代理
武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?
程序中的代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易
代理架构图
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
动态代理的工作原理图
总结:
所有的枚举类都继承自java.lang.enum
枚举类一旦被加载就不能在创建、相当于静态加载只加载一次,在类加载的时候
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔
代理相当于中介,他们拥有共同的接口、而代理能完成事件,但主要的功能属于目标类