参考链接: Java中的可变参数(Varargs)

Java可变参数

Java语言在

JDK1.5中首次推出Java可变参数

,variable arguments,或简称varargs。这一新语言特征给软件开发人员在编写方法重载时提供了方便和灵活性。但可变参数的应用并不像想象的那么简单,使用时有其特殊要求和局限性。

重载的最好例子

读者朋友可能有这样的编程经历:在编写一个方法时,其参数随着程序运行的条件而变化,在编译期间无法确定。具体地讲,例如编写一个打印参加聚会party的程序,其中方法printInvitation()将根据作为参数的参加人姓名,打印邀请卡。但这个参数的数量事先并不确定。当然可以编写许多重载的方法来解决这个问题,如:

void printInvitation(String name);

void printInvitation(String name1, String name2);

void printInvitation(String name1, String name2, String name3);

...

问题是编写多少个重载的方法才可以解决给所有参加者打印邀请卡?也许需要改变您的程序设计,而使用数组或者链接表了。

应用可变参数可以方便、灵活地解决这类问题。例如:

//完整程序存在本书配套资源目录Ch12名为VarargsTest.java

void printInvitation(String...names) {

for (String name : names) {

makeCard(name);             //调用方法按照姓名打印邀请卡

System.out.println("Recording info: invitation card has been printed for " + name);

}

}

这里,

(String...names)便是可变参数。它包括从0到任意个相同类型的参数。在编译期间,这个可变参数将被转换为字符串数组形式

,即:

void printInvitation(String[] names)

如下是调用这个方法的例子:

printInvitation("李刚", "David Smith");

printInvitation("Greg Wu", "Paul Nguyen", "Liu Wei", "张新");

printInvitation();          //无参数

当在无参数情况下调用这个方法时,将不执行任何这个方法中的代码。

如下是运行结果:

Recording info: invitation card has been printed for 李刚

Recording info: invitation card has been printed for David Smith

Recording info: invitation card has been printed for Greg Wu

Recording info: invitation card has been printed for Paul Nguyen

Recording info: invitation card has been printed for Liu Wei

Recording info: invitation card has been printed for 张新

怎样工作

Java可变参数也不神秘。实际上,JVM将根据程序中调用这个方法时提供的参数数量,来装载和运行它。

可变参数的简单语法格式为:

methodName([argumentList], dataType...argumentName);

其中:

argumentList——普通参数,可选项。

dataType——数据类型或者类。自动转换成dataType代表的数组。

...——Java的操作符。表示0到多个。必须是3个点。

argumentName——参数名。

注意,可变参数必须在最后。

下面是应用可变参数的更多例子:

// 完整程序存在本书配套资源目录Ch12名为VarargsTest.java

public static int sumInts(int...numbers) {  //可变整数数组类型参数

int sum = 0;

for (int num : numbers)

sum +=num;

return sum;

}

再如:

public void totalTax(String name, double rate, double...amount) {                                               //普通参数在前、可变参数在后

double total = 0.0,

tax = 0.0;

for (double amount : amounts)

total += amount;

tax = total * rate;

System.out.println("Name: " + name + "\nTotal: " + total + "\ntax: " + tax);

}

Java可变参数也可应用在构造器中。例如:

