Effective Java 阅读笔记——枚举和注解

30:用enum代替int常量

当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外。

31:用实例域代替序数

应该给enum添加int域,而不是使用ordinal方法来导出与枚举关联的序数值。(几乎不应使用ordinal方法,除非在编写像EnumMap这样的基于枚举的通用数据结构)

//WRONG
public enum Fruit{
    APPLE, PEAR, ORANGE;
    public int numberOfFruit(){
        return ordinal() + 1;
   }
}

//RIGHT
public enum Fruit{
    APPLE(1), PEAR(2), ORANGE(3);
    private final int number;
    Fruit(int num) {number  = num;}
    public int numberOfFruit(){
        return number;
    }
}

32:用EnumSet代替位域

EnumSet的内容都表示为位矢量。如若底层的枚举类型个数小于64个,则整个EnumSet就用单个long来表示,因此性能上比的上位域。

//WRONG
public class Text{
    private static final int STYLE_BOLD                  = 1 << 0;
    private static final int STYLE_ITALIC                 = 1 << 1;
    private static final int STYLE_UNDERLINE         = 1 << 2;

    public void applyStyles(int styles) {...}
}
//use
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

//RIGHT
public class Text{
    public enum Style{STYLE_BOLD, STYLE_ITALIC, STYLE_UNDERLINE}

    public void applyStyles(Set<Style> styles) {...} //这里不使用EnumSet<Style>参数是因为考虑到某些客户端可能会传递一些其他的Set实现
}
//use
text.applyStyles(EnumSet.of(STYLE_BOLD, STYLE_ITALIC));

33:用EnumMap代替序数索引

序数索引是指依赖于枚举成员在枚举中的序数来进行数组索引,如:

//定义了植物类,其中植物又分为水果,蔬菜,树木三种
public class Plant{
    public enum Type { Fruit, Vegetables, Tree}
    private final String name;
    private final Type type;

    Plant(String name, Type type){
        this.name = name;
        this.type = type;
    }
}

Set<Plant>[] plants = (Set<Plant>[]) new Set[Plant.Type.valuse().lenght];
//根据植物的类型,分别把所有的植物放入三个set中
for(int i = 0; i < plant.lenght; i++){
   plant[i] = new HashSet<Plant>();
}

for(Plant p : garden){  //garden里放了所有的植物
    plant[p.type.ordinal()].add(p)  //反面教材:利用了枚举的序数来得到想要的数组索引,用户在其他地方可以不使用ordinal函数,而直接使用int值来访问,就可能出错
}    

应该使用EnumMap来实现,EnumMap内部是采用数组实现的,具有Map的丰富功能和类型安全以及数组 的效率:

Map<Plant.Type, Set<Plant>> plants = new EnumMap<Plant.Type, Set<Plant>>(Plant.Type.class);  //构造函数需要 键 类型的Class对象
//根据植物的类型,分别把所有的植物放入三个set中
for(Plant.Type type : Plant.Type.valuse()){
   plant.put(type, new HashSet<Plant>);
}

for(Plant p : garden){  //garden里放了所有的植物
    plant.get(p.type).add(p)  //用户必须使用正确的键值来访问,即Type类型
}    

当需要多维关系时,可以使用EnumMap<..., EnumMap<...>>

34:用接口模拟可以伸缩的枚举

由于在java中enum不是可扩展的,在某些情况下,可能需要对枚举进行扩展,比如操作类型(+-*/等),就可以考虑:

  1. 定义一个接口,比如public interface Operation{...};
  2. 使枚举继承接口:比如public enum BasicOperation implements Operation{...}
  3. 使用时的API写成接口(比如,T extends Enum<T> & Operation),而不是实现(比如BasicOperation )

    private static <T extends Enum<T> & Operation> void function(T t,..); //表示T即表示枚举又是Operation的子类型
  4. 当需要扩展BasicOperation枚举时,就可以另写一个枚举,且implements接口Operation

35:注解优先于命名模式

优先使用注解来表面针对某些程序元素的特定信息

36:坚持使用Override注解

在想要覆盖的方法上使用Override注解,编译器就可以帮助发现一些错误。可以不写Override的特例:在非抽象类中实现了父类的抽象方法,因为要是没有覆盖,则编译器就会发出错误。

37:用标记接口实现类型

标记分为标记接口和标记注解。

标记接口:没有包含方法声明的接口,只是指明某个类实现了具有某种属性的接口。比如Serializable接口。

标记接口与标记注解的最终要的区别在于:标记接口可以在编译时就检查到相应的类型问题,而标记注解则要到运行时。

