Effective Java(8)

effective-java.jpg

文章摘要

Effective Java第8章 通用程序设计 学习笔记

通用程序设计

45 将局部变量作用域最小化

目的

增强代码的可读性维护性,并降低 出错的可能性。

方法

  • 在局部变量第一次使用的地方声明。
  • 几乎每个局部变量的声明都应该包含一个初始化表达式。
  • 使方法小而集中

举例

  • try-catch
  • for循环与while循环对比

46 for-each循环优先于传统的for循环

for循环缺点

  • 迭代器索引变量在每个循环中出现三次,其中有两次很容易出错,且编译器不能发现错误。

    for-each循环的优点

  • 通过完全隐藏迭代器或者索引变量,避免了混乱或者出错的很可能。

  • 利用for-each循环不会有性能损失,在某些情况下,还稍有性能优势。因其对数组索引的边界值只计算一次。
  • 在对多个集合进行嵌套式迭代时,for-each循环能避免for循环可能出现的问题,同时兼顾代码简洁。

    传统for循环嵌套迭代

    enum Face {ONE, TWO, THREE, FOUR, FIVE, SIX}  
    ...
    Collection<Face> faces = Array.asList(Face.values());
    
    for (Iterator<Face> i = faces.iterator(); i.hasNext; )
        for (Iterator<Face> j = faces.iterator(); j.hasNext; )
            Sysytem.out.println(i.next() + " " + j.next());
    

    for-each嵌套迭代

    for (Suit suit : suits)
        for (Rank rank : ranks)
            deck.add (new Card (suit, rank));
    
  • for-each循环不仅能遍历集合和数组,还能遍历任何实现Iterable接口的对象。

    有三种常见的情况无法使用for-each循环

  1. 过滤——需要遍历集合,并删除选定的元素。
  2. 转换——需要遍历列表或数组,并取代它部分或全部的元素值。
  3. 平行迭代——需要并行的遍历多个集合。

47 了解和使用类库

类库中值得注意的两个工具

  • java.util包中Collections Framework(集合框架)
  • java.util.concurrent

48 如果需要精确的答案,请避免使用float和double

  • floatdouble 执行二进制浮点运算,没有提供完全精确的结果,尤其不适合用于货币计算。
  • 使用BigDecimalint 或者 long进行货币计算。

    • 使用BigDecimal缺点:与基本运算类型相比,不方便,而且很慢;优点: 系统记录十进制小数点,允许完全控制舍入,数值超过18位数字。
    • 选用int或者 long取决于所涉及数值的大小,同时自己处理十进制小数点。数值没有超过9位数字用 int,数值没有超过18位数字用float

49 基本类型优于装箱基本类型

基本类型

举例:int,double,boolean

装箱基本类型

举例:Integer,Double,Boolean

区别

  • 基本类型只有值,而装箱基本类型则具有与它们的值不同的同一性(对象引用);
  • 基本类型只有功能完备的值,而每个装箱基本类型还有个非功能值:null
  • 基本类型通常比装箱基本类型更节省时间和空间。

注意

  • 对装箱基本类型使用 == 操作符几乎总是错误的;

    比较器错误地返回1;

    public int compare(Integer first, Interger second) {
        return first < second ? -1 : (first == sencond ? 0 : 1);
    }
    
  • 当一项操作中混合使用基本类型与装箱基本类型时,装箱基本类型会自动拆箱。如果null对象引用被拆箱,则抛出NullPointerException异常。

    抛出NullPointerException异常

    static Integer i;
    
    public static void main(String[] args) {
        if (i == 42)
            System.out.println("123");
    }
    
  • 循环中装箱基本类型的变量被反复装箱和拆箱,产生严重性能问题。

    Long sum = 0L;
    

    ​ for (long i = 0; i < Integer.MAX_VALUE; i++) {
    ​ sum += i;
    ​ }

50 如果其他类型更适合,则尽量避免使用字符串

不应该使用字符串的情形

  • 字符串不适合代替其他的值类型
  • 字符串不适合代替枚举类型
  • 字符串不适合代替聚集类型
  • 字符串不适合代替能力表(capabilities)

51 当心字符串连接的性能

由于字符串不可变,在连接字符串时,它们的内容都要被拷贝。如果数量巨大,应使用StringBuilderappend方法。

52 通过接口引用对象

  • 优先使用接口而不是类来引用对象。如果有合适的接口类型存在,参数、返回值、变量和域都应该用接口类型进声明。

    里氏替换原则

    //通过接口引用对象 
    List<String> lists = new ArrayList<String>(); 
    //通过类引用对象 ×
    ArrayList<String> lists = new ArrayList<String>();
    
  • 如果没有合适的接口存在,用类来引用对象。
    • 情形一 值类,例如StringBigInteger。值类通常是final的,很少会用多个实现,并且很少有对应的接口。
    • 情形二 对象属于框架,框架的基本类型为类不是接口,例如java.util.TimerTask。使用相关的基类引用对象,不用实现类。
    • 情形三 类实现了接口,但提供了接口中不存在的额外方法,例如LinkedHashMap

53 接口优于反射机制

反射机制的弊端

  • 丧失了编译时类型检查的好处。
  • 执行反射访问的代码冗长。
  • 性能损失。

54 55 56 待续