变长参数概念

在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用。形如 function(T …args)。但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组。

看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class VariVargs {
public static void main(String []args)
{
test("hello","welcome","hi");
}
public static void test(String ...args)
{
for(String string:args)
{
System.out.println(string);
}
}
}

将上述代码编译后,再反编译得到的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.PrintStream;

public class VariVargs
{
public static void main(String[] args)
{
test(
new String[] { "hello", "welcome", "hi" });
} public static void test(String[] args) {
String[] arrayOfString = args; //here
int j = args.length;
for (int i = 0; i < j; i++)
{
String string = arrayOfString[i]; //String string = arrayOfString[i];
System.out.println(string);
}
}
}

从上面的代码可以看出java的编译器将变长参数转换成了数组,并且显示的使用了arrayOfString指向了args。后面对变长参数的操作都转换为对arrayOfString的操作。(如果变长参数是Integer型的,数组名就是arrayOfInteger。其他类型类似)

变长参数非常容易理解和使用。但是在使用过程中也会遇到一些小的陷阱,但是只要我们理解了变长参数的本质,那么都能够很容易的理解和避免这些陷阱。

变长参数规则

调用方法时能与固定参数函数以及可变参数函数都匹配时。优先调用固定参数方法

在调用方法时能与固定参数函数以及可变参数函数都匹配时,JVM会优先匹配固定参数方法,下面举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class VariVargsTest1 {
//变长参数和固定参数同时存在
public static void main(String[] args) {
// TODO Auto-generated method stub
test("hello");
}
public static void test(String...args)
{
System.out.println("调用可变参数函数****");
}
public static void test(String test)
{
System.out.println("调用固定参数函数----");
}
}

执行代码,输出:调用固定参数函数—-

调用方法时,两个变长参数函数都匹配时无法通过编译

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class VariVargsTest2 {
//调用方法的参数与两个变长参数匹配,无法通过编译
public static void main(String[] args) {
// TODO Auto-generated method stub
test("hello"); //1
}
public static void test(String ...args)
{
System.out.println("变长参数1");
}
public static void test(String string,String...args)
{
System.out.println("变长参数2");
}
}

当没有1处代码时,程序是能够编译通过的。但是当添加了1处代码后无法通过编译,给出的错误是:The method test(String[]) is ambiguous for the type VariVargsTest2。编译器不知道选取哪个方法

一个方法只能有一个变长参数,且只能是参数列表的最后一个

由于规定变长参数只能是参数列表的最后一个。那么一个方法就不会有多个变长参数。

1
2
3
4
5
6
7
8
9
10
11
public class VariVargs3 {

  public static void main(String[] args) {
// TODO Auto-generated method stub }
public static void test(String...args1,Integer...args2) //1 编译出错
{ }
}

在代码1处编译出错:The variable argument type String of the method test must be the last parameter

变长参数的函数重载问题

首先我们回忆一下函数重载的定义和规则

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

重载规则:

  • 被重载的方法必须改变参数列表

  • 被重载的方法可以改变返回类型

  • 被重载的方法可以改变访问修饰符

  • 被重载的方法可以声明新的或更广的检查异常

