在逛 programcreek 的时候,我发现了一些专注基础但不容忽视的主题。比如说:Java 的可变参数究竟是怎么一回事?像这类灵魂拷问的主题,非常值得深入地研究一下。

以前很不重视基础,觉得不就那么回事嘛,会用就行了。就比如说今天这个主题,管它可变不可变呢,不就是个参数嘛,还能有多大学问——抱着这种态度,我一直横行江湖近十载(苦笑)。可等到读者找我提一些基础的问题时,我几乎回答不上来,感觉知识是散的,或者是浮于表面的。幸好最近一段时间,我开始幡然醒悟,开始不放过任何一个细节,渐渐地,有点“知识储备”了。

好了,牛逼吹完,让我们来步入正题。Java 的可变参数究竟是怎么一回事?

可变参数是 Java 1.5 的时候引入的功能,它允许方法使用任意多个、类型相同(is-a)的值作为参数。就像下面这样。

public static void main(String[] args) {
    print("沉");
    print("沉", "默");
    print("沉", "默", "王");
    print("沉", "默", "王", "二");
}

public static void print(String... strs) {
    for (String s : strs)
        System.out.print(s);
    System.out.println();
}

静态方法 print() 就使用了可变参数,所以 print("沉") 可以,print("沉", "默") 也可以,甚至 3 个、 4 个或者更多个字符串都可以作为参数传递给 print() 方法。

说到可变参数,我想起来阿里巴巴开发手册上有这样一条规约。


意思就是尽量不要使用可变参数,如果要用的话,可变参数必须要在参数列表的最后一位。既然坑位有限,只能在最后,那么可变参数就只能有一个(悠着点,悠着点)。如果可变参数不在最后一位,IDE 就会提示对应的错误,如下图所示。


那可变参数是怎么工作的呢?

原理也很简单。当使用可变参数的时候,实际上是先创建了一个数组,该数组的大小就是可变参数的个数,然后将参数放入数组当中,再将数组传递给被调用的方法

这就是为什么可以使用数组作为参数来调用带有可变参数的方法的根本原因。代码如下所示。

public static void main(String[] args) {
    print(new String[]{"沉"});
    print(new String[]{"沉", "默"});
    print(new String[]{"沉", "默", "王"});
    print(new String[]{"沉", "默", "王", "二"});
}

public static void print(String... strs) {
    for (String s : strs)
        System.out.print(s);
    System.out.println();
}

那如果方法的参数是一个数组,然后像使用可变参数那样去调用方法的时候,能行得通吗?大家感兴趣的话,不妨试一试(行不通,嘘)。

那一般什么时候使用可变参数呢?

可变参数,可变参数,顾名思义,当一个方法需要处理任意多个相同类型的对象时,就可以定义可变参数。Java 中有一个很好的例子,就是 String 类的 format() 方法,就像下面这样。

System.out.println(String.format("年纪是: %d", 18));
System.out.println(String.format("年纪是: %d 名字是: %s", 18, "沉默王二"));

PS:%d 表示将整数格式化为 10 进制整数,%s 表示输出字符串。

如果不使用可变参数,那需要格式化的参数就必须使用“+”号操作符拼接起来了。麻烦也就惹祸上身了。

在实际的项目代码中,开源包 slf4j.jar 的日志输出就经常要用到可变参数(log4j 就没法使用可变参数,日志中需要记录多个参数时就痛苦不堪了)。就像下面这样。

protected Logger logger = LoggerFactory.getLogger(getClass());
logger.debug("名字是{}", mem.getName());
logger.debug("名字是{},年纪是{}", mem.getName(), mem.getAge());

查看源码就可以发现,debug() 方法使用的可变参数。

public void debug(String format, Object... arguments);

那在使用可变参数的时候有什么注意事项吗?

有的,有的。我们要避免重载带有可变参数的方法——这样很容易让编译器陷入自我怀疑中。

public static void main(String[] args) {
    print(null);
}

public static void print(String... strs) {
    for (String a : strs)
        System.out.print(a);
    System.out.println();
}

public static void print(Integer... ints) {
    for (Integer i : ints)
        System.out.print(i);
    System.out.println();
}

这时候,编译器完全不知道该调用哪个 print() 方法,print(String... strs) 还是 print(Integer... ints),傻傻分不清。


假如真的需要重载带有可变参数的方法,就必须在调用方法的时候给出明确的指示,不要让编译器去猜。

public static void main(String[] args) {
    String [] strs = null;
    print(strs);

    Integer [] ints = null;
    print(ints);
}

public static void print(String... strs) {
}

public static void print(Integer... ints) {
}

