【03】Effective Java - 变量、类型、控制语句约定

1、将局部变量的作用域最小化

(1)在第一次使用它的地方声明

        过早的声明不仅会使它作用域扩大,而且结束也过晚

(2)声明的时候包含一个初始化表达式

        如果在初始的时候,没有足够的信息对变量进行有意义的初始化,可以推迟声明,当然try catch例外


 另外,一种使局部变量最小化的实践是

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


2、优先使用for循环替代while循环

enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX }

public class DiceRolls {
    public static void main(String[] args) {
        Collection<Face> faces = Arrays.asList(Face.values());

        for (Iterator<Face> i = faces.iterator(); i.hasNext(); )
            for (Iterator<Face> j = faces.iterator(); j.hasNext(); )
                System.out.println(i.next() + " " + j.next());

        // Preferred idiom for nested iteration on collections and arrays
//      for (Face face1 : faces)
//          for (Face face2 : faces)
//              System.out.println(face1 + " " + face2);
    }
}

  while容易出错,而且变量范围大

Iterator<Element> i = c.iterator();
while(i.hasNext()){
   doSomething(i.next());
}

for(Iterator<Element> i =c.iterator();i.hasNext();){
   doSomething(i.next());
}
...

Iterator<Element> i2 = c2.iterator();
//BUG,拷贝出错了
while(i.hasNext()){
   doSomething(i.next());
}

//compile error
for(Iterator<Element> i2 =c2.iterator();i.hasNext();){
   doSomething(i.next());
}

 

3、for-each优先于传统的for循环

(1)传统的迭代器和索引
for(Iterator i = c.iterator();i.hasNext();){
   doSomething(i.next());
}

for(int i=0;i<a.length;i++){
   doSomething(a[i]);
}

//迭代器和索引变量容易引起一些混乱,特别是循环多层嵌套的情况下

(2)for-each避免出错
public class NestedIteration {
    public static void main(String[] args) {
        Collection<Suit> suits = Arrays.asList(Suit.values());
        Collection<Rank> ranks = Arrays.asList(Rank.values());

        List<Card> deck = new ArrayList<Card>();
        //BUG,NoSuchElementException
        for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )
            for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
                deck.add(new Card(i.next(), 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));
    }
}

(3)无法使用for循环的情况

A、过滤条件,可以使用iterator的remove方法

B、需要使用索引值的情况


4、精确值避免使用float和double

(1)float和double不适合货币计算

它们执行二进制浮点运算,为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的,但没有提供精确的结果,不适合需要精确值的货币计算。


(2)使用BigDecimal,int,long进行货币计算

用BigDecimal替代double,不好的地方就是与基本类型做比较相对麻烦,而且比较慢。

public class Arithmetic {
    public static void main(String[] args) {
        System.out.println(1.03 - .42);
        System.out.println();

        System.out.println(1.00 - 9 * .10);
        System.out.println();

        howManyCandies1();
        System.out.println();

        howManyCandies2();
        System.out.println();

        howManyCandies3();
    }

    // Broken - uses floating point for monetary calculation!
    public static void howManyCandies1() {
        double funds = 1.00;
        int itemsBought = 0;
        for (double price = .10; funds >= price; price += .10) {
            funds -= price;
            itemsBought++;
        }
        System.out.println(itemsBought + " items bought.");
        System.out.println("Change: $" + funds);
    }

    public static void howManyCandies2() {
        final BigDecimal TEN_CENTS = new BigDecimal( ".10");

        int itemsBought = 0;
        BigDecimal funds = new BigDecimal("1.00");
        for (BigDecimal price = TEN_CENTS;
             funds.compareTo(price) >= 0;
             price = price.add(TEN_CENTS)) {
            itemsBought++;
            funds = funds.subtract(price);
        }
        System.out.println(itemsBought + " items bought.");
        System.out.println("Money left over: $" + funds);
    }

    public static void howManyCandies3() {
        int itemsBought = 0;
        int funds = 100;
        for (int price = 10; funds >= price; price += 10) {
            itemsBought++;
            funds -= price;
        }
        System.out.println(itemsBought + " items bought.");
        System.out.println("Money left over: "+ funds + " cents");
    }
}

(3)选择建议

A、对于需要精确值的,请不要用float和double

B、BigDecimal的好处是可以进行四舍五入的控制

