方法(Java)

方法

方法可以用于定义可重用的代码以及组织和简化编码。

方法是为完成一个操作而组合在一起的语句组。例如:System.out.println 、System.exit 、Math.pow 和 Math.random ,这些方法都在 Java库中定义。

定义方法

方法的定义由方法名称、参数、返回值类型以及方法体组成。

定义方法的语法如下所示:

修饰符 返回值类型 方法名(参数列表){
	//方法体;
}

方法头(method header)是指方法的修饰符(modifier)、返回值类型(return value type)、方法名(method name)和方法的参数(parameter)。

方法可以返回一个值。retumValueType 是方法返回值的数据类型。有些方法只是完成某些要求的操作,而不要求返回值。在这种情况下,returnValueType 为关键字 void 。main 方法,System.exit 、System.out.println 方法返回值类型都是 void 。 如果方法有返回值,则称为带返回值的方法(value-returning method),否则就称这个该方法为 void 方法(void method)。

定义在方法头中的变量称为形式参数(formal parameter)或者简称为形参(parameter)。参数就像占位符,当调用方法时,就给参数传递一个值,这个值称为实际参数(actual parameter)或实参(argument)。参数列表(parameter list)指明方法中参数的类型、顺序和个数。方法名和参数列表一起构成方法签名(method signature)。参数是可选的,方法可以不包含参数。

方法体中包含一个执行方法的语句集合。为使带返回值的方法能返回一个结果,必须要使用带关键字 return 的返回语句,执行到 return 语句时方法终止。

注意: 在其他某些语言中,方法称为过程(procedure)或函数(function)。带返回值的方法称为函数,返回值类型为 void 的方法称为过程。

警告: 在方法头中,需要对每一个参数进行独立的数据类型声明。

注意: “定义方法” 和 “声明变量” 是有差别的。定义是指被定义的条目是什么, 而声明通常是指为被声明的条目分配内存来存储数据。

调用方法

方法的调用是执行方法中的代码。

在方法定义中,定义方法要做什么。为了使用方法,必须调用(call或invoke)它。根据方法是否有返回值,调用方法有两种途径。

  • 如果方法返回一个值,对方法的调用通常就当作一个值处理。
  • 如果方法返回 void ,对方法的调用必须是一条语句。

注意: 在 Java 中,带返回值的方法也可以当作语句调用。这种情况下,函数调用者只需忽略返回值即可。虽然这种情况很少见,这样也是被允许的。

当程序调用一个方法,程序控制就转移到被调用的方法。当执行完 return 语句或执行到表示方法结束的右括号时(其中隐藏了语句 return;),被调用的方法将程序控制返还给调用者。

注意: main 方法与其他方法很类似,区别在于它是由 Java 虚拟机调用的。main 方法的方法头永远都是一样的它包括修饰符 public 和 static ,返回值类型是 void , 方法名为 main ,String[] 类型的参数 。 String[] 表明参数是一个 String 型数组。main 中的语句可以调用 main 方法所在类中定义的其他方法,也可以调用别的类中定义的方法 。如果创建了一个新类,可以通过使用 “类名.方法名” 调用。

警告: 对带返回值的方法而言,return 语句是必需的。

有些方法在逻辑上是正确的,但它会有编译错误,因为 Java 编译器认为该方法有可能不会返回任何值。尤其是选择结构,要保证不管 if 语句如何执行,总可以执行到 return 语句。

注意: 方法能够带来代码的共享和重用 。

每当调用一个方法时,系统会创建一个活动记录(也称为活动框架),用于保存方法中的参数和变量。活动记录置于一个内存区域中,称为调用堆栈(call stack)。调用堆栈也称为执行堆栈、运行时堆栈,或者一个机器堆栈,常简称为 “堆栈” 。当一个方法调用另一个方法时,调用者的活动记录保持不动,一个新的活动记录被创建用于被调用的新方法 。方法结束返回到调用者时,其相应的活动记录也被释放。理解调用堆找有助于理解方法是如何调用的。