public class Supper {

public Supper(char...characters) {

...

}

在子类中,可以覆盖这个构造器,如:

class SubClass extends Supper {

public SubClass(char...characters) {

...

}

}

但无法在子类中调用超类的这个构造器。

更多信息  可变参数可以用在构造器中,并可以覆盖。

Java可变参数方法重载

可以对具有可变参数的方法重载。如下例子:

void someMethod(int count, double...prices) {

//语句体

...

}

void someMethod(double...prices) {              //重载

//语句体

...

}

double someMethod(String...names) {             //重载

//语句体

...

}

...

对方法someMethod()实行重载。对具有可变参数的方法重载遵循一般方法重载原则。

如下程序应用枚举和可变参数,对在12.4.4讨论过的根据用户要求打印跑车信息的程序进一步修改,使之具有更强功能。首先,在程序中加入了如下两个用来处理跑车颜色和付款方式的枚举类型对象:

//完整程序存在本书配套资源目录Ch12名为VarargsApp.java

enum ColorType {

WHITE {String getDescription(){

return "有浅白、暗白、和亮白可选";

}

},

SILVER {String getDescription() {

return "有银白、银灰、纯银色可选";

}

},

BLACK {String getDescription() {

return "有深黑和浅黑可选";

}

};

abstract String getDescription();

}

enum PaymentType {

CASH("有10%特别优惠"),

CREDIT("接受所有信用卡"),

LOAN("贷款利息为.56%");

final private String payment;

private PaymentType(String payment) {

this. payment = payment;

}

public String getPayment() {

return payment;

}

}

另外,在SportCar中,根据用户的选择,加入了对跑车类型、颜色,以及付款方式的处理方法,并且利用可变参数。例如:

class SportCar {

SportCarType type;                      //创建

ColorType color;

PaymentType payment;

public SportCar (String...choices) {    //可变参数

type = null;                        //初始化

color = null;

payment = null;

processInfo(choices);               //调用处理信息方法

}

private void processInfo(String[] choices) {

if (choices.length == 1) {           //处理跑车类型

processType(choices[0]);

}

else if (choices.length == 2) {      //处理跑车类型和颜色

processType(choices[0]);

processColor(choices[1]);

}

else if (choices.length == 3) {      //处理跑车类型、颜色和付款方式

processType(choices[0]);

processColor(choices[1]);

processPayment(choices[2]);

}

}

private void processType(String type) {    //处理类型

if (type.equals("P"))

this.type = SportCarType.PORSCHE;

else if (type.equals("F"))

this.type = SportCarType.FERRARI;

else if(type.equals("J"))

this.type = SportCarType.JAGUAR;

}

...

这个类的驱动程序如下:

public class VarargsApp {

public static void main( String args[] ) {

SportCar yourCar = new SportCar("P");          //创建一个参数的对象

System.out.println("你要的跑车信息:\n" + yourCar + "\n");

SportCar myCar = new SportCar("J", "S");       //创建两个参数的对象

System.out.println("我要的跑车信息:\n" + myCar + "\n");

SportCar herCar = new SportCar("F", "B", "C");//创建三个参数的对象

System.out.println("她要的跑车信息:\n" + herCar + "\n");

}

}

运行结果如下:

你要的跑车信息:

制造国:德国

价格:$120,000.00

我要的跑车信息:

制造国:英国

价格:$110,000.00

有银白、银灰、纯银色可选

她要的跑车信息:

制造国: 意大利

价格:$150,000.00

有深黑和浅黑可选

有10%特别优惠

在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用,例如print("hello");print("hello","lisi");print("hello","张三", "alexia");下面介绍如何定义可变长参数 以及如何使用可变长参数。  1. 可变长参数的定义

使用...表示可变长参数,例如  print(String... args){     ...  }  在具有可变长参数的方法中可以把参数当成数组使用,例如可以循环输出所有的参数值。  print(String... args){     for(String temp:args)        System.out.println(temp);  }  2. 可变长参数的方法的调用  调用的时候可以给出任意多个参数也可不给参数,例如:

print();  print("hello");  print("hello","lisi");  print("hello","张三", "alexia")  3. 可变长参数的使用规则  3.1 在调用方法的时候,如果能够和固定参数的方法匹配,也能够与可变长参数的方法匹配,则选择固定参数的方法。看下面代码的输出:

package com;

// 这里使用了静态导入

import static java.lang.System.out;

public class VarArgsTest {

public void print(String... args) {

for (int i = 0; i < args.length; i++) {

out.println(args[i]);

}

}

public void print(String test) {

out.println("----------");

}

public static void main(String[] args) {

VarArgsTest test = new VarArgsTest();

test.print("hello");

test.print("hello", "alexia");

}

}

3.2 如果要调用的方法可以和两个可变参数匹配,则出现错误,例如下面的代码:

package com;

// 这里使用了静态导入

import static java.lang.System.out;

public class VarArgsTest1 {

public void print(String... args) {

for (int i = 0; i < args.length; i++) {

out.println(args[i]);

}

}

public void print(String test,String...args ){

out.println("----------");

}

public static void main(String[] args) {

VarArgsTest1 test = new VarArgsTest1();

test.print("hello");

test.print("hello", "alexia");

}

}

对于上面的代码,main方法中的两个调用都不能编译通过,因为编译器不知道该选哪个方法调用,如下所示:

3.3 一个方法只能有一个可变长参数,并且这个可变长参数必须是该方法的最后一个参数  以下两种方法定义都是错误的。   public void test(String... strings,ArrayList list){     }    public void test(String... strings,ArrayList... list){     }

4. 可变长参数的使用规范

4.1 避免带有可变长参数的方法重载:如3.1中,编译器虽然知道怎么调用,但人容易陷入调用的陷阱及误区

4.2 别让null值和空值威胁到变长方法,如3.2中所示,为了说明null值的调用,重新给出一个例子:

package com;public class VarArgsTest1 {

public void print(String test, Integer... is) {

}

public void print(String test,String...args ){

}

public static void main(String[] args) {

VarArgsTest1 test = new VarArgsTest1();

test.print("hello");

test.print("hello", null);

}

}

这时会发现两个调用编译都不通过:

因为两个方法都匹配,编译器不知道选哪个,于是报错了,这里同时还有个非常不好的编码习惯,即调用者隐藏了实参类型,这是非常危险的,不仅仅调用者需要“猜测”该调用哪个方法,而且被调用者也可能产生内部逻辑混乱的情况。对于本例来说应该做如下修改:

public static void main(String[] args) {

VarArgsTest1 test = new VarArgsTest1();

String[] strs = null;

test.print("hello", strs);

}

4.3 覆写变长方法也要循规蹈矩

下面看一个例子,大家猜测下程序能不能编译通过:

package com;

public class VarArgsTest2 {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

// 向上转型

Base base = new Sub();

base.print("hello");

// 不转型

Sub sub = new Sub();

sub.print("hello");

}

}

// 基类

class Base {

void print(String... args) {

System.out.println("Base......test");

}

}

// 子类,覆写父类方法

class Sub extends Base {

@Override

void print(String[] args) {

System.out.println("Sub......test");

}

}

答案当然是编译不通过,是不是觉得很奇怪?

第一个能编译通过,这是为什么呢?事实上,base对象把子类对象sub做了向上转型,形参列表是由父类决定的,当然能通过。而看看子类直接调用的情况,这时编译器看到子类覆写了父类的print方法,因此肯定使用子类重新定义的print方法,尽管参数列表不匹配也不会跑到父类再去匹配下,因为找到了就不再找了,因此有了类型不匹配的错误。

这是个特例,覆写的方法参数列表竟然可以与父类不相同,这违背了覆写的定义,并且会引发莫名其妙的错误。

这里,总结下覆写必须满足的条件:

(1)重写方法不能缩小访问权限;

(2)参数列表必须与被重写方法相同(包括显示形式);

(3)返回类型必须与被重写方法的相同或是其子类;

(4)重写方法不能抛出新的异常,或者超过了父类范围的异常,但是可以抛出更少、更有限的异常,或者不抛出异常。

最后,给出一个有陷阱的例子,大家应该知道输出结果:

package com;

public class VarArgsTest {

public static void m1(String s, String... ss) {

for (int i = 0; i < ss.length; i++) {

System.out.println(ss[i]);

}

}

public static void main(String[] args) {

m1("");

m1("aaa");

m1("aaa", "bbb");

}

}

[转载] Java之可变参数相关推荐

