Effective.Java第56-66条(规范相关)

56.  为所有已公开的API元素编写文档注释

  要正确地记录API,必须在每个导出的类、接口、构造方法、方法和属性声明之前加上文档注释。如果一个类是可序列化的,还需要记录它的序列化形式。

  文档注释在源代码和生成的代码中都应该是可读的通用原则。

  类或接口中的两个成员方法或构造方法不应具有相同的概要描述。

  总结起来就是一句话,文档注释要规范。

 

阿里规约如下:

【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用 // xxx 方式。
【强制】所有的抽象方法(包括接口中的方法) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
【强制】所有的类都必须添加创建者和创建日期。
【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途

 

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

  最重要的技术就是在使用变量之前再首次声明变量。

  几乎每个局部变量都应该包含一个初始化器。

 

58.  for-each循环优于普通for循环

  for-each官方称为增强for循环。如下:看到冒号(:)读作in。下面的循环读作对于elements中的每个元素e。

for (Element e: elements) {
            
}

 

有三种情况不能使用for-each循环:

有损过滤:如果需要遍历集合并删除元素不能用,需要使用迭代器。

转换:如果需要转换集合的部分元素或值,需要用到下标的话需要用普通for循环。

并行迭代:如果需要并行遍历多个集合,那么需要显示地控制迭代器或索引遍历。可以使用普通for循环。

 

  for-each循环在清晰度,灵活性和错误预防方面提供了超越传统for循环的优势,而且没有性能损失。

 

59.  了解并使用库

  通过使用标准库,你可以利用编写它的专家的知识和以前使用者的经验。

  每个程序员都应该熟悉java.lang、java.util和java.io的基础知识以及其子包。其实java.util.concurrent包也是非常重要的。

  第三方jar包的话有commons-lang、commons-io、commons-collections相关jar包。

  

  总之,不要重复造轮子。如果你需要做一些看起来相当常见的事情,那么库中可能已经有一个工具可以做你想做的事情。

 

60.  若需要精确答案就避免使用float和double

  float和double不能再需要精确计算的地方使用,尤其是货币计算。

比如:

        float num1 = 1.6F;
        float num2 = 0.9255F;
        System.out.println(num1 - num2);

结果:

0.67450005

 

解决办法:使用BigDecimal,注意使用其String类型的构造方法不要使用float或double,避免在计算中引入不准确的值。

        float num1 = 1.6F;
        float num2 = 0.9255F;

        BigDecimal bidnumber1 = new BigDecimal(String.valueOf(num1));
        BigDecimal bidnumber2 = new BigDecimal(String.valueOf(num2));

        BigDecimal subtract = bidnumber1.subtract(bidnumber2);
        float floatValue = subtract.floatValue();
        System.out.println(floatValue);

 

  总之,对于任何需要精确答案的计算,不使用float或double。如果希望系统处理十进制小数点,并且不介意不使用基本类型带来的不便和成本,就使用BigDecimal。使用BigDecimal的另一个好处是,它可以完全控制舍入模式。

 

61.  基本数据类型优于包装类型

  自动装箱和自动拆箱模糊了基本类型和包装类型之间的区别,但不会消除它们。主要区别有下面三条:

第一:基本类型只有它们的值,而包装类型具有与值不同的标识,换句话说,两个相同包装类型具有相同的值和不同的标识。

第二:基本类型只有全功能值,而包装类型除了基本类型的所有功能值外,还有一个非功能值,即null。(将 == 操作符应用于包装类型几乎都是错误的)

第三:基本类型比包装类型更节省时间和空间。

 

  在操作中混合使用基本类型和包装类型时,包装类型就会自动拆箱,如果一个空对象自动拆箱会引发NPE空指针异常。如下:

        Long sum = 0L;
        for (int i = 0; i < 500; i++) {
            sum += i;
        }

  这个程序比它预期的慢的多,因为它意外地声明了一个局部变量(sum),它是包装类型而不是基本类型。变量会被反复的装箱和拆箱,导致产生明显的性能问题。

 

什么情况下应该使用包装类型?

  第一个是作为集合中的元素、键值;第二个是进行反射方法调用时必须使用包装类型。

 

62.  当使用其他类型更合适时应避免使用字符串

  字符串类型是其他值类型的糟糕替代品。比如int、float就用其类型表示,不能用字符串表示;表示是或否就用布尔类型。

  字符串是枚举类型的糟糕替代品。

  字符串是聚合类型的糟糕替代品。

  总之,当存在或可以编写更好的类型时,应避免使用字符串来表示对象。

 

63.    当心字符串链接引起的性能问题

  字符串链接操作(+)是将几个字符串拼接在一起的简便方法。我们也知道字符串的拼接操作是通过生成StringBuilder并调用其append方法进行拼接。

  当拼接项足够多时,考虑使用StringBuilder的append方法代替。

 

64.  通过接口引用对象

  如果存在合适的接口类型,那么应该使用接口类型声明参数、返回值、变量和字段。唯一真正需要引用对象的类的时候是使用构造函数创建它的时候。

  如果养成使用接口引用对象的习惯,将会更加灵活。如果决定切换实现,只需在构造函数切换类名(或使用不同的静态工厂)

  注意:如果原实现提供了接口约定的其他特殊方法,并且依赖于该功能,那么只能用该类接收对象。比如:如下代码只能用LinkedList接收对象引用。

        LinkedList<String> lists = new LinkedList<>();
        lists.peekFirst();

 

  如果没有合适的接口存在,那么类引用对象是完全合适的。比如String等。

 

65.  接口优于反射

  核心反射机制 java.lang.reflect 提供对任意类的编程访问。给定一个 Class 对象,你可以获得Constructor、Method 和 Field 实例,分别代表了该 Class 实例所表示的类的构造器、方法和字段。这些对象提供对类的成员名、字段类型、方法签名等的编程访问。
  此外,Constructor、Method 和 Field 实例允许你反射性地操作它们的底层对应项:你可以通过调用 Constructor、Method 和 Field 实例上的方法,可以构造底层类的实例、调用底层类的方法,并访问底层类中的字段。例如,Method.invoke 允许你在任何类的任何对象上调用任何方法(受默认的安全约束)。

  反射允许一个类使用另一个类,即使在编译前者时后者并不存在。然而,这种能力是有代价的:

(1)失去了编译时类型检查的所有好处,包括异常检查。

(2)执行反射访问所需的代码笨拙又冗长。

(3)性能降低。反射比普通方法调用慢得多。

   有一些复杂的程序需要用到反射,比如代码分析工具和依赖注入框架。

  通过非常有限的形式使用反射,你可以获得反射的许多好处,同时花费的代价很少。

  反射的合法用途(很少)是管理类对运行时可能不存在的其他类、方法或字段的依赖关系。如果编写的程序必须在编译时处理未知 类,则应该尽可能使用反射实例化对象,并在编译时已知的接口或超类访问对象。(比如静态工厂的反射创建对象)

 

66.  明智审慎地本地方法

  Java本地接口(JNI)允许Java调用本地方法,这些方法是c或c++编写而成。本地方法一般有三种用途:提供对特定于平台的设施的访问、提供对现有本地代码库的访问、通过本地语言辨析应用程序中注重性能的部分以提高性能。

  为了提高性能,很少建议使用本地方法。

  

 

posted @ 2019-06-25 22:07  QiaoZhi  阅读(305)  评论(0编辑  收藏  举报