Effective Java 读书笔记(19-38)

原创
2018/04/20 21:03
阅读数 103

19. 用 class 替代 struct

(常见于c语言转过来的程序员,事实上这在java里没有意义)

20. 用 class 层级 取代 union

(union 是c语言的实现,在java里没有讨论的意义。)

21. 用class 取代 enum

在java中,你可以使用类型安全枚举,这好处多多,你也可以继承其他接口,如Comparable接口实现排序功能。因为单例模式,其比较可以直接使用引用(== 而不是 equals),这使其性能可以与常量一致。

public  class MyEnum {
    private final String enumName;
    private MyEnum(String enumName) { this.enumName = enumName; }

    public static final MyEnum ENUM_TYPE1 = new MyEnum("enum1");
    public static final MyEnum ENUM_TYPE2 = new MyEnum("enum2");
}

缺点,复杂,不如直接定义int常量简练和快速。另外我们往往可以通过位运算来计算是否同时满足。如:

public static final int ENUM_TYPE1 = 1;
public static final int ENUM_TYPE2 = 2;
public static infal int ENUM_TYPE3 = 4;

int enumValue = ENUM_TYPE1 | ENUM_TYPE2;
if (enumValue & ENUM_TYPE1 > 0) {
/// TODO    
}

22. 用 class or interface 取代 function point (函数指针)

在java体系中,我们往往使用接口+匿名内部类的方式实现函数指针的功能。在1.8+中可以使用lambda表达式,这更简洁。 (说句题外话,c# 中倒是有个委托类型实现函数指针的功能)

23. 检查函数参数的有效性

简单来说,就是如果该函数可能出抛出某些异常,那么就在函数中显示的判断有效性并抛出异常,而不是等待jvm运行期再抛出,因为我们自己抛出的异常我们可以方便的调试其代码的位置,而等jvm抛出异常会使调试工作变的艰难。 当然,这并不是一条非常严格的提议,因为在某些操作非常频繁的时候,频繁的检查可能会影响性能。另外,还需要谨慎的考虑校验代码的位置,因为其可能影响操作的原子性(虽然函数已经执行失败,但其中的部分数据还是已经被改变了)。

24. 在需要时对函数参数使用保护性拷贝

这一条指出需要考虑一种情况,即通过参数传入函数(或者类,通过构造函数)内部的对象,仍然可能被其他代码在外部改变内容,所以如果你如果想要确定内部的变量是确实不可变的,请在函数体或者类的内部clone一个使用,而不是使用外部传入的对象。同样,返回一个内部对象也有同样的问题。

25. 谨慎的设计方法的原型

  1. 谨慎选择方法名称。方法名称应该总是遵循命名习惯。
  2. 不要追求便利的方法。比如将好几个操作都集中在一个函数里。只有当你确定这些操作经常被调用的时候才需要提供。
  3. 避免过长的参数列表,通常不要超过3个,因为过长的参数列表代表着调用者只能边看文档边写了。
  4. 参数类型优先使用接口,而不是类。

26. 谨慎的使用重载 overloaded ,尤其是参数具有继承关系或者可以相互转换时。

重载,即具有不同参数的相同名称的方法。因为子类是在运行期决定类型的,而重载在编译期就已经决定了调用哪个方法。所以重载在运行期往往无法正确的调用最合适的方法。如果参数涉及到类继承,考虑使用override,这可以保证在运行期调用到合适的方法。 一个安全而保守的策略是不要有相同参数数目的重载方法。

27. 对于返回Collection的方法,返回长度为0的数组或者list,而不是null

这样调用的时候可以更加方便,而且对于java来说,空对象的性能开销并不大。

28. 所有导出的API元素都需要文档注释,如果可以,尽量使用javadoc

如下,你也可以使用 html 标签嵌入 javadoc 中,这对复杂的说明很有帮助,你可以使用table标签进行组织。但更常见的如 <p> <code> <pre> 等标签,可以另外了解,但对于 <>& 等符号,需要进行xml转义 即&

/**
 * 函数功能 the function of the api 
 * @param 参数说明 the descaption of the param
 * @throws 异常说明 Excetion if param is not ..
 * 线程安全性 thread safty
**/

29. 局部变量作用域最小化

在第12条中已经提到过这一条。你可以在第一次使用局部变量时再声明它来保证这一条。

30. 了解和使用库

这一条有两层意思,第一点,使用库有很多好处,一个良好的尤其是标准库中提供的功能往往是专家们编写的,而且经过成千上万的程序员测试过的,肯定它非常的实用。第二点是,在一些情况下你需要了解库的内部实现,以确定其是否真的不会出问题。

31. 如果要求精确,那么抛弃float和double,请使用 BigDecimal,int 或者long

浮点类型天然的不精确,在很低的位数上会出现偏差。 如果可以确定最小单位,则实用 long 型替代浮点数是个很好的主意。

32. 如果其他类型更适合,则尽量不要用字符串

虽然我们在键盘上的输入总是字符,但在程序中使用其本身的类型才更适合,比如年龄,日期等,这更方便在程序中使用。 另外字符串本质上是数据,而不是“类型”,所以请尽量不要使用其作为原语,枚举等。

33. 了解字符串连接的性能

注意,连接n个字符串而重复的使用字符串连接操作符,要求n的平方级的时间。因为string的不可变性,每次操作都会导致新的对象产生。 所以在需要大量拼接字符串时,请使用其可变版本StringBuffer 或 StringBuilder

34. 通过接口引用对象

第25条说道,应该使用接口作为参数而不是具体的类,这一条更进一步的指出,如果可以,在局部变量中也尽量的使用接口而不是类,如:

// Good
List list = new Vector();
// 而不是像这样
Vector list = new Vector();

这样,你代码的后续修改可以更加简单,因为你可以仅仅更换 new 后面的实现类就可以改变数据类型。 但同样,如果你的代码基于了类的实现细节,当然不需要非要使用接口。

35. 接口 优先于 反射机制

反射提供一种机制,可以让你在运行期得到类的信息,但使用它有很多缺点,首先是无法做编译期检查,另外反射的性能不太好,如果你确定想要调用类的某一个方法,最好还是实现某个接口。 (事实上反射的使用现在还是非常普遍的,尤其是在设计框架的时候,因为这本书非常的老了,当时的反射机制可能还不太完善,现在反射的性能还不算太差)

36. 谨慎的使用本地方法

Java Native Interface (JNI) 允许java调用本地方法(native method),如果你搞过c/c++的话可能对他们很熟悉。因为本地方法无法保证安全,也无法保证性能,而且也会损失跨平台性,所以如果有java领域的实现,尽量不要使用本地方法。

37. 谨慎的进行优化

这是一个老生常谈的问题,因为优化带来的复杂性往往超过的优化带来的好处。 尤其是编写良好的代码,其实在代码级别可以优化的地方是很少的。如果需要优化,请首先找到性能的瓶颈部分,然后进行部分优化。

38. 遵守普遍接受的命名惯例

这部分也是老生常谈的话题,这部分可以参考一下阿里出的Java编程手册,上面还是比较全面的。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部