  1. 论java中可变参数

    论java中可变参数 /*** ...可变参数* 当... 作为形参时,传入的实参是可变的* 在方法体内部,仍然可将可变参数当成数组使用* 如果传入的实参是多个单独的变量,程序会自动将多个单独的变量放 ...

  2. 灵魂拷问:Java的可变参数究竟是怎么一回事?

    这是 Java 极客技术的第 276 篇原创文章 在逛 programcreek 的时候,我发现了一些专注基础但不容忽视的主题.比如说:Java 的可变参数究竟是怎么一回事?像这类灵魂拷问的主题,非常 ...

  3. 五分钟学Java:可变参数究竟是怎么一回事?

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

  4. java的可变参数介绍_Java基础 可变参数介绍(转载)

    本文章转载自:潜龙在渊的博客 一.可变参数简介 在不确定参数的个数时,可以使用可变的参数列表. 1. 语法: 参数类型...(三个点) 例如: void printArray(Object...) 注 ...

  5. java——慎用可变参数列表

    说起可变参数,我们先看下面代码段,对它有个直观的认识,下方的红字明确地解释了可变参数的意思: 1 public class VarargsDemo{ 2 3 static int sum(int... ...

  6. 如何应用Java的可变参数

    http://developer.51cto.com/art/200906/132089.htm Java可变参数 Java语言在JDK1.5中首次推出Java可变参数,variable argume ...

  7. 八、Java的可变参数例子

    1.在Java中什么是可变参数 可变参数是在Java1.5中引入的特性.它准许一个方法 public static void main(String[] args) { print("a&q ...

  8. java+list可变参数_Java 可变参数

    众所周知, Java可以通过... 来给一个方法定义一个可变长度的参数. 例如 // 返回可变参数的个数 public void method(int ... i){ System.out.print ...

  9. 【Java】可变参数

    前言 C语言中,如果一个函数中可能会需要用到不同数量的参数,那么会在函数定义时使用-来省略一些参数来表示这个函数可能的参数数量是不确定的. 那么,Java在jdk1.5之后也实现了这个功能,现在来介绍 ...

最新文章

  1. java acm 统计元音_hdu 2027 统计元音 (java)
  2. 2015浙江财经大学ACM有奖周赛(一) 题解报告
  3. python类变量继承_python 类的成员及继承
  4. 线性表(二)——链表
  5. [jzoj 5775]【NOIP2008模拟】农夫约的假期 (前缀和+递推)
  6. 如何获得完美的调色板?完美的配色素材专辑拿走!
  7. 【Spark调优】内存模型与参数调优
  8. java判断字符串是否包含日文
  9. 【问题集合】3dsMax贴图模糊(2016)
  10. 一个简单的python例子(监控网页是否运行)
  11. Dell+win10 进入Bios界面设定u盘启动详细图解
  12. 【poj2248】 Addition Chains(迭代加深)
  13. 独立的荣耀将首次与华为打擂台,谁的折叠手机更有优势?
  14. world中英文字体分离、字符间距较大解决
  15. 耳机声音一边大一边小
  16. 霍兰德- 职业兴趣测评
  17. ubuntu 软件推荐
  18. Golang中defer的执行时机
  19. 来自 Qt 官网的呐喊
  20. DHL供应链将再投资2.18亿欧元加码中国市场

热门文章

  1. html怎么在jupyter编辑,jupyter home jupyter环境变量怎么设定
  2. jquery显示与隐藏效果
  3. 后序遍历二叉树(迭代 vs 递归)
  4. hdu 3333 Turing Tree 求区间内不同数的和——线段树解法
  5. python守护线程_Python之守护线程与锁
  6. 分治法 —— 折半查找(递归与非递归)
  7. 斐波那契数列(复习)
  8. java获取屏幕截图
  9. 1.4 高并发之线程和进程
  10. java中如何将string 转化成long