• 第四章《类与对象》第2节:方法的定义


    方法是描述某一类事物能够完成哪些操作的程序元素。在面向对象的编程语言中,方法和属性都是类的组成部分,因此它们都被称为类的成员。

    4.2.1如何定义方法

    4.1小节中讲到:类可以表示某一类特定的事物,例如用Person类可以表示人。但4.1小节中所定义的Person类中仅包含了一些属性,也就是说,4.1小节所定义的Person类仅仅是简单的几项数据组合,它还无法真正模拟出一个现实世界中的“人”。因为现实世界中的人,除了有各种基本特征以外,还能做很多事情。比如,人都能说话、吃饭、睡觉等等,再经过后期的学习,还能算算术、写文章等等。Person类中要把人所能做的事情也定义出来,才算是真正模拟出一个现实世界中的人。

    Java语言中把一类事物所能完成的事情叫做“方法”。通常情况下,一个类除了要定义某一类事物所具有的属性之外,还应该定义出这一类事物所具有的方法。进一步分析可知:要做一件事情,有时还必须一些原材料。正如中国有句俗语“巧妇难为无米之炊”所说的那样,要完成“炊”这件事情,就必须以“米”为原材料。同理,有些方法的执行也需要一些原材料,这些原材料其实就是各种类型的数据。人们把方法运行所需的数据称为“参数”。当方法运行结束后,有时还会产生出一个结果,这个结果在专业上称为“返回值”。Java语言在定义一个方法时,需要把这个方法所用到的参数以及方法运行所产生的返回值类型都要定义出来,其格式如下:

    返回值类型 方法名(参数类型1 参数1,参数类型2 参数2...参数类型n 参数n){

       语句;

    }

    假如一个方法不需要任何参数,那么方法名后面的小括号中就什么都不用写,空着就可以,但小括号本身不能省略。而如果一个方法没有返回值,那么该方法的返回值类型就是void。例如之前大家一直使用的main()方法就是一个典型的没有返回值的方法,它的前面就是以void来表示该方法不产生任何返回值。

    程序员如果希望在Person类中定义一个两个整数相加的方法,可以用如下代码实现:

    1. int add(int a,int b) {
    2.    int r = a+b;
    3.    return r;
    4. }

    这个方法的名称是add,方法名前面的int就是返回值类型,它表示这个方法在运行之后会产生一个int类型的数据。方法名后面小括号中的a和b都是方法运行所需的参数。不难看出,这两个参数都是int型的。

    4.2.2方法的详细说明

    定义方法的格式很简单,但对于完全没有学过编程的读者来说,还需要深入理解方法的运行机制,并且记住与方法相关的一些语法细节才能真正掌握它的用法。下面就以add()方法为例来逐条为各位读者讲解方法相关的知识。

    1.定义参数的语法细节

    之前在学习声明变量时曾经介绍过:程序中可以一次性定义多个类型相同的变量,例如:

    int a,b,c;

    但在给方法定义参数时却不能一次性定义多个类型相同的参数,Java语言规定:必须为每一个参数单独指定它的类型,因此以下的写法是错误的:

    1. int add(int a, b) {
    2.    ...
    3. }

    此外还要强调:Java语言在定义方法参数时不能为参数赋值,也就是说,程序员不能为参数指定默认值,因此以下的写法也是错误的:

    1. int add(int a=0,int b=1) {
    2.    ...
    3. }

    2.形式参数与实际参数

    很多读者可能会问:既然程序员不能在定义参数时为它指定一个值,那么在方法中执行a+b的操作时,a和b的值分别又是多少呢?

    想要弄明白这个问题,就必须先要弄清楚两个概念,分别是:方法的“定义”和“调用”。所谓方法的定义,是指程序员详细的规定出一个方法的名称、运行时需要何种参数、运行的步骤以及运行所产生的结果是何种类型的数据。这就如同菜谱上写的做菜流程一样,它告诉人们需要加入何种食材,如何操作并以及最终做出的菜品是什么样子。但菜谱仅仅指出了做菜流程,并没有提供真实的食材,更不会把菜品实际做出来。因此之前编写的add()方法也仅仅是对方法的定义,虚拟机并没有实际执行这个方法。而参数a和b是方法的一部分,它们只是代表了两个整数而已,并没有具体的值。

    那么,a和b在什么时候才会有具体的值呢?这需要等到方法被调用的时候。所谓“调用”其实就是指程序员在代码中使用某个定义好方法。方法是定义在类当中的,所以这个类的每一个对象都具有类中所定义的方法,而调用方法的时候,需要通过对象来完成,例如:

    1. Person p = new Person();
    2. p.add(2,3);

    当通过p对象调用add()方法并且在方法的小括号内写上两个整数时,参数a和b就有了具体值。以上代码中在调用add()方法时,给它传递了2和3两个整数,所以参数a和b的值就分别变成了2和3。专业上,把定义在方法中的a和b称为“形式参数”,简称“形参”,而把实际传递到方法中的数值2和3称为“实际参数”,简称“实参”。

    3.方法的执行过程

    如果程序员在main()方法中写上如下代码:

    1. Person p = new Person();
    2. p.add(2,3);

    那么当执行到语句“p.add(2,3);”时,虚拟机会暂停执行main()方法,然后转入到add()方法中去执行代码。一旦开始执行add()方法,虚拟机会为参数a和b专门分配内存空间来保存它们的值。因此,形式参数的值虽然来自于实际参数,但它们具有独立保存数据的内存空间。形式参数只是实际参数的复制品,即使程序中修改了形式参数的值,实际参数的值也并不会因此而被改变。

    根据add()方法所定义的执行流程,虚拟机会先声明一个int型变量r,并且把a+b的运算结果赋值给r。那么,虚拟机如何把a+b的运算结果传递到main()方法当中呢?观察add()方法很容易就能看到:在方法的最后一行写的是“return r;”,变量r中保存了a+b的运算结果,而把return关键字加到变量r的前面,就能把r的值,也就是a+b的运算结果传递回main()方法中。

    add()方法执行结束后,虚拟机会重新回到之前暂停的main()方法中继续运行。而随着add()方法的运行结束,为参数a和b以及变量r所分配的内存空间也会被全部收回,这样,保存了a+b运算结果的变量r也就消失了。因此,程序员如果想在main()方法中继续使用r的值,就需要在main()方法中用另一个变量来接收这个值,本质上也就是接收add()方法所产生的运行结果。具体实现代码是:

    int r = p.add(2,3);

    以上代码用变量r接收了add()方法运行的返回值。有读者会问:变量r不是消失了吗?怎么能够用来接收方法的返回值呢?首先说明:笔者是故意把接收方法返回值的变量命名为r的。这么做是为了使各位读者明白:这两个变量虽然都叫r,但这两个r定义在不同方法中,它们是完全不同的两个变量。定义在add()方法中的r虽然消失了,但定义在main()方法中的变量r仍然可以在main()方法中继续使用。

    以上代码是在main()方法中调用了add()方法,根据它们之间的调用关系,我们把main()称为“主调方法”,而把add()称为“被调方法”。当然,主调方法和被调方法的关系是相对的,如果在add()中又调用了另一个方法,那么相对而言,add()就成了主调方法,而那个被调用的方法就是被调方法。方法的返回值总是从被调方法中传递到主调方法中的。

    4. return关键字详解

    return关键字可以把被调方法的返回值传递回主调方法中,例如add()方法中,通过“return r;”把返回值r传递给main()方法。如果方法没有返回值,那么就在方法的最后一行单独写一个return并加上分号,return后面不用加任何数据。其实,对于没有返回值的方法,Java语言允许省略return,因此大部分情况下无返回值的方法中都不会出现return关键字。

    return关键字除了能够把运算结果返回到主调方法中,它还有一个重要的作用就是告诉虚拟机方法运行到此结束。既然方法运行到return关键字就意味着方法结束,那么在return关键字后面出现的语句都得不到执行,程序员一旦在return关键字的后面写上一些语句,编译器就会提示语法错误,如图4-1所示。

     图4-1 return之后的语句无法执行

    把鼠标光标悬停在语法错误的红线上,可以看到编译器给出的语法错误提示为“Unreachable statement”,翻译成中文就是“无法到达的语句”。

    5. 方法的返回值详解

    方法运行所产生的结果被称为返回值。必须强调:一个方法最多只能有1个返回值。这个返回值可以是一个变量,一个常量,一个表达式,也可以是一个类的对象,甚至还可以是一个表示空对象的null,只要实际的返回值能够“兼容”于方法声明的返回值类型即可。注意:编译器并不要求方法实际返回的值在类型上完全等同于方法本身所声明的返回值类型,只要达到兼容的标准即可。例如图4-2所示的add()方法:

    图4-2 方法返回值类型兼容

    通过图4-2可以很清楚的看到:方法声明的返回值类型是double型,而方法实际的返回值r是一个int型的变量。但是编译器并没有因为方法声明的返回值类型与实际返回值的数据类型不完全相同而提示语法错误。想要弄清楚没有报错的原因,就必须先要理解定义方法时为什么要声明返回值类型。

    在定义一个方法时,需要在该方法名称的前面指明方法返回值的类型。这么做是为了告诉外界该方法会产生一个什么类型的运算结果,这样程序员就知道应该用一个什么样类型的变量来接收方法的返回值。当add()方法声明自身的返回值为double型,程序员自然也会用double型变量来接收方法的返回值。而方法实际返回的是一个int型的变量,按照Java语言赋值运算规则,用一个int型的数据为一个double型的变量赋值可以实现自动类型转换,直接赋值不会有任何问题。因此,方法声明的返回值虽然是double类型,而实际返回的运算结果是int类型的数据也不会有任何语法问题。

    但是如果方法声明自身的返回值为int类型,而实际返回的是double类型就会出现语法错误,如图4-3所示。

    图4-3方法返回值类型不兼容

    通过图4-3可以看到,最后一行语句中出现了代表语法错误的波浪线,这说明r的类型不符合语法要求。之所以会出现语法错误,原因也不难理解。当方法声明自身返回int型数据时,程序员自然也会用int型变量来接收方法的返回值,但按照Java赋值运算规则,实际返回的double型的数据根本无法直接给int型变量赋值,所以编译器会提示语法错误。

    以上这部分内容对定义和调用一个方法做了详细的讲解。深入理解这部分知识对学习Java编程有着重要的意义,因为Java编程的很大一部分工作就是编写和调用各种方法解决实际的业务逻辑问题。以下的【例04_02】演示了如何在Person类中定义定义add()方法以及怎样在main()方法中调用它。

    【例04_02 方法的定义和调用】

    Person.java

    1. public class Person {
    2.     String name;//姓名
    3.     char sex;//性别
    4.     int age;//年龄
    5.    
    6.    //定义2整数相加的方法
    7.     int add(int a,int b) {
    8.         int r = a+b;
    9.         return r;
    10.     }
    11. }

    Exam04_02.java

    1. public class Exam04_02 {
    2.     public static void main(String[] args) {
    3.         Person p  = new Person();//创建Person类对象p
    4.         int r = p.add(2, 3);//调用add()方法,并以变量r接收返回值
    5.         System.out.println("两数相加的和为"+r);//打印返回值
    6.     }
    7. }

    此处需要提醒各位读者:【例04_01】和【例04_02】中都有Person类,但【例04_02】的Person类比【例04_01】的Person类多了一个add()方法,所以想要正确运行【例04_02】,必须从《范例4-2》中复制Person类的源文件,并且用它代替工程中原来的Person类,或者修改原来工程中的Person类源文件,为它添加一个add()方法。其实本书中很多案例都是从前面案例的基础上进行修改而来,例如后面的【例04_03】也用到了Person类,并且该案例中的Person类又增加了新的方法,所以每运行一个案例都要及时从对应的案例文件夹中下载对应的源文件并且更新到工程的src文件夹下。

    至此,各位读者已经深入的学习了“类”和“对象”两个概念。那么,类与对象之间究竟有什么联系和区别呢?在Java语言中,一个类可以表示一类事物,而一个对象则表示了该类事物中的一个实例。就拿【例04_01】来说:Person这个类表示 “人”这一类事物,而对象p则表示一个具体的人。

    此外,对象都是以类为模板创建的,具体来说:对象所拥有的属性和方法都是类中所定义的。类中定义了哪些属性和方法,对象就相应的拥有哪些属性和方法。读者可以把一个类理解成一张建筑图纸,而把对象理解成依照这张建筑图纸盖起来的楼房。图纸决定了楼房的样子,而楼房是图纸的具体实现结果。现实世界中,可以根据一张建筑图纸盖出多座楼房,而在程序中,程序员也可以用一个类创建出多个对象。但无论创建多少个对象,只要这些对象属于同一类,它们都具有同样的属性和方法,只是每个对象属性具体的值有可能各不相同。

    各位初学Java的读者一定要认真理解并深入体会类和对象这两个概念,它们是学习面向对象编程的重要基石。

    除阅读文章外,各位小伙伴还可以点击这里观看我在本站的视频课程学习Java!

  • 相关阅读:
    Linux常用命令(下).
    成功解决ValueError: cannot reindex from a duplicate axis
    TypeScript常用知识点及其最佳实践总结
    内网Jenkins 部署.net(dotnet)项目
    luogu-P1462 通往奥格瑞玛的道路 && ybt-修建道路【最短路+二分】
    redis 登录客户端命令
    Unity Editor 打包指定资源(AssetBundle)和加载指定资源
    基于SDN环境下的DDoS异常攻击的检测与缓解--实验
    web渗透测试----5、暴力破解漏洞--(9)MS-SQL密码破解
    java导出excel(二):多个sheet
  • 原文地址:https://blog.csdn.net/shalimu/article/details/128011909