上面这段代码是可以编译通过的。因为编译器知道实参是 String 类型还是 Integer 类型,只不过为了运行时不抛出 NullPointerException,两个 print() 方法的内部要做好判空的操作。


好了,各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了

五分钟学Java:可变参数究竟是怎么一回事?的更多相关文章

  1. 五分钟学Java:如何才能学好Java Web里这么多的技术

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 系列文章介绍 本文是<五分钟学Java>系列文章的一篇 本系列文章主要围绕Java程序员必须掌握的核心技能,结合我个人三年 ...

  2. 五分钟学Java:如何学习Java面试必考的JVM虚拟机

    原创声明 本文首发于微信公众号[程序员黄小斜] 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 为什么要学习JVM虚拟机 最近的你有没有参加Java面试呢?你有没有发现,Java ...

  3. 五分钟学Java:如何学习Java面试必考的网络编程

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 简介 Java作为一门后端语言,对于网络编程的支持是必不可少的,但是,作为一个经常CRUD的Java工程师,很多时候都不 ...

  4. 五分钟学Java:打印Java数组最优雅的方式是什么?

    在逛 Stack Overflow 的时候,发现了一些访问量像‎安第斯山一样高的问题,比如说这个:打印 Java 数组最优雅的方式是什么?访问量足足有 220W+,想不到啊,这么简单的问题竟然有这么多 ...

  5. 五分钟学Java:一篇文章搞懂spring和springMVC

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在什么时候?相信很多人和我一样,第一次了 ...

  6. 五分钟学Java:一篇文章带你搞懂spring全家桶套餐

    原创声明 本文首发于微信公众号[程序员黄小斜] 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在 ...

  7. Java可变参数 & Python可变参数 & Scala可变参数

    Java 可变参数的特点: (1).只能出现在参数列表的最后: (2)....位于变量类型和变量名之间,前后有无空格都可以: (3).调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体 ...

  8. Java可变参数/可变长参数

    Java可变参数/可变长参数 传递的参数不确定长度,是变长的参数,例如小例子: package demo; public class Demo { public static int sum(int ...

  9. java可变参数

    Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理.注意:可变参数必须位于最后一项.当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持 ...

随机推荐

  1. Java“类”的内存分配_case1

  2. 关于使用 Java 分片读\写文件

    分片读取文件方法: /** * 分片读取文件块 * * @param path 文件路径 * @param position 角标 * @param blockSize 文件块大小 * @return ...

  3. oracle函数 CONVERT(c1,set1,set2)

    [功能]将源字符串c1 从一个语言字符集set2转换到另一个目的set1字符集 [参数]c1,字符串,set1,set2为字符型参数 [返回]字符串 [示例] select convert('stru ...

  4. Python基础:17类和实例之一(类属性和实例属性)

    1:类通常在一个模块的顶层进行定义.对于Python来说,声明与定义类是同时进行的. 2:类属性仅与其类相绑定,类数据属性仅当需要有更加“静态”数据类型时才变得有用,这种属性是静态变量.它们表示这些数 ...

  5. Python基础:15私有化

    默认情况下,属性在Python 中都是“public”. 1:双下划线(__) Python 为类元素(属性和方法)的私有性提供初步的形式.由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许 ...

  6. AtCoder Beginner Contest 077 D Small Multiple(最短路)

    水过前三道题之后,一直在写这个题,做不对.总有那么几组数据过不去... 看了看题解是最短路,这思路感觉很神奇.看了下唯一做出来这题的那人的代码,是搜索做的. 标程: 对每个数字x,向x+1建一条花费为 ...

  7. [\s\S]*?懒惰模式特殊情形

    通常理解[\s\S]*?X (X代表任意指定字符) 表示匹配任何字符的懒惰模式,一旦遇到后面出现的X便停止匹配,但实际不是如此,会尽可能的把后面的内容也匹配进去.如: 表达式 <tr[\s\S] ...

  8. 传说中Python最难理解的点|看这完篇就够了(装饰器)

    https://mp.weixin.qq.com/s/B6pEZLrayqzJfMtLqiAfpQ 1.什么是装饰器 网上有人是这么评价装饰器的,我觉得写的很有趣,比喻的很形象 每个人都有的内裤主要是 ...

  9. BERT-Pytorch demo初探

    https://zhuanlan.zhihu.com/p/50773178 概述 本文基于 pytorch-pretrained-BERT(huggingface)版本的复现,探究如下几个问题: py ...

  10. poj 1689 && zoj 1422 3002 Rubbery (Geometry + BFS)

    ZOJ :: Problems :: Show Problem 1689 -- 3002 Rubbery 这题是从校内oj的几何分类里面找到的. 题意不难,就是给出一个区域(L,W),这个区域里面有很 ...