改善java程序的151个建议--枚举和注解

83、项目开发中,推荐使用枚举定义常量,来代替接口常量或类常量

Egenum{Spring,Summer,Autumn,Winter;}

枚举定义常量相对于经常使用的常量类和静态常量相比的优势:

1)枚举常量更简单:枚举常量不需要定义枚举值,int spring=1;枚举表示的 是一个枚举项,字面含义不同,其他常量必须是一个类型;

2)枚举常量属于稳态型

3)枚举具有内置的方法,例如values:获得所有值的集合,可用于遍历,ordinal: 获得排序值,compareTo比较方法等;

4)枚举可以自定义方法:枚举常量不仅可以定义静态方法,还可以定义非静 态方法,还能够从根本上杜绝常量类被实例化

缺点:枚举类型是不能有继承的,枚举常量定义完毕后,除非修改重构,否则无法做扩展。

枚举类型的遍历:

for(Season s:Season.values()){
			System.out.println(s);
}

84、使用构造函数协助描述枚举项

enum Season{
	//通过构造方法可以为枚举项添加描述和其他定义
	Spring("春","春暖花开"),Summer("夏","艳阳高照"),Autumn("秋","秋高气爽"),Winter("冬","雪花飘飘");
	
	private String desc;//描述
	private String other;//其他
	
	//添加上述描述和定义必须添加下面所示的构造方法
	Season(String desc,String other){
		this.desc=desc;
		this.other=other;
	}
	
	//获得描述
	public String getDesc(){
		return desc;
	}
	//获得其他
	public String getOther(){
		return other;
	}
}

推荐在枚举定义中为每个枚举项定义描述,比在接口常量或类常量中增加注释的方式友好、简洁

85、小心switch带来的空值异常

	public static void testSwitch(Season s){
		switch(s){
		case Spring:System.out.println("春");break;
		case Summer:System.out.println("夏");break;
		default:throw new AssertionError();
		}
	}

上面代码,如果做测试:testSwitch(null);会发现在switch(s)处抛出空指针异常,这是因为java能判断的switch类型为shortbyteintchar类型,那么为什么枚举类型能编译通过呢,这是因为编译器是根据枚举类型的排序值进行判断的:Switch(s.ordinal()){case Season.Spring.ordinal()},如果为null值,那么没有ordinal方法,肯定会抛空指针异常,所以解决方法是在该方法中加入是否为null判断

86、在switchdefault代码块中增加AssertionError错误

Default:throw new AssertionError();

87、使用valueOf前必须进行校验

枚举类中提供valueOf方法,用于查找字符串值与该参数相等的枚举项,返回一个枚举类型的数据

	//测试valueOf方法,因为Season不存在SpringTime会抛出
	//IllegalArgumentException异常信息
	public static void testValueOf(){
		List<String> list=Arrays.asList("Spring","SpringTime");
		for(String s:list){
			Season season=Season.valueOf(s);
			if(season!=null){
				System.out.println(s);
			}else{
				System.out.println("该枚举类型不存在");
			}
		}
	}

解决方案:

1、在Season.valueOf方法中添加try....catch方法进行捕获

2、在枚举类中添加一个判断是否存在的方法,例如:contains方法,如果存在 该字符串信 息,则进行valueOf 转换为相应的枚举类型,不存在则不转换

88、用枚举实现工厂方法模式更简单

89、枚举项的数量限制在64个以内

 Java提供的枚举集合,由于枚举类型的实例数量固定并且有限,相对来说其效率会比其他SetMap要高:

EnumSet:其元素必须是某一枚举的枚举项

EnumMapkey值必须是某一枚举的枚举项

EnumSet存储枚举类型数据时候,当枚举数量小于等于64时,创建一个RegularEnumSet实例对象,大于64时则创建一个JunmboEnumSet实例对象

	public static void testEnumSet(){
		EnumSet<Season> set1=EnumSet.allOf(Season.class);
		EnumSet<Season> set2=EnumSet.allOf(Season.class);
		//枚举类型Season中小于或等于64个元素,则得到的结果为:class java.util.RegularEnumSet
		System.out.println(set1.getClass());
		//枚举类型Season中超过64个元素,则得到的结果为:class java.util.JumboEnumSet
		System.out.println(set2.getClass());
	}

因为EnumSet提供的实现类底层都是基本的数字类型操作,其性能肯定比其他的Set类型要好很多,特别是数量少于64的时候,速度飞快.

90、小心注解继承

元注解:@Inherited,表示一个注解是否可以自动被继承

91、枚举和注解结合使用威力更大

//定义角色级别,用户,管理员,超级管理员
enum RoleIdentify{
	User,Admin,SuperAdmin;
}

//定义访问权限注解类
@Retention(RetentionPolicy.RUNTIME)//元注解,表示该注解的保留级别
@Target(ElementType.TYPE)//元注解,表示该注解可以标注在什么地方
@Inherited//元注解,表示该注解会被自动继承
@interface Access{
	//定义访问级别,默认为管理员
	RoleIdentify level() default RoleIdentify.Admin;
}
//使用自己定义的注解
@Access(level=RoleIdentify.User)
class TestAnnatation{
	
}

public class AnnatationTest {
	public static void main(String[] args) {
		//测试
		TestAnnatation test=new TestAnnatation();
		Access access=test.getClass().getAnnotation(Access.class);
		System.out.println(access.level());//打印输出User
	}
}

92、主要@Override不同版本的区别

如下代码:

interface MyInterface{
	public void doSomething();
}
class MyImpl implements MyInterface{
	@Override
	public void doSomething() {
		
	}
	
	public void doTest(List<String> strList){
		
	}
}

上面代码在jdk1.6版本上编译没有任何问题,但是在jdk1.5版本上编译上面代码会出错误:The method doSomething of type MyImpl override a superclass...

原因是jdk1.5中的@Override是严格遵守覆写的定义:子类方法与父类方法必须具有相同的方法名称、输入参数、输出参数(允许子类缩小)、访问权限(允许子类扩大),父类必须是一个类,不能是一个接口,否则不能算是覆写。

所以,如果是jdk1.6版本的项目移植到jdk1.5版本上面,就需要删除实现接口方法上的@Override注解。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。