C、如果数值范围没有超过9位的十进制数,可以使用int,如果不超过18位的十进制数,使用long,超过18位的,必须使用BigDecimal

D、如果性能比较关键,自己又不介意记录小数值,涉及的数又不太大的话,可以使用int或long


5、基本类型优于装箱基本类型

(1)区别

A、基本类型只有值,而装箱基本类型有引用的概念,允许值相等但是引用不等

B、基本类型只有功能完备的值,而装箱类型,有null值

C、基本类型比装箱类型更节省时间和空间


(2)对装箱类型使用==几乎都是错误的
Integer i;
....
if(i == 42){
   
}

//可能报空指针错误,因为如果没有赋值,若是类成员,默认为null,不可以用== 跟int比较

//但另外,装箱类型与原始类型进行比较,只要不是null,可以自动拆箱

//Integer first,Integer second
if(first == second){
   //这里是引用比较,不是值比较
}


(3)使用装箱类型的情况

A、作为集合元素、键值时

B、参数泛型时


6、有其他更合适类型时,避免使用字符串

(1)字符串不适合替代其他的值类型

A、如果是数值,用int、float、BigInteger类型

B、如果是布尔,用true/false


(2)字符串不适合替代枚举类型

   枚举类型更适合用来表示常量


(3)字符串不适合替代聚集类型
String compoundKey = className + "#" + i.next();

   这种情况,容易出现结果混乱,通常解析的时候,过程相对缓慢,繁琐,容易出错,使用Scala的Case类的模式匹配正好。


(4)字符串不适合替代能力表

  尽量使用泛型


7、当心字符串的连接性能

(1)不可变性

   为连接n个字符串而且重复地使用字符串连接操作符,需要n的平方级的时间

(2)使用StringBuilder替代

   在方法里头,用StringBuilder,如果是类成员范围的,如果线程安全的话,需要StringBuffer,但一般不怎么在类成员范围内使用。因而StringBuilder足够。


8、通过接口引用对象

List<String> list = new Vector<String>();

//优于
Vector<String> list = new Vector<String>();

  同理,在参数、返回值类型也适用。这样做的目的,是可以更改实现,特别是对外部系统暴露接口时,更改实现的时候,不需要调用方更改类型。


9、接口优先于反射

(1)反射的代价

A、丧失了编译时类型检查的好处

B、执行反射访问所需要的代码非常笨拙和冗长

C、性能损失


(2)建议

A、反射功能只是在设计时被用到,普通程序不应该在运行时以反射访问对象

     比如容器初始化阶段、比如在开发阶段用代码生成器生成增删改查方法

B、以有限形式使用,可以获得许多好处的情况下,可以使用

     比如远程服务框架等


10、慎用本地方法

(1)本地方法用途

A、提供访问特定于平台的机制,比如访问注册表等

B、提供访问遗留代码库的能力

C、通过本地语言提升系统性能

    这点不值得提倡,容易导致不可移植,以及代码维护性问题


(2)缺点

A、本地语言不安全

B、本地方法难以调试

C、胶合代码难以阅读


11、接受普遍命名规则

(1)包名应该是层次性的

      com.sun等,以Internet域开头

(2)包名鼓励使用有意义的缩写

     使用util替代utilities

(3)对于缩写字符,建议仅首字母大写

    使用HttpUrl替代HTTPURL

(4)常量用大写加下划线

    NEGATIVE_INFINITY

(5)泛型常用的表达字母

    T表示任意的类型,E表示集合的元素类型,K和V表示映射的键值类型,X表示异常

    类型序列可以使用T,U,V,或者T1,T2,T3

(6)几种常见命名

A、执行动作的方法,常用动词或动词短语了来命名,比如append,drawImage

B、布尔值,常以is开头后面跟名称或名称短语,很少用has

     isDigit,isProbablePrime,isEmpty,isEnabled,hasSiblings

C、类型转换,使用toType命名,比如toString,toArray

D、返回视图,使用asType,比如asList

E、返回基本类型,使用typeValue,比如intValue

F、静态工厂经常使用valueOf,of,getInstance,newInstance,getType,newType


12、谨慎进行优化

(1)API设计时考虑性能

    特别是设计数据结构的时候,如果合理,可以避免不变要的join

(2)优化前后要进行测量

     考虑优化的重心在哪里



你可能感兴趣的:(【03】Effective Java - 变量、类型、控制语句约定)