Java 程序经验小结: 慎用可变参数

社区
1、写在开头

在 Java1.5 发行版中,加入了可变参数列表方法,称为 variable arity method(可匹配不同长度的变量的方法)。

可变参数机制:通过创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传到数组中,最后将数组传递给方法。

2、可变参数使用

我们通过例子简单了解可变参数的使用:


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

复制代码

再看一个例子,我们可以在代码运行时检测可变参数数组长度:

 private int min (int... args) throws IllegalAccessException {    if (args.length == 0){      throw new IllegalAccessException("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 循环。

有种更好的方法实现上面没有的效果:方法参数改为,一个是指定类型的正常参数,另一个是这种类型的 varagas 参数。


  private int min(int firstArg, int... args){    int min = firstArg;    for (int i = 1; i < args.length; i++){      if (args[i] < min){        min = args[i];      }    }    return min;  }

复制代码

3、可变参数对 Arrays.asList 的影响

在 Java1.5 发行版之前,打印数组内容常用做法是:


List<String> homophones = Arrays.asList("a", "b");System.out.println(homophones);

复制代码

因为数组的元素类型是包装类 String,因此从 Object 继承了它们的 toString 实现,这是有效的。

int[] args = {3,1,4,1,5,9,2,6,5,4};System.out.println(Arrays.asList(args));

复制代码

但如果数组元素是基本类型,我们尝试这么做,在 java 发行版 1.4 会抛出异常。

如果是 java 发行版 1.5 之后,程序运行结果会产生无意义字符串:[[I@27bc2616]。


  private static void testArraysAsList(){    int[] args = {3,1,4,1,5,9,2,6,5,4};    System.out.println(Arrays.toString(args));  }

复制代码

如果是 java 发行版 1.5 之后,程序运行结果会产生无意义字符串:[[I@27bc2616]。

对此,java1.5 发行版给 Arrays 类补充完整的 Arrays.toString 方法(不是可变参数!),专门为了将任何类型的数组转变为字符串而设计。

4、可变参数与性能

在重视性能的情况下,使用可变参数机制要特别小心。可变参数方法的每次调用都会导致进行一次数组分配和初始化。

如果凭借经验无法承受这一成本,但又需要可变参数的灵活性,有一种模式可以实现:

假设某个方法 95% 调用会有 3 个或者更少的参数,就声明该方法的 5 个重载,每个重载方法带有 1 至 3 个普通参数,当参数的数目超过 3 个时,就使用一个可变参数方法。


public interface VarargsInterface {  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);  }

复制代码

EnumSet 类对它的静态工厂就是使用了这个方法,以最大限度的减少创建枚举集合的成本。

picture.image

5、总结

简而言之,在定义参数数目不定的方法时,可变参数是一种方便的方式。但是,我们不应该滥用可变参数,使用不当会产生混乱结果。

6、延伸阅读

《源码系列》

JDK之Object 类

JDK之BigDecimal 类

JDK之String 类

JDK之Lambda表达式

《经典书籍》

Java并发编程实战:第1章 多线程安全性与风险

Java并发编程实战:第2章 影响线程安全性的原子性和加锁机制

Java并发编程实战:第3章 助于线程安全的三剑客:final & volatile & 线程封闭

《服务端技术栈》

《Docker 核心设计理念

《Kafka史上最强原理总结》

《HTTP的前世今生》

《算法系列》

读懂排序算法(一):冒泡&直接插入&选择比较

《读懂排序算法(二):希尔排序算法》

《读懂排序算法(三):堆排序算法》

《读懂排序算法(四):归并算法》

《读懂排序算法(五):快速排序算法》

《读懂排序算法(六):二分查找算法》

《设计模式》

设计模式之六大设计原则

设计模式之创建型(1):单例模式

设计模式之创建型(2):工厂方法模式

设计模式之创建型(3):原型模式

设计模式之创建型(4):建造者模式

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论