第47条:了解和使用类库
Top 100 Java Libraries on Github 2016
Library Number of Projects Type % of projects
junit.junit 2412 Testing 62.45% Total Projects Tested 3,862
org.slf4j.slf4j-api 895 Logging 23.17% Unique Repos Used 12,059
com.google.guava.guava 650 Utilities 16.83% Total Records 47,251
log4j.log4j 615 Logging 15.92% Avg. repos per project 12.23
commons-io.commons-io 537 Utilities 13.90%
org.slf4j.slf4j-log4j12 479 Logging 12.40%
org.mockito.mockito-all 394 Mocks 10.20%
commons-lang.commons-lang 369 Utilities 9.55%
ch.qos.logback.logback-classic 342 Logging 8.86%
org.apache.commons.commons-lang3 327 Utilities 8.47%
javax.servlet.servlet-api 323 Java Extension 8.36%
org.apache.httpcomponents.httpclient 295 Web 7.64%
org.springframework.spring-context 290 Utilities 7.51%
com.fasterxml.jackson.core.jackson-databind 277 Parsing 7.17%
commons-codec.commons-codec 260 Utilities 6.73%
org.mockito.mockito-core 260 Mocks 6.73%
org.springframework.spring-test 259 Testing 6.71%
joda-time.joda-time 248 Utilities 6.42%
com.google.code.gson.gson 246 Parsing 6.37%
org.testng.testng 234 Testing 6.06%
第48条:如果需要精确的答案,请避免使用float和double
float和double类型主要是为了科学计算和工程计算而设计的。它们执行二进制浮点运算( binary floating-point arithmetic ),这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不适合用于货币计井,因为要让一个float或者double精确地表示0.1(或者10的任何其他负数次方值)是不可能的。
遗憾的是,它输出的结果是$0.61000000000000000001.
总而言之,对于任何需要精确答案的计算任务,请不要使用float或者double。如果你想让系统来记录十进制小数点,并且不介意因为不使用基本类型而带来的不便,就请使用BigDecimal。使用BigDecima!还有一些额外的好处,它允许你完全控制舍人,每当一个操作涉及舍入的时候,它允许你从8种舍入模式中选择其一。如果你正通过法定要求的舍入行为进行业务计算,使用Bigpecimal是非常方便的。如果性能非常关键,并且你又不介意自己记录十进制小数点,而且所涉及的数值又不太大,就可以使用int或者long。如果数值范围没有超过9位{进制数字,就可以使用int;如果不超过18位数字,就可以使用long。如果数值可能超过18位数字,就必须使用BigDecimal.
第49条:基本类型优先于装箱基本类型
这个程序运行起来比预计的要慢一些,因为它不小心将一个局部变量(sum)声明为是装箱基本类型Long, 而不是基本类型long. 程序编译起来没有错误或者警告,变量被反复地装箱和拆箱,导致明显的性能下降。
那么什么时候应该使用装箱基本类型呢?它们有几个合理的用处。第一个是作为集合中的元素、键和值。你不能将基本类型放在集合中,因此必须使用装箱基本类型。这是一种更通用的特例。在参数化类型中,必须使用装箱基本类型作为类型参数,因为Java不允许使用基本类型。例如,你不能将变量声明为ThreadLocal
第50条:如果其他类型更适合,则尽量避免使用字符串
经常被错误地用字符串来代替的类型包括基本类型、枚举类型和聚集类型。
-
宇符串不适合代替其他的值类型。
-
字符串不适合代替枚举类型。
-
字符串不适合代替聚集类型。
如果一个实体有多个组件,用一个字符串来表示这个实体通常是很不恰当的。例如,下面这行代码来自于真实的系统—标识符的名称已经被修改了,以免发生纠纷:
这种方法有许多缺点。如果用来分隔域的字符也出现在某个域中,结果就会出现混乱。为
了访问单独的域,必须解析该字符串,这个过程非常慢,也很繁琐,还容易出错。你无法提供equals, toString或者compareTo方法,只好被迫接受String提供的行为。更好的做法是,简单地编写一个类来描述这个数据集,通常是一个私有的静态成员类(见第22条).
第53条:接口优先千反射机制
核心“反封机制(core reflection facility) java.lang.reflect, 提供了“通过程序来访问关于已装载的类的信息”的能力。给定一个CIass实例,你可以获得Constructor, Method和Field实例,分别代表了该Class实例所表示的类的Constructor(构造器)、Method(方法)和Field(域)。这些对象提供了“通过程序来访问类的成员名称、域类型、方法签名等信息”的能力。
而且,Constructor, Method和Field实例使你能够通过反扮机制操作它们的底层对等体:通过调用Constructor, Method和Field实例上的方法,可以构造底层类的实例、调用底层类的方法,并访问底层类中的域。例如,Method.invoke使你可以调用任何类的任何对象上的任何方法(遵从常规的安全限制)。反射机制(reflection)允许一个类使用另一个类,即使当前者被编译的时候后者还根本不存在。然而,这种能力也要付出代价:
- 丧失了编译时类型检查的好处
包括异常检查。如果程序企图用反射方式调用不存在的或者不可访问的方法,在运行时它将会失败,除非采取了特别的预防措施. - 执行反射访问所需要的代码非常笨拙和冗长
编写这样的代码非常乏味,阅读起来也很困难。 - 性能损失
反射方法调用比普通方法调用慢了许多。具体慢了多少,这很难说,因为受到了多个因素的影响。在我的机器上,速度的差异可能小到2倍,也可能大到50倍。
有一些复杂的应用程序需要使用反射机制。这些示例中包括类浏览器、对象监视器、代码分析工具、解释型的内嵌式系统。在RPC(远程过程调用)系统中使用反射机制也是非常合适的。
第54条:谨慎地使用本地方法
Java Native Interface (JNI)允许Java应用程序可以调用本地方法( native method ) ,所谓本地方法是指用本地程序设计语言(比如C或者C++)来编写的特殊方法。本地方法在本地语言中可以执行任意的计算任务,并返回到Java程序设计语言。
从历史上看,本地方法主要有三种用途。它们提供了“访问特定于平台的机制”的能力,比如访问注册表(registry)和文件锁(file lock)。它们还提供了访问遗留代码库的能力,从而可以访问遗留数据(legacy data)。最后,本地方法可以通过本地语言,编写应用程序中注重性能的部分,以提高系统的性能。
使用本地方法来提高性能的做法不值得提倡
使用本地方法有一些严重的缺点口因为本地语言不是安全的,所以,使用本地方法的应用程序也不再能免受内存毁坏错误的影响。因为本地语言是与平台相关的,使用本地方法的应用程序也不再是可自由移植的。使用本地方法的应用程序也更难调试。在进入和退出本地代码时,需要相关的固定开销,所以,如果本地代码只是做少量的工作,本地方法就可能降低(decrease)性能。最后一点,需要“胶合代码”的本地方法编写起来单调乏味,并且难以阅读。
第56条:遵守普遍接受的命名惯例
对于首字母缩写。到底应该全部大写还是只有首字母大写,没有统一的说法。虽然大写更常见一些,但还是强烈建议采用仅有首字母大写的形式:即使连续出现多个首字母缩写的形式,你仍然可以区分出一个单词的起始处和结束处。
下面这两个类名你更愿意看到哪一个,HTTPURL还是HttpUrl ?
类型参数名称通常由单个字母组成。这个字母通常是以下五种类型之一:T表示任意的类型,E表示集合的元素类型,K和V表示映射的键和值类型,X表示异常。任何类型的序列可以是T, U, V或者T1, T2, T3.
对于返回boolean值的方法。其名称往往以单词“is”开头,很少用has。