变长参数概念

在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. 【BZOJ】4292: [PA2015]Równanie

    题解 \(f(n)\)的取值范围最多\(9^2 * 18\) 直接枚举判断就好 代码 #include <bits/stdc++.h> #define fi first #define s ...

  2. 004 jquery过滤选择器-----------(基本过滤选择器)

    1.介绍 2.常见基本过滤器 3.程序 <!DOCTYPE html> <html> <head> <meta charset="UTF-8&quo ...

  3. 访问url下载文件----python

    工作上有时候有需求,会下载pdf,doc,zip等文件,可以用以下方法,推荐使用第一种 下载文件: import urllib import urllib2 import requests url = ...

  4. Spring框架学习——Spring的体系结构详解

    1.Spring简介 Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题.它是一个分层的JavaSE/JavaEE ...

  5. Python之路,入门学习(一)

    一.变量\字符编码 声明变量 #_*_coding:utf-8_*_ name = "Alex Li" 上述代码声明了一个变量,变量名为: name,变量name的值为:" ...

  6. [代码审计]云优cms V 1.1.2前台多处sql注入,任意文件删除修复绕过至getshell

    0X00 总体简介 云优CMS于2017年9月上线全新版本,二级域名分站,内容分站独立,七牛云存储,自定义字段,自定义表单,自定义栏目权限,自定义管理权限等众多功能深受用户青睐,上线短短3个月,下载次 ...

  7. hihoCoder.1465.后缀自动机五 重复旋律8(后缀自动机)

    题目链接 \(Description\) 给定母串S,求模式串的循环同构串在S中的出现次数. \(Solution\) 将模式串s复制一遍,在母串的SAM上匹配,记录以每个位置作为后缀所能匹配的最大长 ...

  8. zoj 3659 第37届ACM/ICPC 长春赛区现场赛E题 (并查集)

    题意:给出一棵树,找出一个点,求出所有点到这个点的权值和最大,权值为路径上所有边权的最小值. 用神奇的并查集,把路按照权值从大到小排序,然后用类似Kruskal的方法不断的加入边. 对于要加入的一条路 ...

  9. 【Codechef FRBSUM】【FJOI2016】【BZOJ4299】【BZOJ 4408】 可持久化线段树

    4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 475  Solved: 287[Submit][Status ...

  10. 【Hadoop】HDFS - 创建文件流程详解

    1.本文目的 通过解析客户端创建文件流程,认知hadoop的HDFS系统的一些功能和概念. 2.主要概念 2.1 NameNode(NN): HDFS系统核心组件,负责分布式文件系统的名字空间管理.I ...