zoukankan      html  css  js  c++  java
  • 《Effective Java》读书笔记

    Chapter 8 General Programming

    Item 45: Minimize the scope of local variables

    local variables应该在他们要被用的地方声明,而不应该,比如,全部声明在方法开头。而且在声明的时候,记得要初始化。有个例外,比如你的IQueryable<T> localv需要在接下来的if else中分别初始化成不同的值,那么你可以在if之前先声明(我自己瞎举的例子)。
    如果某个local variable只是这个循环需要的,那么用for(包括foreach)比while好。因为比如最经典的for(int i = 1;i<N;i++)如果用while写的话,要在while块前面先写int i = 1,这样就很不好了,因为这个i在while执行完毕后就不需要了,这时候如果你不小心把另一个变量写成了i,那么编译器并不会报错,而for就会报错(因为i的生命周期已经结束了)。
    BTW,如果每次循环都需要计算某个值,而这个值每次都一样的话,可以放到for的“声明块”里去,比如:

    for (int i = 0, n = expensiveComputation(); i < n; i++) {
        doSomething(i);
    }
    

    Item 46: Prefer for-each loops to traditional for loops

    在1.5版本之前,遍历一个集合或数组的方式如下:

    // No longer the preferred idiom to iterate over a collection!
    for (Iterator i = c.iterator(); i.hasNext(); ) {
        doSomething((Element) i.next()); // (No generics before 1.5)
    }
    // No longer the preferred idiom to iterate over an array!
    for (int i = 0; i < a.length; i++) {
        doSomething(a[i]);
    }
    

    相比foreach的写法,上面这种老写法可读性较差,而且容易出错,foreach写法:

    // The preferred idiom for iterating over collections and arrays
    for (Element e : elements) {
        doSomething(e);
    }
    

    特别是在嵌套多层遍历的时候,foreach的优势更能体现,如下所示:

    // not preferred
    for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) {
        Suit suit = i.next();
        for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
            deck.add(new Card(suit, j.next()));
    }
    // Preferred idiom for nested iteration on collections and arrays
    for (Suit suit : suits)
        for (Rank rank : ranks)
            deck.add(new Card(suit, rank));
    

    只要是实现Iterable<E>接口的类,都可以用foreach去遍历,如果你写的类代表了a group of elements,那么你也可以让它实现这个接口。
    以下几种情况不能用foreach:
    一.如果你在遍历的时候需要删除某些元素,那么你只显式地用一个iterator,然后用它的remove方法。
    二.如果你需要修改某些元素的值。
    三.如果你要“平行地”遍历多个集合(或数组)的时候,比如:
    for (int i = 0,j = 0; i < a.length && j < b.length; i++,j++){...}

    Item 47: Know and use the libraries

    书上举得例子是Random.nextInt(int n),返回一个均匀分布的0到n的随机数,如果你自己实现这个方法的话,估计肯定分布得不均匀。所以说,用标准库的好处就在于你利用了写这个库的专家的知识和其他所有用过这个库的人的经验,而且不需要你重新发明轮子。
    每一个programmer都应该熟悉java.lang和java.util,最好也能掌握java.io,其他的package可以需要的时候再看。

    Item 48: Avoid float and double if exact answers are required

    float和double执行的是二进制浮点运算(binary floating-point arithmetic),能够提供较为精准的快速近似计算,主要用于科学计算和工程计算。所以他们并不提供精准的结果,所以不适用于需要精确结果的场合,尤其是货币。比如想要让一个float或double精确地表示0.1是不可能的,比如System.out.println(1.03 - .42)的输出结果是0.6100000000000001。解决办法是用BigDecimal, int,或long来代替,BigDecimal需要额外的性能开销和影响可读性,用int和long比较好,只是你需要自己处理好十进制小数点。但如果数值大于18位,就得用BigDecimal。

    Item 49: Prefer primitive types to boxed primitives

    对boxed primitives用 == 运算符几乎是肯定错误的,因为这时候比较的是identity,而不是内容。如果对一个primitive和一个boxed primitives作比较,那么boxed primitive被自动拆箱,如果它恰好是个null,那么就NullPointerException。所以说一定要分清到底是primitives还是boxed primitives。有一些情况下必须用boxed primitives:作为集合元素的是时候;作为泛型参数的时候;调用反射方法的时候。

    Item 50: Avoid strings where other types are more appropriate

    Strings不适合代替其他的类型。当数据从文件,网络,或键盘输入传进来的时候,经常是以字符串的形式存在,但是很多人就不管了。正确做法是应该把这种string representation转换成他们应该是的类型,比如如果是int,就转换成int。
    Strings不适合代替enum类型。
    如果你需要一个东西,它有多个组件,那么就老老实实写个类,别用String偷懒,比如:
    String compoundKey = className + "#" + i.next();
    这行代码的意思是,你需要一个key,由一个className和一个数字组成,然后由一个#作为分隔符隔开。这种做法非常的不好。
    不要用string来grant access to some functionality,书上举的例子是ThreadLocal,这个我看的不是很懂,但是如果你复习到这里也不用纠结了,跳过就好,相信我。

    Item 51: Beware the performance of string concatenation

    这个很熟悉了不解释了,需要的时候用StringBuilder吧。

    Item 52: Refer to objects by their interfaces

    在head first设计模式中也提过这点,这样做是为了更好的灵活性。当然这个原则不是绝对的,有很多情况下没法满足这个原则,比如String,Random,或者有时候只能用基类(一般都是抽象的)来作为引用类型。

    Item 53: Prefer interfaces to reflection

    使用反射虽然很强大,但是代价是:
    一.You lose all the benefits of compile-time type checking。
    二.代码冗长且可读性差。
    三.Performance suffers。
    而这个item的意思是:如果某个类在编译时不存在,所以你不得不用反射来创建他的实例的时候,你可以用一个interface类型(当然,必须是这个类实现的interface,或者继承的某个super class)来refer to它的对象,从而既得到反射的强大,也可以从一定程度上得到编译时的好处,比如你可以先用反射创建一个对象,但用一个编译时类型来refer to它:

    Set<String> s = (Set<String>)Class.forName(...).newInstance();
    

    一旦创建完成这个对象,这里这个s就和其他任何Set<String>类型的对象没任何区别了,你可以,比如对s进行添加元素的操作:s.add(...),而不需要用反射来调用这个方法。

    Item 54: Use native methods judiciously

    The Java Native Interface (JNI)允许Java应用程序调用本地方法(native methods),一般用于一些platform-specific facilities,或performance-critical parts(不建议这么做,因为JVM已经越来越成熟了)。尽量不要用本地方法,第一它们不安全,不受JVM保护;第二会影响移植性,第三难以调试并且会造成额外的性能开销(在进和出本地方法的时候)。

    Item 55: Optimize judiciously

    关于优化有这么一条真理:it is easy to do more harm than good。性能可能会与设计构架相冲突,不要为了性能而牺牲合理的结构,并且必须在设计过程中考虑性能(如果构架成型后,除非全部重写系统,否则很难再改进性能)。比如,如果你决定让你的API返回一个mutable的东西,那么你每次都要进行defensive copy,就会影响性能。
    不要去计较性能上的一些小小的得失,在试着优化后要进行性能对比测试。
    Profiling tools(性能剖析工具)可以帮助你找到那些需要优化的地方。
    有很多因素会影响到性能,比如JVM的不同实现,不同的CPU等等。
    你的原则应该是:do not strive to write fast programs—strive to write good ones; speed will follow。

    Item 56: Adhere to generally accepted naming conventions

    Package names应该是小写字母(很少包括数字),然后把你的域名反着写一下作为开头,其余部分应该精简而有含义,比如util rather than utilities,一般都应该只包含一个单词或缩写。
    Class,interface names, including enum and annotation type names的首字母大写,尽量不要用缩写,如果要用的话,最好也是每个单词的首字母大写,比如HttpUrl。
    Method and field names的首字母小写。
    constant fields应该是全部大写,用下划线分开。enum constants就是。
    Local variable与方法命名类似,并且可以使用缩写。
    Type parameter一般就一个字母:T代表任意type, E代表一个集合的element,K代表key ,V代表value,X代表exception. 一堆不同的泛型参数可以用T, U, V或T1, T2, T3。

  • 相关阅读:
    firstResponder
    形而上学
    du -h
    数据本地存贮与数据结构(对象)
    RFC
    oc语言源代码
    HTTP1.1协议中文版-RFC2616
    如何提高团队协作的效率
    iOS应用架构谈 开篇
    nginx版本如何选择?
  • 原文地址:https://www.cnblogs.com/raytheweak/p/7224651.html
Copyright © 2011-2022 走看看