Java最新实用教程第四章 类的方法
Java最新实用教程
2009年 7月 27日星期一 2
学习目的:
掌握 java方法的创建和调用
掌握 重载
熟悉 Math类
掌握变量 作用域
了解递归
学习重点:
方法的创建和调用
重载
Java最新实用教程
2009年 7月 27日星期一 3
第 四 章 类的方法本章提要:
方法的创建和调用
方法重载
Math类
作用域
递归
Java最新实用教程
2009年 7月 27日星期一 4
4.1方法的简介上一章已经提到,一个类的类体有变量定义和方法定义两部分,且已经学习了构造方法等特殊的一些方法。方法的定义又包括两部分:方法的声明和方法体。一般格式为:
方法的声明部分
{ 方法体的内容
}
4.2方法的结构
Java最新实用教程
2009年 7月 27日星期一 5
方法的声明部分最基本的要描述清楚方法名和方法的返回类型
。 基本格式为:
[修饰符 ]返回类型 方法名 ( [参数表 ])
方法的名字必须符合标识符规则 。 在给方法起名字时还应遵循以下命名习惯:名字如果 使用拉丁字母,首字母小写;如果名字由多个单词组成,则从第二个单词开始的其他单词首字母使用大写字母 。 例如,addActionListener等 。
方法的返回类型可以时 java任意的数据类型,当一个方法不需要返回数据,返回类型必须为 void关键字 。 当一个方法的返回类型非空的话,对应方法体中必须有 return语句 。 当方法执行完 return语句时方法返回到方法被调用处 。
很多的方法声明中都给出方法的参数,参数使用逗号隔开的一些变量声明,参数也可以为任意的 java数据类型 。 每一个参数需要声明单独的数据类型,如,int num1,num2”是不正确的

Java最新实用教程
2009年 7月 27日星期一 6
方法的参数表项应该写为,int num1,int num2”。
方法的修饰符也包括访问控制修饰符和非访问控制修饰符,基本规则请参照上一章 3.2.3节中对修饰符的分析 。
方法体:方法声明之后的一对大括号,{”,,}” 以及它们之间的内容称作方法的方法体 。 方法体的内容包括方法的局部变量和合法的 java语句 。
如下面程序中常用的 main方法:
class Test
{
public static void main(String args[])
{
System.out.println(args[0]); //输出用户输入的第一个元素
}
}
注意,main方法可以在执行程序时接受键盘输入内容作为参数 ( 为字符串类型 ),如下面的执行过程:
Java最新实用教程
2009年 7月 27日星期一 7
>javac Test.java
>java Test aaa
运行结果为:
aaa
上一章关于类的介绍中,曾经提到方法分为静态方法 ( 类方法 ) 和实例方法,以及两者之间的调用规则,这些在本章仍然适用 。
4.3 方法的调用前面 3.4节中提到,有两种传递参数给方法的方式,分别对应与两种方法的调用方式:传值调用和引用调用。
4.3.1传值调用
Java最新实用教程
2009年 7月 27日星期一 8
基本数据类型的参数是按值传递的 。
Java 方法的参数是基本数据类型时,是按值传递的( pass by
value)。 这一点可以通过一 个简单的例子来说明:
例 4.1:分析下面程序的输出结果。
public class Passvalue
{ public static void test(boolean x)
{
x = !x; System.out.println("方法调用中 x = "+x); }
public static void main(String[] args)
{ boolean a = true;
System.out.println("方法调用前 a ="+a); test(a);
System.out.println("方法调用后 a ="+ a);
}
}
运行结果图 4-1。
图 4-1 传值调用
Java最新实用教程
2009年 7月 27日星期一 9
不难看出,虽然在 test( boolean x) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对
main(String[]) 方法里的 test 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。
4.3.2引用调用
Java 中简单数据类型的传递没有引用 。
引用也是一种数据类型,可以把它想象为类似 C 语言中指针的东西,它指示了对象在内存中的地址 ——只不过不能够观察到这个地址究竟是什么 。 一个对象在内存中会占用一块空间来保存数据,根据对象的大小,它可能需要占用的空间
Java最新实用教程
2009年 7月 27日星期一 10
大小也不等。访问对象的时候,一般不会直接是访问对象在内存中的数据,而是通过引用去访问。
如果定义了不止一个引用指向同一个对象,那么这些引用是不同的,因为引用也是一种数据类型,需要一定的内存空间来保存。但是它们的值是相同的,都指示同一个对象在内存 的中位置。比如
String a =,hello”;
String b=a;
这里 a和 b都是引用,当改变了 b指示的对象的值时,a所指示的对象的值也改变 。 所以,a和 b都指向同一个对象,即包含
,Hello”的一个字符串对象 。
这里我描述了两个要点:
引用是一种数据类型,保存了对象在内存中的地址,这种类型即不是平时所说的简单数据类型也不是类实例 ( 对象 ) ;
Java最新实用教程
2009年 7月 27日星期一 11
不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类型的变量 。
知道了引用是什么东西,现在分析一下对象作为参数是如何传递的 。 还是先以一个程序为例 。
例 4.2:分析下面程序的执行结果 。
public class Test {
public static void test(StringBuffer s) {
s.append(",World!");
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("Hello");
test(string);
System.out.println(string);
}
}
运行结果:
Hello,World!
Test类中调用了 test( StringBuffer) 方法,并将 string 作为
Java最新实用教程
2009年 7月 27日星期一 12
参数传递了进去 。 这里 string 是一个引用,而引用是一种数据类型,不是对象,所以它不可能按引用传递,所以是按值传递的,但引用的值是对象的地址 。 传递给 s的是 string的引用,即 s
是和 string完全相同的引用句柄,都指向了,hello”。 效果如图
4-2所示 。
并且明显地,通过引用 s或者 string都可以实现对字符串内容的修改 。
图 4-2 传值调用
Java最新实用教程
2009年 7月 27日星期一 13
但若把上面的例子中的 test方法改为:
public static void test(StringBuffer s) {
s=new StringBuffer(",World!");
}
此时的运行结果则为:
Hello
为什么 test方法改变前后输出不同呢? 因为参数 string 是一个引用,在 string 传递给 s 但未执行,s=new StringBuffer(",
World!");”时,string和 s 的效果仍如图 4 -2 所示 。 但执行
,s=new StringBuffer(",World!");”后,引用 s被单独重新创建了,改变了 s引用的值,使之指向了另一个对象 。 此时 string和
s的内存状态可表示为图 4-3。 由于 string和 s是不同的引用,s
的改变对 string就不会造成任何影响,结果就如例中所示 。
Java最新实用教程
2009年 7月 27日星期一 14
图 4-3 引用调用
Java 中,改变参数的值有两种情况,第一种,使用赋值号
,=”直接进行赋值使其改变,如例 4.1;第二种,对于某些对象的引用,会影响到源数据。例 4.2中 test方法改变前,因为引用指示的某个固定对象,对其成员数据进行改变则实质上是改变的该对象。
Java最新实用教程
2009年 7月 27日星期一 15
4.4 方法的重载方法重载是指一个类中可有多个方法具有相同的名字,但这些方法的参数必须不同,即或者是方法的参数个数不同,或者方法的参数的类型不同 。 如前面学过的构造方法,一个类可以有多个同名构造方法,每个构造方法具有不同的参数个数或参数类型,所以,可以这样说:构造方法可以重载 。
例 4.3:编写一个程序来创建三个方法,第一个能找出所给两个参数中最大的整数,第二个能找出所给两个双精度数中最大的双精度符点数,第三个在三个双精度数之间找出最大的数。 public class OverLoading
{
//返回整型参数中的最大值
public static int max(int num1,int num2)
{
if (num1>num2)
return num1;
Java最新实用教程
2009年 7月 27日星期一 16
else
return num2;
}
//返回两个双精度参数中的最大值
public static double max(double num1,double num2)
{
if (num1>num2)
return num1;
else
return num2;
}
//返回三个双精度参数中的最大值
public static double max(double num1,double num2,double num3)
{
return max(max(num1,num2),num3);
}
public static void main(String args[])
{
//调用第一个方法
System.out.println("整数 4和 10之间的最大值是,"+max(4,10));
System.out.println("数 4.0和 1.0之间的最大值是,"+max(4.0,1.0));
System.out.println("数 4.0,1.0和 5.0之间的最大值是,"+max(4.0,1.0,5.0));
}
}
Java最新实用教程
2009年 7月 27日星期一 17
程序的执行结果如图 4-4。
图 4-4 方法重载注意:方法的返回类型和参数的名字不参与比较,也就是说如果两个方法的名字相同,即使返回类型不同,也必须保证参数是不同时,才是方法的重载 。
4.5 4.1 Math类
Math类是 java.long包中的方法,包括了完成基本的数学函数需要的方法,如指数函数等。本节节绍 Math类中常用的方法,它们可以被分为三角函数方法、指数方法和服务方法。除了方法外,Math类中还有两个有用的常量,PI和 E,分别代表圆周率
∏和自然对数的底数 2.71828。
u
Java最新实用教程
2009年 7月 27日星期一 18
u指数方法
Math类包含以下三角函数方法:
public static double sin(double radians)
public static double cos(double radians)
public static double tan(double radians)
public static double asin(double radians)
public static double acos(double radians)
public static double atan(double radians)
每个方法有一个 double型参数,它的返回值类型也是 double
。 这个参数代表一个以弧度为单位的角度 。 例如,
Math.cos(Math.PI)返回 ∏的三角余弦即计算 cos180° 。
自从 JDK 1.2以来 Math类还提供了度与弧度之间的角度换算方法:
Java最新实用教程
2009年 7月 27日星期一 19
public static double toRadians(double angdeg) ; 将度转换为弧度,如:
System.out.println(Math,toRadians(180)); 输出为 3.1415…
public static double toDegrees(double angrad); 将弧度换算为度

u指数方法在 Math类中,有四个和指数有关的方法:
public static double exp(double a); 返回自然指数 e的 a次方,即
ea
public static double log(double a); 返回以自然指数为底的 a的对数,即 ㏒ ea
public static double pow(double a,double b); 返回 a的 b次方,
即 ab
public static double sqrt(double a); 返回 a的平方根,即 。
Java最新实用教程
2009年 7月 27日星期一 20
u其他服务方法除了上面两类方法外,Math类中还有一些其他的数学方法,
如求最值、绝对值、随即数及取整等方法。
max和 min重载方法返回两个数字(任意数字类型)间的最大值和最小值,例如,max( 5.0,10.4) 返回 10.4,min( 5,10
) 返回 5。
abs重载方法返回数字(任意数字类型)的绝对值。例如,abs
( -3.1) 返回 3.1。
Math类还有一个强大的方法 random,它产生一个大于或等于
0.0并且小于 1.0的随即双精度型浮点数。
例 4.4:编写一个程序,每次执行生成 10个 0~1000之间的随机整数,并求它们的平均值 。
分析,Math.random()*1000回返回 0~1000之间的 double类型的随机数,题目中要求产生整数,只需把 Math.random()*1000
Java最新实用教程
2009年 7月 27日星期一 21
取整即可,取整的方法不止一个,选择按照,四舍五入,规则取整的 round(Double a)方法。程序如下:
public class MathRandom
{
public static void main(String args[])
{
final int count=10;
int num=0;
double sum=0;
double mean=0;
for(int i=0;i<count;i++)
{
num=(int)Math.round(Math.random()*1000);
System.out.println(num);
sum=sum+num;
}
mean=sum/count;
System.out.println("以上随即数的和为平均值 ="+mean);
}
}
执行结果如图 4-5。
Java最新实用教程
2009年 7月 27日星期一 22
图 4-5 随机数
4.6作用域一个变量、引用或方法的标识符的作用域是指程序中可以引用该标识符的范围。
4.6.1作用域规则一个标识符的作用域分为类作用域、块作用域。 java中的方法和某些内容是由语句块定义的,语句块是括在,{}” 大括号的
Java最新实用教程
2009年 7月 27日星期一 23
语句序列。
类作用域是指包含在类定义的大括号内的部分。类中的方法和实例变量具有类作用域。类作用域使得类中的方法可以直接调用同类中定义的所有方法和该类继承的方法,并可以直接访问定义在该类中的所有实例变量(前面介绍过
static方法即变量的例外)。从某种意义上说,类的所有实例变量和方法对于该类中定义的方法来说是全局的,可以直接修改实例变量和调用该类中的其他方法。
块作用域是指当用户在某一个语句块声明某个变量时
,该变量值在给定的语句块中或该块嵌套的子块中有效。语句块的用途之一是控制对象、变量或其他标识符的作用范围
。如有下面的语句块:
{
int a=12;
}
System,out,println(a);
Java最新实用教程
2009年 7月 27日星期一 24
代码的最后一行是无效的,因为计算机创建了变量 a,但遇到
,}”时就把 a扔掉了。
需要注意的是:当语句块的内部声明了一个和块外部变量同名的标识符时,编译器会产生一个错误。
4.6.2标识符的生命周期标识符作为用户自定义的名字,除了显式的名字属性、上一节提到的作用域属性,还包括持续的时间属性。
一个标识符的持续时间,也称为标识符的生命周期。标识符的生命周期决定了标识符在内存中的存在时间。一些标识符存在的时间很短,一些被反复的创建和收回,而另外一些则在程序的整个执行过程都存在。
在一个方法中代表局部变量(即在方法内定义或方法的参数
)的标识符具有自动持续时间。这种变量在程序控制到达它的声明时被创建,当控制退出它所在的块后就消亡。所以
Java最新实用教程
2009年 7月 27日星期一 25
有时也把局部变量称为自动变量。
自动持续时间变量有利于节约内存。但这种变量在使用前必须由程序员对它初始化。
4.7 递归学习了一个方法调用另一个方法,即包含在一个方法体内的语句调用另一个方法 。 这就出现一个问题:方法能不能调用自己? 如果这样做将会发生什么?
4.7.1递归所谓递归就是直接调用自身或通过别的方法间接调用自身的方法 。 是一种解决某类问题的有效方案 。
Java中利用递归来解题时,进行递归的方法首先需要处理待解决问题的基本问题,即所谓基本问题 。 在解决基本问题时
,方法会返回一个结果 。 然后可以将更复杂的问题分解成两部分:
Java最新实用教程
2009年 7月 27日星期一 26
一个部分是基本问题的解决,而另一部分是解决基本问题的方法尚不能解决的,但后一部分问题与基本问题类似,是原问题的稍简单或稍小的版本 。 因为新问题类似基本问题,递归方法就可以再去调用自身去解决较小的问题,这称为递归调用,也称为递归步骤 。
递归中所用的方法通常含有 return语句,因为结果要返回调用处不能解决的部分结合起来 。
递归步骤执行时是开放的,没有终止执行 。 递归还可以产生更多的递归调用,因为该方法将每个新的子问题有分成了两部分 。 为了时递归最终能够结束,每次的递归调用都必须简化原问题,而且必须收敛在基本问题上 。
4.7.2递归实例例 4.5:递归求 0~10之间整数的阶乘。
Java最新实用教程
2009年 7月 27日星期一 27
分析:已知 0! =1! =1,求大于 1的整数 n阶乘可用迭代法 ( 非递归 ),利用 for语句进行计算,如下所示:
factorial=1;
for(int I=n; I>=1;I--)
factorial=factorial*I;
分析上面的运算表达式,可以得到阶乘方法的递归定义:
n!=(n-1)!*n
如:明显地 5! = 5*4*3*2*1 = 5*( 4*3*2*1) =4! *5
假设有递归方法 factroial( int n) 终止条件是否为真:即 n是否小于或等于 1,如果 n小于或等于 1,则方法返回 1,不需要递归
,程序结束,如果大于 1,则应该返回递归调用:即 return
factorial( n-1) *n,注意,计算 factorial( n-1) 要比 factorial( n
) 简单,且收敛于事件 factorial( 1) 。
程序如下:
Java最新实用教程
2009年 7月 27日星期一 28
public class Factorial
{
public static void main(String args[])
{
for(int n=1;n<=10;n++)
{
System.out.println(n+"!="+factorial(n)); //递归方法
}
}
public static long factorial(int i)
{
if(i<=1)
return 1; //解决基本问题,求 1或 0的阶乘
else
return(i*factorial(i-1)); //递归调用方法,求 n的阶乘
}
}
执行结果如图 4-6。
图 4-6 递归求阶乘
Java最新实用教程
2009年 7月 27日星期一 29
4.8 本章小结类的方法表述了类的行为,完成一定的功能。本章主要讲述了方法的创建和调用、重载。并以 Math类的方法为例讲述了用方法解决一般问题的应用。也分析了方法的应用对类中标识符的生命周期、作用域的影响。最后,介绍了方法调用的特殊应用
:自身调用,即递归方法,并以简单例子加以说明。