编写高质量代码:改善Java程序的151个建议 --[26~36]

提防包装类型的null值

public static int testMethod(List<Integer> list) {
        int count = 0;
        for (Integer i : list) {
            count += (i != null) ? i : 0;
        }
        return count;
    }

包装对象和拆箱对象可以自由转换,这不假,但是要剔除null值,null值并不能转换为基本类型。对于此问题,我们谨记一点:包装类型参与运算时,要做null值校验。

谨慎包装类型的大小比较

public class Client {
    public static void main(String[] args) {
        Integer i = new Integer(1345);
        Integer j = new Integer(1345);
        compare(i, j);
    }

    public static void compare(Integer i, Integer j) {
        System.out.println(i == j);
        System.out.println(i > j);
        System.out.println(i < j);

    }
}

运行结果:
false
false
false

  1. ij:在java中""是用来判断两个操作数是否有相等关系的,如果是基本类型则判断值是否相等,如果是对象则判断是否是一个对象的两个引用,也就是地址是否相等,这里很明显是两个对象,两个地址不可能相等。
  2. i>j 和 i<j:在Java中,">" 和 "<" 用来判断两个数字类型的大小关系,注意只能是数字类型的判断,对于Integer包装类型,是根据其intValue()方法的返回值(也就是其相应的基本类型)进行比较的(其它包装类型是根据相应的value值比较的,如doubleValue,floatValue等),那很显然,两者不肯能有大小关系的。

优先使用整型池

(1)、new产生的Integer对象

    new声明的就是要生成一个新的对象,没二话,这是两个对象,地址肯定不等,比较结果为false。

(2)、装箱生成的对象

  对于这一点,首先要说明的是装箱动作是通过valueOf方法实现的,也就是说后两个算法相同的,那结果肯定也是一样的,现在问题是:valueOf是如何生成对象的呢?我们来阅读以下Integer.valueOf的源码:

 public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low));
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

cache是IntegerCache内部类的一个静态数组,容纳的是-128到127之间的Integer对象。通过valueOf产生包装对象时,如果int参数在-128到127之间,则直接从整型池中获得对象,不在该范围内的int类型则通过new生成包装对象。

不要随便设置随机种子

在Java中,随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个原则:
种子不同,产生不同的随机数
种子相同,即使实例不同也产生相同的随机数

import java.util.Random;

public class Client30 {
    public static void main(String[] args) {
        Random r = new Random();
        for(int i=1; i<=4; i++){
            System.out.println("第"+i+"次:"+r.nextInt());
            
        }
    }
}
Random r = new Random(1000);

1000为随机种子,运行多次,虽然实例不同,但都会获得相同的四个随机数。

在接口中不要存在实现代码

public class Client31 {
    public static void main(String[] args) {
        //调用接口的实现
        B.s.doSomeThing();
    }
}

// 在接口中存在实现代码
interface B {
    public static final S s = new S() {
        public void doSomeThing() {
            System.out.println("我在接口中实现了");
        }
    };
}

// 被实现的接口
interface S {
    public void doSomeThing();
}

在B接口中声明了一个静态常量s,其值是一个匿名内部类(Anonymous Inner Class)的实例对象,就是该匿名内部类(当然,也可以不用匿名,直接在接口中是实现内部类也是允许的)实现了S接口。

静态变量一定要先声明后赋值

静态变量是在类初始化的时候首先被加载的,JVM会去查找类中所有的静态声明,然后分配空间,注意这时候只是完成了地址空间的分配,还没有赋值,之后JVM会根据类中静态赋值(包括静态类赋值和静态块赋值)的先后顺序来执行。对于程序来说,就是先声明了int类型的地址空间,并把地址传递给了i,然后按照类的先后顺序执行赋值操作,首先执行静态块中i = 100,接着执行 i = 1,那最后的结果就是 i =1了。

不要覆写静态方法

避免在构造函数中初始化其它类

public class Client35 {
    public static void main(String[] args) {
        Son son = new Son();
        son.doSomething();
    }
}

// 父类
class Father {
    public Father() {
        new Other();
    }
}

// 相关类
class Other {
    public Other() {
        new Son();
    }
}

// 子类
class Son extends Father {
    public void doSomething() {
        System.out.println("Hi, show me Something!");
    }
}

构造方法循环调用

posted @ 2018-07-24 08:56  西北野狼  阅读(385)  评论(0编辑  收藏  举报