使用:当标记要应用到任何程序元素,包括方法、域等,而不仅仅是接口和类,或者在未来会给标注添加更多信息,或者要适应已经广泛使用注解类型的框架,那么应该使用标记注解;当标记只应用给类和接口,定义一个任何新方法都不会与之关联的标记类型,就应该使用标记接口。

时间: 2024-10-18 01:34:46

Effective Java 阅读笔记——枚举和注解的相关文章

Effective java经验之谈,枚举,注解,方法,通用设计,异常

这几章看的比较快,内容就如同标题一样比较容易理解,所以只有部分内容会在[]中解释,其他的就直接理解标题,并不影响阅读质量. 不过如果时间充足的话,还是仔细读一读原书的内容,相信还是有所收获的.主要最近自己想进入算法与机器学习部分,尽快结束这本书. 另一方面,讨论一些自己感兴趣的内容,我会将搞过的东西总结下.后面可能写一部分关于java字节码阅读以及编写的东西.只所以这么来, 是因为字节码编程还是非常用途实际的,这一"java编译语言"使得String与StringBuilder的性能对

Effective Java阅读笔记——创建和销毁对象(一)

类通常提供一个公有的构造器方法,以此来让客户端可以获取自己(类)的一个实例.但是在创建对象时,应该首先考虑利用静态工厂方法代替构造器来返回一个实例.利用静态工厂方法而不是公有的构造器有几个优势: 静态工厂方法有(不同的)名字 构造器方法都有相同的名字,就是类的名字.区分不同构造器的方法是通过观察方法的签名确定的.方法的签名包括方法名,方法参数的类型,数目以及顺序,方法的返回类型不是方法签名的一部分.无法通过构造器方法名区分不同的构造器,只能通过参数的不同区分,这种情况下(特殊情况下有可能参数类型

Effective Java 阅读笔记——方法

38:检查参数的有效性 每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制,在方法的开头处对参数进行检查,并且把这些限制写入文档. 注意: 对于公有方法,应该使用@throws标签在文档中说明违反参数值限制会抛出的异常 对于非公有的方法,通常使用断言来检查他们的参数:断言如果失败,抛出AssertionError:如果没有起到作用,本质上也不会有成本开销 private void sort(long a[]){ assert a != null; } 对于构造函数中的,或者参数将会被保存,

Effective Java 阅读笔记——并发

66:同步访问共享的可变数据 synchronized:1互斥,阻止线程看到的对象处于不一致的状态:2保证线程在进入同步区时能看到变量的被各个线程的所有修改 Java中,除了long或者double,“读”或者“写”一个变量是原子的.注意:是读或者写单个动作是源自的,而不是读写这两个动作整体是原子的. 由于虚拟机会对代码进行优化,所以可能会导致一些错误:可能你想的是在另一线程中改变done的值来终止while循环,但是优化之后却无法做到这样.要避免这样的优化错误,就必须对done同步. //优化

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

Effective Java读书笔记(4 类和接口)

4.1 使类和成员的可访问性最小化 要区别设计良好的模块和设计不好的模块,最重要的因素在于,这个模块对于外部的其他模块而言,是否隐藏其内部数据和其他实现细节.设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰的隔离开来,然后模块之间只通过API进行通信,一个模块不需要知道其他模块内部的工作情况,这个概念被称为信息隐藏或封装,是软件设计的基本原则之一. 4.2 在公有类中使用访问方法而非公有域 坚持面向对象程序设计思想:如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改

Effective Java读书笔记(3对于所有对象都通用的方法)

3.1 覆盖equals时请遵守通用约定 什么时候应该覆盖Object.equals()方法呢? 如果类具有自己特有的"逻辑相等"概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法. Object.equals()方法具有自反性.对称性.传递性.一致性和与null比较返回false的特点. 实现高质量equals方法的诀窍: (1)使用==操作符检查"参数是否为这个对象的引用".如果是,则返回true,这

Effective C++ 阅读笔记_条款27 尽量少做转型动作

Effective C++ 阅读笔记_条款27 尽量少做转型动作 1.转型的三种形式,可以分为两大类. (1)旧式转型(old-style casts) (1.1) (T) expresstion (1.2) T (expression) (2) 新式转型(c++-style casts) (2.1)const_cast<T> (expression) (2.2)dynamic_cast<T> (expression) (2.3)reinterpret_cast<T>

【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错

原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-import.html#1.2.values方法的使用: Java基础笔记 – 枚举类型的使用介绍和静态导入 本文由arthinking发表于4年前 | Java基础 | 暂无评论 |  被围观 8,332 views+ 1.枚举(Enum):1.1.枚举类型中的两个静态方法:1.2.values方法的使用: