java方法参数可以是java不确定参数数字吗

Java方法参数太多怎么办—Part5—方法命名 - ImportNew
| 分类: ,
本文是这个系列的第五篇,介绍了通过方法命名解决参数过多的问题。如果你也希望参与类似的系列文章翻译,可以加入我们的和小组。
在前面文章()中,讨论了如何使用方法重载减少方法和构造函数的参数,指出了这种方式的一些不足并建议使用多个不同的函数名代替重载。本文将深入讨论如何通过函数命名解决参数过多的问题,并且可以弥补方法重载的一些不足。
从减少参数的角度来看,方法重载的核心问题在于:当参数过多时,相同名字的方法到底可以重载多少次?当其中一些参数的类型相同时尤其如此。举个例子,我定义一个包含三个String属性的类,想通过三个构造函数分别初始化不同的属性。这样完全没法用重载来解决这个问题。通过试验发现,只有查阅注释(Javadoc)才能确定构造函数到底初始化了哪个String属性。不使用构造函数重载,通过定义不同的方法名能提高代码的可读性。
下面的示例代码展示了如何调用其它类的方法实例化Person。这些方法都有着长长的名字,详细描述了所需的参数。这意味着需要的方法注释更少,对于调用方法的开发者代码更具可读性,相比方法重载支持的参数组合方式更多也更具前景。
通过函数名描述Person实例化示例:
publicPerson createPersonWithFirstAndLastNameOnly(finalString firstName,finalString lastName)
// implementation goes here ...
publicPerson createEmployedHomeOwningFemale(finalFullName name,finalAddress address)
// implementation goes here ...
publicPerson createEmployedHomeOwningMale(finalFullName name,finalAddress address)
// implementation goes here ...
publicPerson createUnemployedHomeOwningFemale(finalFullName name,finalAddress address)
// implementation goes here ...
publicPerson createEmployedRentingMale(finalFullName name,finalAddress address)
// implementation goes here ...
上面的示例代码中,使用较长的描述性方法名可以让开发者更好地了解调用方法所需的参数。当然,我可以写更多的这样方法来覆盖不同的参数排列组合,这里只是列出其中的一个小集合。请注意我在示例代码中使用了参数对象(在之前的文章中定义过的Fu、lName和Address)来进一步减少调用方法时所需的参数。
上面的示例代码展示了在实例化过程中,通过不同的描述性方法名来显示哪些参数需要传递。在一些情况下,哪些参数可以由方法名知道无需传递。新手可能会觉得这种方法无法用于对象实例化和初始化,理由是Java中类的构造函数必须与类同名相同。这意味着构造函数仅能通过同名函数重载。幸运的是,Josh Bloch在每一版的第一章都会解释这个问题。按照Bloch的说法,我们可以使用静态初始化工厂实例化类。这样做的好处之一是,我们可以用任意合理的方式命名方法。
下列的代码为我们展示了静态初始化工厂的功能。当我实现这些功能时,我喜欢定义一个或几个只提供静态初始化工厂调用的私有(标记为private,不能被其它类调用)构造函数。只有我定义的类必须使用这样的方式初始化,其他人使用静态初始化工厂初始化会更加简单。这样把参数很多的构造函数隐藏了起来,可以让这些构造函数的声明完全满足需求。具体地说,如果构造函数要求参数传入空值,可以通过定义不同的静态初始化工厂方法解决。这样调用者不必特意为这些参数传递入空值,而是由静态初始化工厂方法代替客户为构造函数传递空值。简而言之,静态初始化工厂方法为用户呈现了一个更整洁、更友善的接口并且隐藏了类定义中的带有过多参数构造函数。由于构造函数无法自定义方法名,无法直接使用解决参数过多的问题。如果需要,静态初始化工厂方法可以接受“原始”类型参数并在内部把它转为通用类型和参数对象,这是静态初始化工厂方法的另一个优势。以上所说的功能在下面的示例代码中一一列举了出来:
静态初始化工厂验证
* 构造函数标记为private,因为只有内部构造器会调用它创建实例。
* @param newName Name of this person.
* @param newAddress Address of this person.
* @param newGender Gender of this person.
* @param newEmployment Employment status of this person.
* @param newHomeOwner Home ownership status of this person.
privatePerson(
finalFullName newName,finalAddress newAddress,
finalGender newGender,finalEmploymentStatus newEmployment,
finalHomeownerStatus newHomeOwner)
this.name = newN
this.address = newA
this.gender = newG
this.employment = newE
this.homeOwnerStatus = newHomeO
publicstaticPerson createInstanceWithNameAndAddressOnly(
finalFullName newName,finalAddress newAddress)
returnnewPerson(newName, newAddress,null,null,null);
publicstaticPerson createEmployedHomeOwningFemale(
finalFullName newName,finalAddress newAddress)
returnnewPerson(
newName, newAddress, Gender.FEMALE, EmploymentStatus.EMPLOYED, HomeownerStatus.HOME_OWNER);
publicstaticPerson createEmployedHomeowningMale(
finalFullName newName,finalAddress newAddress)
returnnewPerson(
newName, newAddress, Gender.MALE, EmploymentStatus.EMPLOYED, HomeownerStatus.HOME_OWNER);
publicstaticPerson createUnemployedMaleRenter(
finalFullName newName,finalAddress newAddress)
returnnewPerson(
newName, newAddress, Gender.MALE, EmploymentStatus.NOT_EMPLOYED, HomeownerStatus.RENTER);
publicstaticPerson createPersonWithFirstNameLastNameAndAddress(
finalName newFirstName,finalName newLastName,finalAddress newAddress)
returnnewPerson(
newFullName.FullNameBuilder(newLastName, newFirstName).createFullName(),
newAddress,null,null,null);
publicstaticPerson createPersonWithFirstNameLastNameAndAddress(
finalString newFirstName,finalString newLastName,finalAddress newAddress)
returnnewPerson(
newFullName.FullNameBuilder(newName(newLastName),newName(newFirstName)).createFullName(),
newAddress,null,null,null);
如上面的示例代码所示,这些方法具有较高的可读性并且不会要求传入很多参数。最后两个示例结合了方法重载和静态初始化工厂。
方法命名的好处和优点
相比简单的方法重载,恰如其分地定义描述了所需参数信息的方法名有一些优点。方法名可以根据方法的预期和假设定制,调用方法函数的代码的意图也更加明显。就像上面的示例代码那样,通过方法名可以看出哪些参数不必直接提供,因为它们被设定为方法的一部分(这种意图通过方法名而不是来注释来传达)。
有一个优势在本文并没有明确地说明:相比方法重载,方法名可以包含参数的单位或者其它背景信息。举个例子,可以用接收整数的方法setWholeLengthInMeters(int)和接收小数的方法setFractionalLengthInFeet(double)来代替需要同时接收整数和小数的方法setLength()。
方法命名的代价和缺点
虽然使用不同名字的方法实例化和静态初始化工厂相比方法重载具有一些明显的优势,但不幸的是方法命名也具有方法重载的一些问题。一个相同的问题是,为了支持可能会用到的参数所有排列组合,需要编写大量的方法。拿上面的例子来说,为包含性别、房屋所有权和工作状况的所有组合方式就需要8个方法(2的3次方)。假设任意一个单独的参数值有2种以上可能,那么为了覆盖参数值的不同组合方法名数量就需要随着参数可能的数量增长。当然,参数值具有无限可能的情况不能用不同的方法名定义所有的可能,只能传递它的值而不是在方法中设定。
虽然描述性的方法名非常易于理解,但是用户在调用方法时可能会需要在长长的方法列表中跋涉。因此,方法命名可能导致由于方法过多而降低整体的可读性。另外,一些人可能不喜欢长长的方法名所据过多的屏幕空间。我个人并不介意长函数名,我认为它们为提高可读性在屏幕上显示额外的文本是值得的。有IDE和语言规范的帮助,基本不会在输入长方法名时出错。为开发者配备多个大屏监控器也使得长方法名的不是那么让人烦恼。
方法名可以用来向用户传递重要信息。在努力净化特定方法的参数个数(包括减少参数个数)时,通过适当的方法命名可以了解方法的默认设置、哪些参数是不需要的,还可以解释其它参数的排列顺序及其特征。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:)
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNew1.用正则表达式
首先要import java.util.regex.Pattern 和 java.util.regex.Matcher
* 利用正则表达式判断字符串是否是数字
* @param str
public boolean isNumeric(String str){
Pattern pattern = pile("[0-9]*");
Matcher isNum = pattern.matcher(str);
if( !isNum.matches() ){
return false;
return true;
2.用JAVA自带的函数
public static boolean isNumeric(String str)
  for (int i = 0; i & str.length(); i++)
    System.out.println(str.charAt(i));
    if (!Character.isDigit(str.charAt(i)))
    return false;
  return true;
3.使用mons.lang
mons.lang.StringU
boolean isNunicodeDigits=StringUtils.isNumeric("aaa");
http://jakarta.apache.org/commons/lang/api-release/index.html下面的解释:
public static boolean isNumeric(String str)Checks if the String contains only unicode digits. A decimal point is not a unicode digit and returns false.
null will return false. An empty String ("") will return true.
StringUtils.isNumeric(null)
StringUtils.isNumeric("")
StringUtils.isNumeric(" ")
StringUtils.isNumeric("123") = true
StringUtils.isNumeric("12 3") = false
StringUtils.isNumeric("ab2c") = false
StringUtils.isNumeric("12-3") = false
StringUtils.isNumeric("12.3") = false
4、判断ASCII码值
public static boolean isNumeric0(String str){    for(int i=str.length();--i&=0;)  {
  int chr=str.charAt(i);
  if(chr&48 || chr&57)
    return false;  }
  return true;
5、逐个判断str中的字符是否是0-9
public static boolean isNumeric3(String str){
final String number = "";
for(int i = 0;i & number. i ++)
{   if(number.indexOf(str.charAt(i)) == -1)
return false;
return true;
6、捕获NumberFormatException异常
public static boolean isNumeric00(String str){
  Integer.parseInt(str);
  return true;
}catch(NumberFormatException e)
  System.out.println("异常:\"" + str + "\"不是数字/整数...");
  return false;
阅读(...) 评论()Java方法参数过多 - 简书
Java方法参数过多
重构--Java方法参数过多
public void getNews(Context context,
Callback callback,
String uuid,
String uid,
String from,
String token,
String uid,
String .....){
示例构造函数
public class Person {
public String lastN
public String firstN
public String middleN
public String streetA
public boolean isF
public boolean isE
public boolean isHomeO
public Person(String lastName, String firstName, String middleName, String salutation,
String suffix, String streetAddress, String city, String state,
boolean isFemale, boolean isEmployed, boolean isHomeOwner) {
this.lastName = lastN
this.firstName = firstN
this.middleName = middleN
this.salutation =
this.suffix =
this.streetAddress = streetA
this.city =
this.state =
this.isFemale = isF
this.isEmployed = isE
this.isHomeOwner = isHomeO
添加大量参数理解难度
易参数位置不正确且运行正常
那一个方法或者构造方法多少个参数才好了--没有答案
在Clean Code写到
函数参数的理想个数是零,其次是一,紧随其后的是二,应该尽可能避免三个参数的情况。参数如果多于三个则需要特殊的理由,而且无论如何都不应该再使用。
在Code Complete中写到:开发者应该限制参数在七个以内
引入参数对象
参数关系紧密合并到一个对象中
Person.class
public class Person {
public FullName fullN
public boolean isF
public boolean isE
public boolean isHomeO
public Person(FullName fullName, Address address, boolean isFemale, boolean isEmployed, boolean isHomeOwner) {
this.fullName = fullN
this.address =
this.isFemale = isF
this.isEmployed = isE
this.isHomeOwner = isHomeO
FullName.class
public class FullName {
public String lastN
public String firstN
public String middleN
public FullName(String lastName, String firstName, String middleName, String salutation, String suffix) {
this.lastName = lastN
this.firstName = firstN
this.middleName = middleN
this.salutation =
this.suffix =
Address.class
public class Address {
public String streetA
public Address(String streetAddress, String city, String state) {
this.streetAddress = streetA
this.city =
this.state =
问题:参数对象可能被滥用。如果一个开发者纯粹为了减少参数数量,把联系不紧的几个参数强捆在一个类中这肯定是行不通的,在可读性上甚至适得其反。
Builder模式:
需求:当一个对象需要不同的参数构造方法?不能写5*5满足所有的需求吧
适用范围:构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,使用Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。
Person.class
public class Person {
public FullName fullN
public boolean isF
public boolean isE
public boolean isHomeO
public Person(FullName fullName, Address address, boolean isFemale, boolean isEmployed, boolean isHomeOwner) {
this.fullName = fullN
this.address =
this.isFemale = isF
this.isEmployed = isE
this.isHomeOwner = isHomeO
public static class Builder {
private FullName fullN
private boolean isF
private boolean isE
private boolean isHomeO
* 如果有必填参数这里可以构造必填构造方法
public Builder() {
public Builder setFullName(FullName fullName) {
this.fullName = fullN
public Builder setAddress(Address address) {
this.address =
public Builder setFemale(boolean female) {
isFemale =
public Builder setEmployed(boolean employed) {
isEmployed =
public Builder setHomeOwner(boolean homeOwner) {
isHomeOwner = homeO
public Person create() {
return new Person(fullName, address, isFemale, isEmployed, isHomeOwner);
FullName.class
public class FullName {
public String lastN
public String firstN
public String middleN
public FullName(String lastName, String firstName, String middleName, String salutation, String suffix) {
this.lastName = lastN
this.firstName = firstN
this.middleName = middleN
this.salutation =
this.suffix =
public static class Builder {
private String lastN
private String firstN
private String middleN
public Builder() {
public Builder setLastName(String lastName) {
this.lastName = lastN
public Builder setFirstName(String firstName) {
this.firstName = firstN
public Builder setMiddleName(String middleName) {
this.middleName = middleN
public Builder setSalutation(String salutation) {
this.salutation =
public Builder setSuffix(String suffix) {
this.suffix =
public FullName create() {
return new FullName(lastName, firstName, middleName, salutation, suffix);
Address.class
public class Address {
public String streetA
public Address(String streetAddress, String city, String state) {
this.streetAddress = streetA
this.city =
this.state =
public static class Builder {
private String streetA
public Builder() {
public Builder setStreetAddress(String streetAddress) {
this.streetAddress = streetA
public Builder setCity(String city) {
this.city =
public Builder setState(String state) {
this.state =
public Address create() {
return new Address(streetAddress, city, state);
调用的地方
public static void main(String[] args) {
FullName fullName = new FullName.Builder().setFirstName("yes")
.setLastName("no").create();
Address address = new Address.Builder().setCity("china").setState("12")
.create();
Person person = new Person.Builder().setAddress(address)
.setFullName(fullName).create();
优点:客户端代码的可用性和可读性得到了大大提高,构造函数的参数数量明显减少调用起来非常直观。单个builder构建多个对象时Builder参数可在创建期间进行调整,还可以根据对象不同而进行改变,有效的避免重载构造函数。
缺点:增加代码量,代码变得更加冗长(相比较参数数量的增加,相同类型的参数混在一起,可选参数的增加而言,改善代码可读性更有价值)
适用范围:方法中参数可选参数或者参数中指定参数相同
public String name(String name,int year) {
return name+
* @param name
public String name(String name) {
return name+"null";
优点:遇到可选参数或者默认参数时,使用方法重载会十分有效。JAVA获取方法参数名的分析(一) - 〖wEiCH@° - ITeye技术网站
博客分类:
首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数。但有一个东西反射取不到,那就是我们对方法传入参数的命名。
取得传入参数的名字有什么意义?
对这个问题的探究,源于在写一个测试类时候的需求。假设我们有一个类需要测试,这个类中有数十个方法。为每个方法编写测试类,将耗费大量的时间和精力。因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数据进行传入。(能这样生成数据的前提是:这个类的编码需要遵循严格的规范,对参数的命名有统一的标准,同时,这个类应该和某种业务紧密相关,这样,才能通过业务和参数名字,判断应生成什么合适的数据)。如果能做到上面说的,那么对具有数十或数百个方法的类,要测试的话只需要传入这个类就可以了。
存在的问题
根据上面的设想,问题就出现了。获得类的方法,获得类的参数类型,反射都可以做到。但参数名称呢?上网求证,多数人给了直接否定的答案。因为API中根本没有提供相关的方法。但有一些人的观点启发了我。他们提到,IDE(如eclipse,myeclipse)中在编码过程中,调用一个类的方法,在代码提示的时候,ide是可以显示出方法中的参数名字的,如下图:
IDE是怎样做到的呢,如果IDE可以做到,我们是否可以尝试去分析它们的做法,来获得参数名称。
可能的做法
网上找到了一个很直观的方法——通过直接读取.java文件,把类作为一个普通文本,用正则表达式匹配方法,来直接获取参数的名字。
*一个测试程序,用来扫描文件(java文件),找出所有方法的参数列表
import java.io.*;
import java.util.regex.*;
public class ScanSource {
static void findArgsList(Object targetSrc) {
* 正则匹配串基本上是这样子分组情况(A(B(c(d))))
* 串是:(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[
* \\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{) 比如public
* static void findArgsList(Object targetSrc,int []a){
* A是匹配整个方法定义行:这里是:static void findArgsList(Object targetSrc,int []a){
* B是匹配匹配参数列表:这里是Object targetSrc,int []a
* C是匹配一个参数,包括类型和类型名称和逗号:这里是Object targetSrc, D是匹配数组标识符:这里是[]
* 这个串有点bt,水平有限,只能这样
Pattern p = Pattern
.compile("(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[\\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{)");
Matcher m = p.matcher((CharSequence) targetSrc);
// locate the all methord defination
while (m.find()) {
String methodName = m.group(0);
String methodArgName = m.group(1);
String strArgs = m.group(2);
String fourArgs = m.group(3);
System.out.println(methodName + "\n" + methodArgName + "\n" + strArgs + "\n" + fourArgs + "\n");
public static String LoadTargetFile(String targetFileName) {
String result =
FileInputStream fis = new FileInputStream(targetFileName);
// 临时分配10000size给byte数组。
byte[] bufReceived = new byte[10000];
int counts = fis.read(bufReceived);
byte[] bufActual = new byte[counts];
System.arraycopy(bufReceived, 0, bufActual, 0, counts);
result = new String(bufActual);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
public static void main(String[] args) {
String target = LoadTargetFile("src/com/spring/aop/TestAspect.java");
System.out.println(target);
findArgsList(target);
这个通过正则表达式的类,在我写的一个简单的测试类中,是可以取得参数的值的,但当把它用在我们那个有几十个方法的类的时候,表达式的匹配就失效了,没有得到任何的结果(具体原因可能是正则表达式的错误,没能匹配到一些方法)。同时,这种方法需要有.java这个源文件,而在IDE中引入的常常是.class组成的Jar包。为了进一步了解IDE对方法传入参数名的处理,下面我做了一个测试。
测试IDE对方法传入参数的处理
建立一个工程。在工程中新建如下的一个类:
public class TestJar {
public void testJar(String jarName, String yourName){
System.out.println("jarName:" + jarName + "|| yourName:" + yourName);
接着我们用2种方式对这个类打jar包:
1. 用javac编译类文件然后打到jar包中,命名为testPlugin_javac.jar.
2. 用MyEclipse直接对工程进行导出,导出为testPlugin_myeclipse.jar.
(打开2个jar中的TestJar.class文件,会发现2个class文件有差异)。
再建立一个工程,先后将2个jar包引入做实验,可以看到:
1. 引入testPlugin_javac.jar, 调用testJar方法,如下图
可以看到,2个传入参数失去了原有的名称。
2. 移除上面的包,引入testPlugin_myEclipse.jar, 调用testJar方法,如下图
可以看到,参数名称被识别出来了。
关键在于,2个jar包中的class文件不同。我们打开2个class文件(我们只是直观的看一下class文件中的变量,所以没有用专用的工具查看):
javac生成的.class:
myelipse直接打出来的.class(实际上就是调用了debug模式编译出来的.class):
2个class文件里下面的部分都有SourceFile块。应该是用来表示这个class文件是从哪个java文件编译来的。我们重点看上面的部分。
可以看到,用普通的javac编译出来的类,方法的传入参数名会被编译器修改,于是上面第一个图里SourceFile以上的部分就找不到jarName和yourName 2个名字。而只有通过-debug模式编译出来的类,它的参数名才能被保存下来。而也就是在.class文件中有保留下来参数名的jar包,在IDE中代码提示才能正确显示出参数名字。
那么说明IDE是否能识别类中的方法名取决于编译过后产生的不同的class文件。那么下一节我们会使用工具来解析这2个class文件来看其中到底有什么不同。
浏览 11338
论坛回复 /
(0 / 3382)
浏览: 216518 次
来自: 福建
我也遇到楼主一样的问题 果然在&body&后面填一 ...
/wendal/ ...
谢谢!我这个问题研究2天了。非常受教。感谢分享。
楼主,国外的那个论坛,能否提供下地址?谢谢 。}

我要回帖

更多关于 java 方法参数默认值 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信