Effective Java 第三版——59. 熟悉并使用Java类库

Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,因此JDK 最好下载 JDK 9以上的版本。java

Effective Java, Third Edition

59. 熟悉并使用Java类库

假设想要生成0到某个上界之间的随机整数。对于这个常见的任务,许多程序员会编写一个相似这样的方法:git

// Common but deeply flawed!
static Random rnd = new Random();

static int random(int n) {
    return Math.abs(rnd.nextInt()) % n;
}

这个方法可能看起来不错,但它有三个缺陷。 首先,若是n是一个比较小的2的乘方,则随机数的序列将在至关短的时间段后开始重复。 第二个缺陷是,若是n不是2的乘方,平均而言,某些数字会比其余数字出现得更加频繁。 若是n很大,这种效果可能很是明显。 如下程序有力地证实了这一点,该程序在精心选择的范围内生成了100万个随机数,而后打印出有多少个数字落在范围的上半部分:程序员

public static void main(String[] args) {
    int n = 2 * (Integer.MAX_VALUE / 3);
    int low = 0;
    for (int i = 0; i < 1000000; i++)
        if (random(n) < n/2)
            low++;
    System.out.println(low);
}

若是random方法正常工做,程序将打印接近50万的数字,但若是运行它,你会发现它打印的数字接近666,666。random方法生成的三分之二数字落在其范围的上半部分!github

random方法的第三个缺陷是,在极少数状况下,它可能会灾难性地失败,返回超出指定范围以外的数字。 这是由于该方法尝试经过调用Math.absrnd.nextInt()返回的值映射到非负整数。 若是nextInt()返回Integer.MIN_VALUE,则Math.abs也会返回Integer.MIN_VALUE,假设n不是2的乘方,取模运算符(%)将返回负数。 这几乎确定会致使程序失败,而且可能难以重现。web

要编写一个纠正这些缺陷的random方法的版本,你必须知道关于伪随机数生成器,数论和二进制补码算法的知识。幸运的是,你没必要这样作 —— 它已经为你完成了,就是Random.nextInt(int)方法。你没必要关心它如何完成其​​工做的细节(,若是您很好奇,能够研究文档或源代码)。一位具备算法背景的高级工程师花费了大量时间来设计,实现和测试这种方法,而后向该领域的几位专家展现,以确保其正确性。而后,这个类库通过了beta测试,发布,并被数百万程序员普遍使用了近二十年。该方法还没有发现任何缺陷,但若是发现了缺陷,将在下一个版本中修复。经过使用标准类库,能够利用编写类库专家的知识以及在前人使用它的经验。算法

从Java 7开始,就不该再使用Random了。 对于大多数用途,选择的随机数生成器如今是ThreadLocalRandom。 它产生更高质量的随机数,并且速度很是快。 在个人机器上,它比Random快3.6倍。 对于fork-join池和并行流的应用,请使用SplittableRandoms编程

使用这些类库的第二个好处是,没必要浪费时间为那些与你的工做关联不大的问题上,而去编写专门的解决方案。若是像大多数程序员同样,那么宁愿将时间花在应用程序上,而不是底层内容上。api

使用标准类库的第三个优势是,它们的性能会随着时间的推移而不断提升,而你无需付出任何努力。 由于许多人使用它们而且由于它们被用于行业标准基准测试,因此提供这些类库的组织有强烈的动力使它们运行得更快。 多年来,许多Java平台类库都通过重写,有时甚至是重复编写,从而显着提高性能。多线程

使用类库的第四个优势是它们倾向于随着时间的推移不断增长功能。 若是某个类库遗失了某些东西,开发人员社区就会知道它,而且可能会在后续版本中添加缺乏的功能。并发

使用标准库的最后一个好处是,能够将代码放在主流中。这样的代码更容易被开发人员阅读、维护和重用。

鉴于全部这些优势,使用类库设施优先于专门实现彷佛是合乎逻辑的,但许多程序员并不这样作。为何不呢? 也许他们不知道类库工具设施的存在。 在每一个主要版本中,都会向类库中添加许多特性,了解这些新增特性是很是值得的。每次有Java平台的主要版本发布时,都会发布一个web页面来描述它的新特性。这些页面很是值得一读[Java8-feat, Java9-feat]。为了强调这一点,假设你想编写一个程序来打印命令行中指定的URL的内容(这大体与Linux系统下curl命令相同)。 在Java 9以前,这段代码有点乏味,但在Java 9中,transferTo方法被添加到InputStream中。 如下是使用此新方法执行此任务的完整程序:

// Printing the contents of a URL with transferTo, added in Java 9
public static void main(String[] args) throws IOException {
    try (InputStream in = new URL(args[0]).openStream()) {
        in.transferTo(System.out);
    }
}

这些类库太大了,以致于没法学习全部文档[Java9-api],但每一个程序员都应该熟悉java.langjava.utiljava.io及其子包的基础知识。 能够根据须要获取其余类库的知识。 总结类库设施超出了本条目的范围,这些设施多年来已经发展得很是庞大。

几个类库特别值得一提。 集合Collection框架和流Stream类库(条目4——-48)应该是每一个程序员的基本工具包的一部分,java.util.concurrent中的并发实用程序的也应如此。 该软件既包含了用于简化多线程编程任务的高级实用程序,还包括偏底层的原语,以容许专家编写本身的高级并发抽象。 条目 80和81会讨论java.util.concurrent的高级部分。

有时,类库设施可能没法知足你的需求。需求越专门化,发生这种状况的可能性就越大。虽然第一个冲动应该是使用这些类库,可是若是已经了解了它们在某些领域提供的功能,而这些功能不能知足你的需求,那么可使用另外一种实现。任何有限的类库集所提供的功能老是存在漏洞。若是你在Java平台库中找不到你须要的东西,你的下一个选择应该是寻找高质量的第三方库,好比谷歌的优秀的开源Guava类库[Guava]。若是没法在任何适当的类库中找到所需的功能,可能别无选择,你只能本身实现了。

总而言之,不要从新发明轮子。 若是须要作一些彷佛应该至关常见的事情,那么类库中可能已经有了一个能够知足你需求的工具。 若是有,请使用它; 若是不知道,请检查。 通常来讲,类库代码可能比您本身编写的代码更好,而且可能会随着时间的推移而改进。 这并不反映你做为程序员的能力。 规模经济决定了类库代码获得的关注远远超过大多数开发人员能够承担的相同的功能。

相关文章
相关标签/搜索