枚举
什么时候需要使用枚举?
当程序中需要使用到一组常量(整型,字符串)时,就应该使用枚举对这些常量进行封装。
如,星期一到星期天、性别分男和女等
使用枚举可以带来哪些好处:
类型安全,防止传入错误的参数值
可读性好,比int值强
不易出错,比硬编码字符串到程序各个角落好
枚举实例可以具备行为,操作起来更加方便
使用枚举替换int常量和字符串常量
public enum Sex {
MALE, FEMALE;
}
用实例域替代枚举自身的序数
如果需要使用枚举常量的序数,最好自己定义,不要使用默认的ordinal实现
public enum Priority {
STEP_A(0, "洗脸"),
STEP_B(1, "刷牙"),
STEP_C(2, "抽烟"),
STEP_D(2, "WC"),
STEP_E(3, "上班");
private int ordinal;
private String task;
Priority(int ordinal, String task) {
this.ordinal = ordinal;
this.task = task;
}
public int getOridnal() {
return this.ordinal;
}
@Override
public String toString() {
return this.ordinal + ": " + this.task;
}
}
//test
public static void main(String[] args) {
for(Priority p : Priority.values()) {
System.out.println(p);
}
}
EnumSet
通过集合接收几个枚举常量
package effective.chapter6;
import java.util.EnumSet;
public class Text {
public enum Style {
BOLD, ITALIC, UNDERLINE, STRIKETHROUGH;
}
public static void applyStyle(EnumSet<? extends Style> styles) {
for(Style s : styles) {
System.out.println("apply style: " + s);
}
}
//test
public static void main(String[] args) {
Text.applyStyle(EnumSet.of(Style.BOLD, Style.STRIKETHROUGH));
}
}
EnumMap
枚举类型作为Map的key进行使用
描述两个阶段映射到一个阶段过渡
package effective.chapter6;
import java.util.EnumMap;
import java.util.Map;
/**
* 嵌套枚举定义
*/
public enum Phase {
SOLID("固体"), LIQUID("液体"), GAS("汽体");
private String status;
Phase(String status) {
this.status = status;
}
@Override
public String toString() {
return status;
}
//不同阶段转换的过程
public enum Transition {
MELT(SOLID, LIQUID),
FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS);
private Phase src;
private Phase dst;
Transition(Phase src, Phase dst) {
this.src = src;
this.dst = dst;
}
//key: 起始状态
//value: [key:结束状态,value:转变过程]
private static final Map<Phase, Map<Phase,Transition>> m =
new EnumMap<Phase, Map<Phase,Transition>>(Phase.class);
static {
for(Phase p : Phase.values())
m.put(p, new EnumMap<Phase,Transition>(Phase.class));
for(Transition trans : Transition.values())
m.get(trans.src).put(trans.dst, trans);
}
public static Transition from(Phase src, Phase dst) {
return m.get(src).get(dst);
}
@Override
public String toString() {
return this.src + "->" + this.dst;
}
}
}
public class TestNestEnum {
public static void main(String[] args) {
Transition trans = Phase.Transition.from(Phase.SOLID, Phase.LIQUID);
System.out.println(trans);
}
}
结果:固体->液体
通过接口扩展枚举的功能
public interface Operation {
double aplly(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+"){
@Override
public double aplly(double x, double y) {
return x + y;
}
},
MINUS("-"){
@Override
public double aplly(double x, double y) {
return x - y;
}
},
TIMES("*"){
@Override
public double aplly(double x, double y) {
return x * y;
}
},
DIVIDE("/"){
@Override
public double aplly(double x, double y) {
return x / y;
}
},
EXP("^") {
@Override
public double aplly(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%"){
@Override
public double aplly(double x, double y) {
return x % y;
}
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString(){
return symbol;
}
@Override
public double aplly(double x, double y) {
throw new RuntimeException("You must implements method in each enum instance!");
}
}
class TestEnumInterface {
public static void main(String[] args) {
double x = 2.17;
double y = 3.02;
//数组转换为集合
testEnum(Arrays.asList(BasicOperation.values()), x, y);
}
//泛型参数接收枚举集合
public static void testEnum(Collection<? extends Operation> opSet, double x, double y) {
for(Operation op : opSet)
System.out.printf("%f %s %f = %f%n", x, op, y, op.aplly(x, y));
}
}
结果:
2.170000 + 3.020000 = 5.190000
2.170000 - 3.020000 = -0.850000
2.170000 * 3.020000 = 6.553400
2.170000 / 3.020000 = 0.718543
2.170000 ^ 3.020000 = 10.377874
2.170000 % 3.020000 = 2.170000
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
注解
标记注解
package effective.chapter6;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//配置‘@AnnoTest’应该被保留到运行阶段,在运行阶段通过反射定位此注解
@Target(ElementType.METHOD)//配置‘@AnnoTest’可使用的目标:用在方法上,而不是用在字段或者类上
public @interface MyAnno {
}
package effective.chapter6;
/**
* 将注解标注到方法上
*/
public class Sample {
@MyAnno
public static void m1() {}
public static void m2() {}
@MyAnno
public static void m3() {
throw new RuntimeException("Boommmm");
}
public static void m4() {}
@MyAnno
public void m5() {}
public void m6() {}
@MyAnno
public static void m7() {
throw new RuntimeException("Boommmm again");
}
public static void m8(){}
}
package effective.chapter6;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 读取注解,只处理那些标记了特定注解的方法
* 在Sample类中使用@MyAnno注解对若干方法进行标记
* 1.反射Sample类中的方法
* 2.判断方法是否被@MyAnno标记了
* 3.如果标记了,则以静态方法反射调用此方法
* 4.统计被正确调用的静态方法个数,与出现异常的方法个数
* 5.未标记@MyAnno注解的方法不会得到执行
*/
public class Test {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName("effective.chapter6.Sample");
for(Method m : testClass.getDeclaredMethods()) {
//判断方法上的注解是否为@MyAnno
if(m.isAnnotationPresent(MyAnno.class)) {
tests++;
try{
//反射调用静态方法
m.invoke(null);
passed++;
} catch (InvocationTargetException wrappedExc) {
Throwable exc = wrappedExc.getCause();
System.out.println(m + "failed:" + exc);
} catch (Exception e) {
System.out.println("Invalid @MyAnno:" + m);
}
}
}
System.out.printf("passed: %d, failed %d%n", passed, tests - passed);
}
}
注解中定义数组参数
package effective.chapter6;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//配置‘@AnnoTest’应该被保留到运行阶段,在运行阶段通过反射定位此注解
@Retention(RetentionPolicy.RUNTIME)
//配置‘@AnnoTest’可使用的目标:用在方法上,而不是用在字段或者类上
@Target(ElementType.METHOD)
public @interface MyAnno {
Class<? extends Exception>[] values();//接收一组异常类
}
package effective.chapter6;
import java.util.ArrayList;
import java.util.List;
/**
* 将注解标注到方法上
* 给注解的数组参数添加配置
*/
public class Sample {
@MyAnno(values = {IndexOutOfBoundsException.class, NullPointerException.class})
public static void badMethod() {
List<String> list = new ArrayList<String>();
list.addAll(5,null);
}
}
package effective.chapter6;
import java.lang.reflect.Method;
/**
* 对抛出特定异常的方法进行测试
*/
public class Test {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName("effective.chapter6.Sample");
for(Method m : testClass.getDeclaredMethods()) {
//判断方法上的注解是否为@MyAnno
if(m.isAnnotationPresent(MyAnno.class)) {
tests++;
try{
//反射调用静态方法
m.invoke(null);
System.out.printf("Tests %s failed: no exception%n", m);
} catch (Throwable wrappedExc) {
Throwable excCause = wrappedExc.getCause();
Class<? extends Exception>[] excTypes = m.getAnnotation(MyAnno.class).values();
int oldPassed = passed;
for(Class<? extends Exception> excType : excTypes) {
if(excType.isInstance(excCause)) {
passed++;
break;
}
}
if(passed == oldPassed)
System.out.printf("Test %s failed: %s %n", m, excCause);
else
System.out.println("Test ok!");
}
}
}
System.out.printf("passed: %d, failed %d%n", passed, tests - passed);
}
}
建议:坚持使用@overrider注解,避免非法错误!