第5章 方法 2012.ppt
HSTCJava面向对象语言HSTC第5章 方法 方法定义调用方法 Void方法 传值参数 模块化代码 问题:十进制转换成十六进制 方法重载 变量的作用范围数学类举例:生成随机字符Method Abstraction and Stepwise RefinementHSTC方法介绍假设需要计算若干个十个数的和,如分别从1-10,从20-30,和从35到45的和,按之前我们的做法,代码书写如下。显然十分的麻烦,同时我们发现他们之间有些相似之处,仅仅是起始和终止的整数不同,其他都类似。我们写一个通用的代码是不是会更好?当然,我们可以定义一个方法,这个方法就是可以反复使用。基于此,代码进一步进行改写如下。HSTC方法定义方法定义的语法如下:方法可以返回一个值,returnValueType是返回值的返回类型。可以不返回值,即returnValueType为void。如main方法就死void,不返回任何值。在方法头部定义的变量称为形式参数或简单参数。一个参数就如一个占位符,当一个方法被调用时,要传递一个值给这个参数,这个值称实际参数。参数列表指一个方法的参数的类型,顺序以及个数。参数是可选的,即一个方法可以没有参数,如Math.random()方法没有参数。如下的方法是找到两个整数中的较大者。因为这个方法要返回一个整数,因此要在语句中包含有return语句。其他编程语言中,方法被叫做过程或函数,返回值的称函数,不返回值的称过程。在定义方法参数的时候要分别为每一个参数定义类型,如max(int num1,int num2),而不是max(int num1,num2)。HSTC调用方法定义好方法之后,还要使用方法,称方法调用。根据方法是否有返回值,有2种方式调用方法。对于有返回值的方法调用,通常以一个值来对待,如下对于返回void的方法,以一条语句来代替。如下例。注意:一个具有返回值的方法也可以被当做Java语句被调用,这种情况下会忽略返回值,只有当对返回值不敢兴趣的时候才可用。完整的程序如下。HSTC方法调用程序包含2个方法,main和max,其中main方法由JVM来调用,在main方法的语句中,可用调用类定义的其他方法,也可以调用其他类定义的方法。如max方法和main定义在同一个类中,由类(或类生成对象)调用。方法(a)在逻辑上是正确的,但编译会出错,原因就是编译器任务程序可能会没有返回值。(b)是修改后的方法,这个方法是正确的,原因是编译器看到不管哪个if语句被执行都会有返回值。方法使得代码能够分享和重用,max方法可以在这个类以外的任意位置被调用。如TextMax.man(1,2);HSTC调用方法调用栈方法的每次调用,系统都会用内存中开辟的一块叫栈的空间来保持参数和变量的值。理解调用栈,对于我们理解方法和调用有很大的帮助。HSTCVoid 方法举例上述例子给出了一个有返回值的方法,下面讲述如何定义和调用void方法。如下图。使用语句来调用void方法printGrade()。HSTC为了观察和返回值方法的不同,先把void方法进行改写getGrade(),如下。HSTCgetGrade方法可以在字符出现的任何位置被调用。而printGrade方法没有返回值,因此必须用语句调用。注意:虽然return语句没有必要用在void方法中,但它可以被用来终止方法并返回给方法的的掉用者。简单的语法为:return;这种做法并不是经常的,但对于在void方法中的进行控制流程很有用。如下代码,当分数非法时,方法会返回并终止执行。HSTC传值参数一个方法的强大是因为它的参数,我们可以用println方法打印一个字符串,用max找到两个整数的最值。当调用一个方法时,我们需要提供实参,实参的顺序和类型等修饰必须和方法定义的形参对应,这叫做参数顺序结合。如下例打印n次信息的方法。我们可以用nPrintln(“Hello”,3);来打印Hello字符串3次,语句中“Hello”的值被传递给形式参数message,3被传递给形参整数n。但如果语句nPrintln(3,“Hello”);就会出错,因为3的类型没有和第一个形参message类型匹配,第二个参数也没能匹配。HSTC注意:实参必须按顺序,数量和兼容类型(如可以把int传给double)与形参匹配。当我们调用方法时,实际参数的“值”传递给形式参数,我们称按值传递参数。即如果传递的实参是一个变量,把变量的值给形参,而不是把变量的地址传给形参,即使以后传递的参数在方法中被改变,也不会影响到实际参数的值。如下程序当中x(1)的值被传递给参数n,虽然之后n的值增加1,但x的值不受影响。HSTC但如下程序定义一个交换两个变量值的方法。通过传递2个实际参数值调用swap方法。HSTCHSTC模块化代码 方法可以简化代码,并能使代码重用。使用方法就是在编程过程中改善程序的质量并使得代码模块化。之前的求两个数最大公约数的程序,即提示用户输入2个整数并显示他们的最大公约数,我们用方法改写它,如下。HSTC上例在gcd方法中封装了代码,使得程序有如下好处:计算最大公约数的程序从main方法中与其他代码分离出来,因此,逻辑上变得清晰和易读。计算最大公约数的程序如果有错误被限制在gcd方法中,缩小了调试程序的范围。gcd方法可以被其他程序使用。求前50个素数的例子,模块化以后,如下编写。HSTC十进制转换成十六进制 16进制数常被用在计算机系统程序中,下面我们要编写一个程序,把十进制数转换成16进制数。公式如下 们只需要得到这一系列的h就可以了。如图计算123的16进制数是7B的过程 HSTCHSTC方法重载 max方法只能用在整数上,如果计算2个浮点数的最大值就要再编写一个方法,这个方法具有相同的名字max,但是参数的类型不同,如下代码。如果我们用整数参数调用max,则期待参数为int的方法会被调用,如果我们给出的参数是double类型则参数为double类型的max被调用。这就是方法的重载,即在一个类中,有2个方法,它们具有相同的名字,不同的参数列表。Java编译器会根据参数决定调用哪个方法。如下程序中包含3个重载的max方法。HSTCHSTC我们能否用一个int值和一个double值来调用max方法呢?如max(2,2.5),如果可以,哪个方法会被调用?第一个问题答案是肯定的,第二个问题是找到double参数的max方法,当无法找到精确匹配(int,double)的参数值时,会找到它兼容的模式(double,double),因此参数2会被自动转换成double类型。注意:重载的方法必须有不同的参数列表。有时有2个以上的兼容匹配模式,但编译器不知道如何决定最合适的匹配,这就导致了含糊的调用,含糊的调用会导致编译错误。如下列代码。HSTC量的作用范围 变量的作用范围是程序中的变量在程序中被引用的范围。定义在方法内的变量称之为局部变量。局部变量的作用范围是从他声明开始到包含这个变量的语句块的结束部分。局部变量在使用前必须被声明并赋值。形式参数实际上就是局部参数,作用范围是整个方法。在for循环中初始化动作中的声明的变量的作用范围在整个循环中。但在for循环体中声明的变量作用范围从声明之处开始到包含这个变量语句块结束。如图。HSTC我们可以在不同的语句块中声明相同名字的变量,但不能在同一个语句块或它的嵌套结构中再次声明此变量。如下图所示。值得注意的是我们不能在语句块中声明变量,却要在语句块外来使用它。如下程序是错误的。HSTCMath数学类Math类中包含着进行基本数学运算的方法,如之前我们已经用过pow(a,b)方法来计算乘方,用random()方法来取得随机数。这节中将介绍其他一些有用的Math类中的方法,它们被分为3类,三角函数,指数函数和服务类方法。Math类中除了提供给我们方法外,还包含2个有用的double类型的常量PI和E。5.10.1 三角函数方法Math中包含的三角函数方法如下:HSTC其中sin,cos,tan参数是弧度,而asin,acos和atan的参数的单位是角度,如下使用三角函数的例子。HSTC5.10.2 指数方法Math类中提供5个指数类方法,如下图。HSTC5.10.3 Rounding方法HSTC5.10.4 min,max和abs方法Min和max方法是计算得到2个数的最小或最大值,这个方法被重载(int,long,float或double),如max(3.4,5.0)返回5.0,而min(3,2),返回2。Abs方法也同样在这4个类型中被重载。如图例子。HSTC5.10.5 random方法我们之前已经用了random方法,这个方法非常有用,可以生成一定范围的数。如下例。我们可以通过查找API来获取Math类的其他信息。注意:不是所有的类都需要main方法,如Math类和JoptionPane类没有main方法,这些类包含其他类要使用的方法。HSTC举例:生成随机字符 生成随机数的例子很常见,生成随机字符也很重要。在前面已经讲过Java支持Unicode字符集,每一个字符都是独一无二的Unicode码,因此生成随机字符就是生成一个0-65535的随机数,如下表达式。现在我们来考虑如何生成一个随机小写字母。Unicode字符集中小写字符从a到z的整数是(int)a-(int)z。那么一个整数随机数为:我们知道所有的数值操作符都可以用在字符上,上面的表达式还可以进一步简写:如下是随机字母:如下生成从某个字符ch1到ch2的随机字符,ch1 ch2。HSTC举例:5个重载的方法生成随机字符,我们可以熟练掌握,来方便以后实践。HSTC如下给出了一个测试程序显示175个随机小写字母。HSTC抽象方法和阶梯式递进开发 开发软件的关键是应用抽象的概念。抽象方法就是从它的实施中分离出不同的用处。客户可以不用考虑它如何实施的而使用它,实施的细节被封装在方法中隐藏起来,就像一个黑箱子一样,把复杂的细节隐藏起来,这就是信息隐藏和封装。如果想改变它的细节,调用它的客户的程序也不会受到影响。Print方法,showInputDialog方法,max方法等,我们知道如何调用它,但我们不知道它们是如何实施的。抽象方法的理念也可以用在开发程序的过程,当要写一个大程序时,可以采用逐步开发策略,也就是阶梯式递进开发,把它分解成很多子问题。子问题再可以进一步分解成跟小的问题,最终是可以轻松解决的问题。HSTC假设我们编写一个给出这个年的月份得到月历,程序提示用户输入年份和月份,程序显示输出整个月的月历。HSTC自顶向下的设计 初学者在编程过程中往往试图从头到尾编写每个细节。尽管在最终程序中细节也非常重要,若尽早的关注细节会阻碍程序编写的进程。为了尽可能顺利的解决流程问题,我们利用方法抽象把细节从设计中孤立出来,最后再实现细节。如打印日历这个例子,首先可以分成两个子问题:从用户那里得到输入和按月打印日历。在这个时候我们就应该关注如何分离子问题,而不是两个子问题如何实现。如图,另外一个图形说明printMonth还可以进一步分成2个子问题:打印月标题和打印月主体。HSTC月标题由3行组成:月和年,短线行和星期几的名字。我们需要从月份值(如1月)得到月的名字(如January)。可以使用getMonthName方法.为了打印月体,我们需要知道知道这个月的第一天是星期几(getStartDay方法)和这个月有多少天(getNumberOfDaysInMonth)。如图。HSTC我们如何才能得到这个月的第一天?有好几种方法可以用,在这个例子中,我们使用如下方法。假设我们知道1800年1月1日是星期3(startDay1800=3),我们就可以计算出从1800年1月1日到我们计算月份的第一天的总的天数(totalNumberOfDays),则(totalNumberOfDays+startDay1800)%7。为了得到总天数,我们需要知道年是否为闰年,每个月的天数是多少。因此getTotalNumberOfDays又被进一步的分成2个子问题:isLeapYear和getNumberOfDaysInMonth,如图。HSTCHSTC自顶向下或自底向上的实施 现在我们要试图尝试实现我们的想法。通常,一个子问题在实现的过程中对应一个方法,尽管有些子问题非常简单没有必要。我们需要决定哪个模块在实施的过程中作为方法实现,哪个模块被并到别的方法中。判断的原则就是基于程序整体是否易读,在这个例子中子问题readInput就被实现在main方法中。我们在实现的时候可以采取自顶向上也可以采用自底向上的方法。自顶向上的方法实现是根据结构图自顶向上的编写程序梗概(程序可执行的不完整的版本)。即我们先编写各方法的大概或轮廓,方法的具体细节等待后面逐步实施。如下例。HSTCHSTC编译并测试程序,修改错误。然后我们就可以实现printMonth方法了,在这个方法中调用的其他方法也可以先编写轮廓。自底向上的方法从结构的底部开始到顶部实现方法。对于每个方法,编写测试程序来测试它,两种方法都是很好的。两种方法都是增量开发的方法,远离编程中的错误,使得调试更加容易,有时候两种方法也被用在一起来开发程序。HSTC实现细节isLeapYear(int year)方法可以如下实现。在编写getTotalNumberOfDaysInMonth(int year,int month)中要注意的要点是:在getTotalNumberOfDays(int year,int month)方法中,我们需要计算总天数totalNumberOfDays和这个月的第一天是星期几。完整的程序如下。HSTCHSTCHSTCHSTC程序并没哟验证用户输入的有效性,如月份在1-12月以外,年在1800年以前,我们可以在打印日历前加一个if语句来检验输入,避免这个错误。这个程序也很容易修改成打印一整年的日历,尽管只能打印1800年之前的日历,但也可以修改它打印1800之前的月份。注意:方法抽象模块化程序是简洁的,分层的。程序由各种简单的方法组成使得程序易读易写,易调试、易修改,这种书写方式使得许多代码、方法可以重用。当编写较大型程序时,利用自底向上或自顶向下的编程方法,不要一次把整个程序都编完整,虽然模块化方法看上去会花费很多开发时间(反复修改,反复编译和运行程序),但实际上会节省我们程序调试的时间,也会使得程序调试更加容易。HSTC作业实践作业:理论纸质作业: