天天看点

3分钟快速阅读-《Effective Java》(五)

41.慎用重载

使用重载时有些重载方法并不会根据你想要的方式来进行运行的,编译器会根据自己想要的最简便的方式来运行对应的方法,如下所示
public class CollectionClassFind {

    public static String classFind(List<?> list){
        return "List";
    }
    public static String classFind(Set<?> set){
        return "Set";
    }
    public static String classFind(Collection<?> collection){
        return "Collection";
    }

    public static void main(String[] args) {
        Collection<?>[] collections = {
          new HashSet(),new ArrayList(), new HashMap().values()
        };
        for (Collection collection : collections) {
            System.out.println(classFind(collection));
        }
        //理想结果:List Set Collection
        //实际结果:Collection Collection Collection
    }
}
      
简而言之,能够重载方法并不意味着应该重载方法.如果一定要进行方法重载,那么就要考虑是否会被编译器所忽略

42.慎用可变参数

当我们需要定义参数数目不定的方法时,可变参数时一种很方便的方式,但是不应该被过度滥用,参数列表不应该只有一个可变参数,避免调用方一个参数也不传导致程序出现问题.应该如下所示,用一个固定参数来进行区分
  • 错误做法
public static void main(String[] args) {
        int sum = sum();
        System.out.println(sum);
    }
    public static int sum(int... args){
        int sum = 0;
        for (int arg : args) {
            sum += arg;
        }
        return sum;
    }
      
  • 正确做法
public static void main(String[] args) {
        int sum = sum(1, 3, 4, 5, 6, 7, 8, 9);
        System.out.println(sum);
    }
    public static int sum(int first,int... args){
        for (int arg : args) {
            first = first + arg;
        }
        return first;
    }
      

43.返回0长度的数组或者集合,而不是null

如果方法返回集合是抛给一个null,那么就会需要调用方来处理这个问题,此时我们应该尽可能的自己来解决这个异常,以避免给调用者带来不必要的麻烦
private final List<Cheese> cheeses = new ArrayList<>(10);
    public List<Cheese> getCheeses(){
        if(cheeses.size() == 0){
            return null;
        }
        return cheeses;
    }
      
private final List<Cheese> cheeses = new ArrayList<>(10);
    public List<Cheese> getCheeses(){
        if(cheeses.size() == 0){
            return null;
        }
        return Collections.EMPTY_LIST;
    }
      

44.为所有导出的API元素编写文档注释

这一条没什么好解释的,需要我们习惯性的去执行即可

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

原文释义:早期程序的设计会要求在方法开始的时候就把所有需要用到的局部变量都进行生命,但是这样会导致程序的可读性降低,最好的做法是当局部变量需要用到的时候去使用它.
其实我觉得如果每个方法都能非常规范的把局部变量的声明写在最前面,这样也是可方便方法的阅读的.但是如果想要真正的提高一个方法的可读性,就需要我们把方法设计的小而集中,这才是解决问题的本质做法

46.foreach循环优先于传统的for循环

使用foreach循环好处是相比于传统for循环并没有性能损耗,并且比较不容易出现BUG或者写错循环次数导致索引越界的问题
  • 以下三种情况无法使用foreach
    • 过滤:需要在集合当中删除选定的元素
    • 转换:需要取代集合当中的某个元素
    • 平行迭代:需要并行的遍历多个集合,需要显示控制迭代器或者索引变量,一遍所有迭代器或者索引变量都可以得到同步前移

47.了解和使用类库

引用原文:不要重新发明轮子,如果你要做的事情看起来是十分常见的
Java发展至今正常可能遇到的需求工具类,不要着急自己写一个,首先先找Java自带的util包下看看有没有,如果没有就到谷歌的common.lang3下的util包下,或者Spring社区当中也出了很多的工具包.只要是Spring相关的项目,引入简便,使用起来也十分轻松

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

public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        BigDecimal sum = BigDecimal.ZERO;
        for(int i =0; i<=10000;i++){
            sum = sum.add(new BigDecimal(1));
        }
        long end = System.currentTimeMillis();
        System.out.println("BigDecimal总耗时:"+(end - begin)+"结果:"+sum);//5ms

        long begin1 = System.currentTimeMillis();
        double sum1 = 0;
        for(int i =0; i<=10000;i++){
            sum1 = sum1 + 1;
        }
        long end1 = System.currentTimeMillis();
        System.out.println("double总耗时:"+(end1 - begin1)+"结果:"+sum1);//0ms

    }
      

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

  • 49.1 装箱基本类型属于引用类型,会在堆空间开辟一块新的内存空间,相比于基本类型,更加消耗性能,当计算次数越大,差别将会越明显
public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        Integer sum = 0;
        for(int i =0; i<=1000000;i++){
            sum = sum + 1;
        }
        long end = System.currentTimeMillis();
        System.out.println("Integer总耗时:"+(end - begin)+"ms,结果:"+sum);//10ms

        long begin1 = System.currentTimeMillis();
        int sum1 = 0;
        for(int i =0; i<=1000000;i++){
            sum1 = sum1 + 1;
        }
        long end1 = System.currentTimeMillis();
        System.out.println("int总耗时:"+(end1 - begin1)+"ms, 结果:"+sum1);//0ms
    }
      
  • 49.2 装箱基本类型和基本数据类型混合使用的时候,JVM编译器的自动拆装箱容易导致空指针异常,所以使用时记得类型的一致性保证
  • 49.3 装箱基本类型使用==比较的结果是比较两者在堆空间的地址值,这是不准确的

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

  • 50.1 字符串不适合代替其他的值类型.不要试图用String去替代int,double,boolean这些基本类型就能完成的事情
  • 50.2 字符串不适合代替枚举类型.枚举类型的一个大的特点就是简洁且安全,相比之下字符串都不具有这样的优势
  • 50.3 字符串不适合代替聚集类型.即是不要使用String的+号来拼接而形成一个新的字符串