在 JDK 1.5 中,新增了 变长参数 机制:在定义方法时,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
变长参数是 Java 中的一个语法糖,本质上还是基于数组的实现。
它适用于参数个数不确定,类型确定的情况。
可变长参数用于方法的形参中。定义方法时,在最后一个形参后加上三点 …
,表示该形参可以接受多个参数值(多个参数值被当成数组传入)
语法结构:
修饰符 返回值类型 方法名(参数类型... 参数名) {
// TODO
}
可变参数方法的使用与方法参数部分使用数组是一致的。如:可以循环输出所有的参数值
public class Test {
// 定义
public void test(int... args) {
for (int arg : args) {
System.out.println(arg);
}
}
public static void main(String[] args) {
Test test = new Test();
// 调用
test.test(1, 2, 3);
}
}
传 0 个实参也行:
public static void main(String[] args) {
Test test = new Test();
test.test();
test.test(1, 2, 3);
}
传一个数组也行:
public static void main(String[] args) {
Test test = new Test();
test.test();
test.test(1, 2, 3);
// 入参是一个数组
test.test(new int[]{5, 6, 7, 8});
}
总结一下可变长参数的特点:
不能这样吗?
public void test(int... args, int a) {
// TODO
}
如果写成这样,那么一定会编译期报错!!那么,为什么不行呢?
因为参数个数不定,所以,当其后边还有类型参数时,java 无法区分传入的参数属于前一个可变参数还是后边的参数,所以,只能让可变参数位于最后一项。
那么,为什么传一个数组也行呢?
将上述代码通过反编译工具 jad
进行反编译,代码如下:
public class Test
{
public Test()
{
}
public transient void test(int args[])
{
int ai[] = args;
int i = ai.length;
for(int j = 0; j < i; j++)
{
int arg = ai[j];
System.out.println(arg);
}
}
public static void main(String args[])
{
Test test = new Test();
test.test(new int[0]);
test.test(new int[] {
1, 2, 3
});
test.test(new int[] {
5, 6, 7, 8
});
}
}
传入 0 个实参时:
test.test(); ==> test.test(new int[0]);
传入多个实参时:
test.test(1, 2, 3); ==> test.test(new int[] {5, 6, 7, 8});
通过反编译代码知:编译器会将可变长参数转换为对应的数组,然后进行相应的处理。
虽然可变长参数在某些方面使用起来较为方便,但它也有自身的局限。在使用过程在,需要注意。如下:
1、拥有可变参数的方法可以被重载。在调用方法的时候,如果能够和固定参数的方法匹配,也能够与可变长参数的方法匹配,则选择固定参数的方法。如:
public class Test {
public void testOverload(int i) {
System.out.println("i");
}
public void testOverload(int i, int j) {
System.out.println("i, j");
}
public void testOverload(int i, int... args) {
System.out.println("args");
}
public static void main(String[] args) {
Test test = new Test();
// i
test.testOverload(1);
// i, j
test.testOverload(1, 2);
// args
test.testOverload(1, 2, 3);
}
}
2、如果要调用的方法可以和两个可变参数匹配,则会编译期错误
public class Test {
public void testOverload(Object... args) {
System.out.println("args");
}
public void testOverload(Object o, Object... args) {
System.out.println("args");
}
public static void main(String[] args) {
Test test = new Test();
// 编译期报错
test.testOverload(1);
test.testOverload(1, 2);
}
}
3、一个方法只能有一个可变长参数,并且这个可变长参数必须是该方法的最后一个参数。否则,会编译期报错
4、不要用数组去重载可变长参数,否则,会编译期报错
public class Test {
// 编译期报错
public void testOverload(Object... args) {
System.out.println("args");
}
// 编译期报错
public void testOverload(Object[] args) {
System.out.println("args");
}
public static void main(String[] args) {
Test test = new Test();
test.testOverload(1);
test.testOverload(1, 2);
}
}
5、别让 null 值和空值威胁到变长方法
public class Test {
public void testOverload(String dept, Integer... args) {
}
public void testOverload(String name, String... args) {
}
public static void main(String[] args) {
Test test = new Test();
// 编译期报错
test.testOverload("人事部");
// 编译期报错
test.testOverload("zzc", null);
}
}
因为两个方法都匹配,编译器不知道选哪个,于是就报错了。
同时,这里还有个非常不好的编码习惯,即:调用者隐藏了实参类型,这是非常危险的。不仅仅调用者需要“猜测”该调用哪个方法,而且被调用者也可能产生内部逻辑混乱的情况。作出如下修改:
public static void main(String[] args) {
Test test = new Test();
test.testOverload("人事部");
String[] strs = null;
test.testOverload("zzc", strs);
}
6、重写可变参数方法也要循规蹈矩
public class Test {
public static void main(String[] args) {
Parent parent = new Sub();
parent.print("hello");
Sub sub = new Sub();
// 编译报错
sub.print("hello");
}
}
class Parent {
void print(String... msg) {
System.out.println("Parent#print()");
}
}
class Sub extends Parent {
@Override
void print(String[] msg) {
System.out.println("Sub#print()");
}
}
第一个能编译通过,这是为什么呢?事实上,Parent 对象把子类对象 Sub 做了向上转型,形参列表是由父类决定的,当然能通过。再看看子类直接调用的情况,这时,编译器看到子类覆写了父类的 print() 方法,因此肯定使用子类重新定义的 print() 方法,尽管参数列表不匹配也不会跑到父类再去匹配下,因为找到了就不再找了,因此有了类型不匹配的错误
这是个特例,重写的方法参数列表与父类不相同,这违背了重写的定义,并且会引发莫名其妙的错误
重写方法必须满足的条件如下:
因篇幅问题不能全部显示,请点此查看更多更全内容
怀疑对方AI换脸可以让对方摁鼻子 真人摁下去鼻子会变形
女子野生动物园下车狼悄悄靠近 后车司机按喇叭提醒
睡前玩8分钟手机身体兴奋1小时 还可能让你“变丑”
惊蛰为啥吃梨?倒春寒来不来就看惊蛰
男子高速犯困开智能驾驶出事故 60万刚买的奔驰严重损毁