Tips

书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code

注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

53. 明智而审慎地使用可变参数

可变参数方法正式名称称为可变的参数数量方法『variable arity methods』 [JLS, 8.4.1],接受零个或多个指定类型的参数。 可变参数机制首先创建一个数组,其大小是在调用位置传递的参数数量,然后将参数值放入数组中,最后将数组传递给方法。

例如,这里有一个可变参数方法,它接受一系列int类型的参数并返回它们的总和。如你所料,sum(1,2,3)的值为6,sum()的值为0:

// Simple use of varargs

static int sum(int... args) {
int sum = 0;
for (int arg : args)
sum += arg;
return sum;
}

有时,编写一个需要某种类型的一个或多个参数的方法是合适的,而不是零或更多。 例如,假设要编写一个计算其多个参数最小值的方法。 如果客户端不传递任何参数,则此方法定义不明确。 你可以在运行时检查数组长度:

// The WRONG way to use varargs to pass one or more arguments!

static int min(int... args) {
if (args.length == 0)
throw new IllegalArgumentException("Too few arguments");
int min = args[0];
for (int i = 1; i < args.length; i++)
if (args[i] < min)
min = args[i];
return min;
}

该解决方案存在几个问题。 最严重的是,如果客户端在没有参数的情况下调用此方法,则它在运行时而不是在编译时失败。 另一个问题是它很难看。 必须在args参数上包含显式有效性检查,除非将min初始化为Integer.MAX_VALUE,否则不能使用for-each循环,这也很难看。

幸运的是,有一种更好的方法可以达到预期的效果。 声明方法采用两个参数,一个指定类型的普通参数,另一个此类型的可变参数。 该解决方案纠正了前一个示例的所有缺陷:

// The right way to use varargs to pass one or more arguments

static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for (int arg : remainingArgs)
if (arg < min)
min = arg;
return min;
}

从这个例子中可以看出,在需要参数数量可变的方法时,可变参数是有效的。可变参数是为printf方法而设计的,该方法与可变参数同时添加到Java 平台中,以及包括经过改造的核心反射机制。printf和反射机制都从可变参数中受益匪浅。

在性能关键的情况下使用可变参数时要小心。每次调用可变参数方法都会导致数组分配和初始化。如果你从经验上确定负担不起这个成本,但是还需要可变参数的灵活性,那么有一种模式可以让你鱼与熊掌兼得。假设你已确定95%的调用是三个或更少的参数的方法,那么声明该方法的五个重载。每个重载方法包含0到3个普通参数,当参数数量超过3个时,使用一个可变参数方法:

public void foo() { }

public void foo(int a1) { }

public void foo(int a1, int a2) { }

public void foo(int a1, int a2, int a3) { }

public void foo(int a1, int a2, int a3, int... rest) { }

现在你知道,在所有参数数量超过3个的方法调用中,只有5%的调用需要支付创建数组的成本。与大多数性能优化一样,这种技术通常不太合适,但一旦真正需要的时候,它是一个救星。

EnumSet的静态工厂使用这种技术将创建枚举集合的成本降到最低。这是适当的,因为枚举集合为比特属性提供具有性能竞争力的替换(performance-competitive replacement for bit fields)是至关重要的(条目 36)。

总之,当需要使用可变数量的参数定义方法时,可变参数非常有用。 在使用可变参数前加上任何必需的参数,并注意使用可变参数的性能后果。

Effective Java 第三版——53. 明智而审慎地使用可变参数的更多相关文章

  1. Effective Java 第三版——52. 明智而审慎地使用重载

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  2. Effective Java 第三版——55. 明智而审慎地返回Optional

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  3. Effective Java 第三版——32.合理地结合泛型和可变参数

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. Effective Java 第三版——45. 明智审慎地使用Stream

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  5. Effective Java 第三版——67. 明智谨慎地进行优化

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  6. Effective Java 第三版——66. 明智谨慎地使用本地方法

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  7. Effective Java 第三版——83. 明智谨慎地使用延迟初始化

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  8. 《Effective Java 第三版》目录汇总

    经过反复不断的拖延和坚持,所有条目已经翻译完成,供大家分享学习.时间有限,个别地方翻译得比较仓促,希望有疑虑的地方指出批评改正. 第一章简介 忽略 第二章 创建和销毁对象 1. 考虑使用静态工厂方法替 ...

  9. 《Effective Java 第三版》新条目介绍

    版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...

随机推荐

  1. [HNOI2016]序列(未通过)

    题解: 虽然知道有点问题但是并没有debug出来 发现错误了..相同元素的处理有错误 网上题解大都是分块..(hn怎么道道分块) 用最普通的思路,可以枚举每个点作为最小值,向左向右延伸 但是多组询问显 ...

  2. BZOJ1103 [POI2007]大都市meg dfs序 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...

  3. 练习题|网络编程-socket开发

    1.什么是C/S架构? C指的是client(客户端软件),S指的是Server(服务端软件),C/S架构的软件,实现服务端软件与客户端软件基于网络通信. 2.互联网协议是什么?分别介绍五层协议中每一 ...

  4. 安装及配置sublime

    纯个人笔记,粗略编写,有时间再修改 参考:关于Sublime text3 配置及插件整理 参考:https://github.com/boontime/sublime-text3-config 参考: ...

  5. C# 组件模组引用第三方组件问题

    对接上一文章由于是动态加载指定程序集,会把当前目录下所有dll都加载进来.如果像sqlite这种第三组件调用了由C.C++非.net语言所以生成的Dll.因为自动生成的原因.会把非C#生成的dll都加 ...

  6. WebSocket协议解析

    WebSocket协议解析 转载请注明出处:WebSocket解析 现在,很多网站为了实现推送技术,所用的技术都是轮询.轮询是指在特定的时间间隔(如每一秒),由浏览器对服务器发起HTTP请求,然后由服 ...

  7. 洛谷P2242 公路维修问题

    To 洛谷.2242 公路维修问题 题目描述 由于长期没有得到维修,A国的高速公路上出现了N个坑.为了尽快填补好这N个坑,A国决定对M处地段采取交通管制.为了求解方便,假设A国的高速公路只有一条,而且 ...

  8. 洛谷.3273.[SCOI2011]棘手的操作(左偏树)

    题目链接 还是80分,不是很懂. /* 七个操作(用左偏树)(t2表示第二棵子树): 1.合并:直接合并(需要将一个t2中原有的根节点删掉) 2.单点加:把这个点从它的堆里删了,加了再插入回去(有负数 ...

  9. java的关键字与保留字

    1,Java 关键字列表 (依字母排序 共50组): abstract, assert, boolean, break, byte, case, catch, char, class, const(保 ...

  10. JUnit pass/failure/error区别

    pass:被测程序没有抛出异常,得到的是预期的值. failure:被测程序的逻辑有错误,得不到预期的值.执行了JUnit的断言. error:被测程序本身抛出异常,还没有执行到JUnit的断言就抛出 ...