void 方法

void 方法不返回任何值,对 void 方法的调用必须是一条语句。 这条语句同其他 Java 语句一样,以分号结束。

注意: void 方法不需要 return 语句,但它能用于终止方法并返回到方法的调用者。它的语法是:

return;

这种用法很少,但是对于改变 void 方法中的正常流程控制是很有用的。

通过传值进行参数传递

调用方法的时候是通过传值的方式将实参传给形参的。

方法的强大之处在于它处理参数的能力,可以使用方法 println 打印任意字符串。调用方法时,需要提供实参,它们必须与方法签名中所对应的形参次序相同。这称作参数顺序匹配(parameter order association)。

警告: 实参必须与方法签名中定义的参数在次序和数量上匹配,在类型上兼容。类型兼容是指不需要经过显式的类型转换,实参的值就可以传递给形参。

基本数据类型的兼容性(向后兼容):double(64位)–>float(32位)–>long(64位)–>int(32位)–>short(16位)–>byte(8位)

注意: char(16位) 与 byte 、short 类型不兼容, int 、long 、float 和double 类型兼容 char 类型。boolean 类型与上述类型互不兼容,只有字面量 true 和 false 。

当调用带参数的方法时,实参的值传递给形参,这个过程称为按值传递(pass-by-value)。如果实参是变量而不是直接量,则将该变量的值传递给形参。无论形参在方法中是否改变,该变量都不受影响。形参和实参是否同名是没有任何分别的。形参是方法中具有自己存储空间的变量。局部变量是在调用方法时分配的,当方法返回到调用者后它就消失了。

注意: 为了简便描述,实参 x 传给形参 y 实际含义是指将 x 的值传递给 y 。

模块化代码

模块化使得代码易于维护和调试,并且使得代码可以被重用。

使用方法可以减少冗余的代码,提高代码的复用性。方法也可以用来模块化代码,以提高程序的质量。将实现某一功能的代码封装在一个方法中,这个程序就具备了以下几个优点 :

  1. 将问题的求解过程和 main 方法中的其他代码分隔开,这样做会使逻辑更加清晰而且程序的可读性更强;
  2. 出现错误就限定在方法中,这样就缩小了调试的范围;
  3. 其他程序就可以重复使用方法。

重载方法

重载方法使得可以使用同样的名字来定义不同方法,只要保证它们的签名是不同的。

方法重载(method overloading)是指在一个类中有多个方法,它们具有相同的名宇,但有不同的参数列表。Java 编译器根据方法签名决定使用哪个方法。调用方法时,Java 编译器寻找最精确匹配的方法(数据类型兼容)。

提示: 重载方法可以使得程序更加清楚,以及更加具有可读性。执行同样功能但是具有不同参数类型的方法应该使用同样的名字。

注意: 被重载的方法必须具有不同的参数列表。不能基于不同修饰符或返田值类型来重载方法。

注意: 有时调用一个方法时,会有两个或更多可能的匹配。但是,编译器无法判断哪个是最精确的匹配。这称为歧义调用(ambiguous invocation)。歧义调用会产生一个编译错误。

变量的作用域

变量的作用域 (scope of a variable) 是指变量可以在程序中引用的范围。

在方法中定义的变量称为局部变量(local variable)。局部变量的作用域从声明变量的地方开始,直到包含该变量的块结束为止。局部变量必须在使用之前进行声明和赋值。

参数实际上就是一个局部变量。一个方法的参数的作用域涵盖整个方法。在 for 循环头中初始动作部分声明的变量,其作用域是整个 for 循环。但是在 for 循环体内声明的变量,其作用域只限于循环体内,是从它的声明处开始,到包含该变量的块结束为止。

可以在一个方法中的不同块里声明同名的局部变量,但是,不能在嵌套块中或同一块中两次声明同一个局部变量。

警告: 不要在块内声明一个变量然后在块外使用它,这样就会产生一个语法错误。

生成随机字符

字符使用整数来编码。产生一个随机字符就是产生一个随机整数。

每个字符都有一个唯一的在十六进制数 0 到 FFFF (即十进制的 65535) 之间的Unicode 。生成一个随机字符就是使用下面的表达式生成从 0 到 65535 之间的一个随机整数(注意:0 <= Math.random() <1.0 ,故 65535 要加 1)。

(int)(Math.random()*(65535+1));

小写字母的 Unicode 是一串连续的整数, (int)‘a’ 到(int)‘z’ 之间的随机整数是:

(int)((int)'a'+MAth.random()*((int)'z'-(int)'a'+1))

所有的数字操作符都可以应用到 char 操作数上。如果另一个操作数是数字或字符,那么 char 型操作数就会被转换成数字。可以简化为:

'a'+Math.random()*('z'-'a'+1)

随机的小写字母是:

(char)('a'+Math.random()*('z'-'a'+1))

可以生成任意两个字符 Ch1 和 ch2 之间的随机字符,其中 ch1 < ch2:

(char)(ch1+Math.random()*(ch2-ch1+1))

方法抽象和逐步求精

开发软件的关键在于应用抽象的概念。

方法抽象(method abstraction)是通过将方法的使用和它的实现分离来实现的。用户在不知道方法是如何实现的情况下,就可以使用方法。方法的实现细节封装在方法内,对使用该方法的用户来说是隐藏的。这就称为信息隐藏(information hiding)或封装(encapsulation)。如果决定改变方法的实现,但只要不改变方法签名,用户的程序就不受影响。

方法抽象的概念可以应用于程序的开发过程中。当编写一个大程序时,可以使用 “分治” (divid-and-conquer) 策略,也称为逐步求精(stepwise refinement), 将大问题分解成子问题。子问题又分解成更小、更容易处理的问题。

自顶向下的设计
尽管细节对最终程序很重要,但在前期过多关注细节会阻碍解决问题的进程。为使解决问题的流程尽可能地流畅,先用方法抽象把细节与设计分离,只在后面才实现这些细节。

自顶向下和自底向上的实现
通常,一个子问题对应于实现中的一个方法,即使某些子问题太简单,以至于都不需要方法来实现。需要决定哪些模块要用方法实现,而哪些模块要与其他方法结合完成。这种决策应该基于所做的选择是否使整个程序更易读而做出的。

可以采用 “自顶向下” 或 “自底向上” 的办法。“自顶向下” 方法是自上而下,每次实现结构图中的一个方法。等待实现的方法可以用待完善方法代替。待完善方法(stub)是方法的一个简单但不完整的版本。使用待完善方法可以快速地构建程序的框架。自底向上方法是从下向上每次实现结构图中的一个方法,对每个实现的方法都写一个测试程序进行测试。自顶向下和自底向上都是不错的方法:它们都是逐步地实现方法,这有助于分离程序设计错误,使调试变得容易。有时,这两种方法可以一起使用。

逐步求精的优势
逐步求精将一个大问题分解为小的易于处理的子问题。每个子问题可以使用一个方法来实现。这种方法使得问题更加易于编写、重用、调试、修改和维护。

  • 更简单的程序。逐步求精方法将其分解为较小的方法,而不是在一个方法中写很长的语句序列。这样简化了程序,使得整个程序易于阅读和理解。
  • 重用方法。逐步求精提高了一个程序中的方法重用。这减少了冗余的代码。
  • 易于开发、调试和测试。每个子问题在一个方法中解决,而一个方法可以分别的开发、调试以及测试。这隔离了错误,使得开发、调试和测试更加容易。
  • 更方便团队合作。当一个大问题分解为许多子问题,各个子问题可以分配给不同的编程人员。这更加易于编程人员进行团队工作

切记: 编写大型程序时,不要一次性地编写整个程序,可以使用自顶向下或自底向上的方法。使用这些方法似乎浪费了更多的开发时间(因为要反复编译和运行程序),但实际上,它会更节省时间并使调试更容易。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值