  • 方法能够在同一个类中或者在一个子类中被重载

变长参数与数组重载

经过上面的分析,我们知道编译器将变长参数转换为了数组,所以这两个方法的参数列表是一样的,不满足重载的第一条规则,用一个小例子证明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class VariVargsTest41 {

  public static void main(String[] args) {
// TODO Auto-generated method stub }
//变长参数与数组重载
public static void test(String ...args)
{ }
public static void test(String []args)
{ }
}

上述代码无法通过编译:Duplicate method test(String[]) in type VariVargsTest41

可变参数方法与可变参数方法重载

上面已经分析过,这里不再说明

可变参数方法与无参方法的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
public class VariVargsTest4 {
public static void test(String ...args)
{
System.out.println("调用变长参数函数");
}
public static void test()
{
System.out.println("调用无参函数");
}
public static void main(String []args)
{
test(); //调用方法是无参的
}

输出:调用无参函数。

可变参数的重写问题

函数重写的定义和规则

重写(Override)是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。

重写规则:

  • 参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载

  • 返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载

  • 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)

  • 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

验证

我们先定义两个类:

1
2
3
4
5
6
7
public class Base {
public void test(String string)
{
System.out.println("Base");
} }
1
2
3
4
5
6
7
public class Child extends Base{
public void test(String ...args)
{
System.out.println("Child");
} }

Child类是Base类的子类

先看下面的测试程序:

1
2
3
4
5
6
7
8
public static void main(String []args)
{
Base base=new Child();
base.test("one");
Child child=new Child();
child.test("one");
child.test("one","two");
}

上述代码执行的输出结果是:

Base
Base
Child
base变量是Base类型的,当调用的test函数的参数只有一个时,便会调用Base类中的test(String string)方法。如果是base.test(“one”,”two”)这样调用的话,程序无法通过编译:The method test(String) in the type Base is not applicable for the arguments (String, String)

child变量是Child类型的,当调用的test函数的参数只有一个时,因为Child类继承了Base类,Child类继承了Base类中的test(String string)方法。Child有可变参数方法也有继承来的单参数方法,所以虚拟机会不管变长参数方法,而直接调用完全匹配的那个方法,输出“Base”。

总结

    • 变长参数确实方便了我们的编程,在使用变长参数时,我们需要明确的一点是:这货就是一颗语法糖,天啊我们一直是在对数组做操作

    • 我们在使用变长参数时,需要注意变长参数的使用规则

    • 注意变长参数方法的重载和重写问题

Java语法糖初探(三)--变长参数的更多相关文章

  1. Java基础12-工具类;变长参数;IO

    作业解析 取出整数的16进制表示形式 \u00ff /** * int2hex * */ public static String int2hex(int i) { String str = &quo ...

  2. Java语法糖详解

    语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更 ...

  3. Java语法糖设计

    语法糖 Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这 ...

  4. Java语法糖1:可变长度参数以及foreach循环原理

    语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的 ...

  5. java语法糖:(1)可变长度参数以及foreach循环原理

    语法糖 语法糖:是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用 ...

  6. java常量和变量的定义规则,变长参数的使用

    首先是定义的一般规则,类名首字母全部大写,常量全部大写用下划线分隔,变量用驼峰形式.注意使用long赋值用L时不能写小写的L要写大写的,不然会和数字“1”傻傻分不清. 下面是举例: public cl ...

  7. Java 语法糖详解

    语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法. 这种语法对语言的功能并没有影响,但是 ...

  8. 深入理解java虚拟机(十二) Java 语法糖背后的真相

    语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言 ...

  9. Java语法糖(一)

    概述 语法糖(Syntactic Sugar):主要作用是提高编码效率,减少编码出错的机会. 解语法糖发生在Java源码被编译成Class字节码的过程中,还原回简单的基础语法结构. 语法糖之一:泛型( ...

随机推荐

  1. net mvc webapi 实用

    asp.net mvc webapi 实用的接口加密方法   在很多项目中,因为webapi是对外开放的,这个时候,我们就要得考虑接口交换数据的安全性. 安全机制也比较多,如andriod与webap ...

  2. 【LOJ】#2106. 「JLOI2015」有意义的字符串

    题解 点一个技能点叫特征方程 就是 \(a_{n + 2} = c_1 a_{n + 1} + c_2 a_{n}\) \(x^2 = c_1 x + c_2\) 解出两根来是\(x_1,x_2\) ...

  3. hadoop2.6.4的HA集群搭建超详细步骤

    hadoop2.0已经发布了稳定版本了,增加了很多特性,比如HDFS HA.YARN等.最新的hadoop-2.6.4又增加了YARN HA 注意:apache提供的hadoop-2.6.4的安装包是 ...

  4. win7下再装Ubuntu双系统

    一.UltraISO制作U盘启动盘 1.1打开 UltraISO,单机“文件”,选择“打开”. 1.2然后单击“启动”,选择“写入硬盘映像”. 二.装Ubuntu 前面省略,直接到安装类型(我的安装好 ...

  5. Spring-Session实现Session共享入门教程

    任何一种技术的出现,都是来解决特定的问题的! 本篇开始学习Spring-Session相关的一些知识学习整理,让我们开始吧! Spring-Session介绍 Spring-Session使用的场景? ...

  6. odoo导入功能二开

    原来有的导入功能相信很多小伙伴对其功能不是很满意,不过没关系,我们可以二开啊,把它的功能改造成你想要的样子,接下来让我们看看怎么办吧 例如我想把员工导入功能中添加上用户同步注册功能 首先,我要找到原模 ...

  7. IEnumerable<T>

    IEnumerable 饮水思源 <C#本质论> Overview 根据定义,.Net 的中集合,本质上是一个类,它最起码实现了IEnumeraable 或者非泛型的IEnumerable ...

  8. 2010-2011 ACM-ICPC, NEERC, Moscow Subregional Contest Problem F. Finance 模拟题

    Problem F. Finance 题目连接: http://codeforces.com/gym/100714 Description The Big Boss Company (BBC) pri ...

  9. Training JTAG Interface

    For most embedded CPU architecture implementations, the JTAG port is used by the debugger to interfa ...

  10. kettle的基本介绍

    Kettle 主要内容: 一.ETL介绍 二.Kettle介绍 三.Java调用Kettle API 一.ETL介绍 1. ETL是什么? 1).ETL分别是“Extract”.“ Transform ...