面向对象的编程-Application 10

Previously on OOP:

The two methods declared in Object class are toString() and equal(). These functions can be overridden by all classes in Java, as Object class is the ancestor class of all the other. To overriding, it is suggested to write an annotation in front of method declaration.

考虑到国庆节放假和各位宝宝们的心情,本黄鸭决定在本文的第一部分举一个for each statement的例子,因为这个例子非常简单,不会损伤脑细胞的。

首先,创建一个数组,里面存放的元素类型是String类型的。再强调一下,创建数组的方括号中是最多能存放元素的个数,而数组的下标都是从开始的。

接下来,要遍历这个数组中的每一个元素,我们可以采用while, for, for each循环中的一个。在C语言中,最常用的是for循环,如上段代码所示。但是在Java语言中,最常见的是for each循环,如“for (String element : s)”。

在这行代码中,声明了一个String类型的local variable,名称是element。element负责依次指向s数组中的每一个元素。换句话说,在Java语言的for each循环中,“String element = s[i];”这行代码可以省略,因为element会自动指向s[i]。所以,for each statement是非常简洁的一种语言,也是我们必须掌握的技能之一。

在循环体中,先判定当前数组元素是不是空指针,如果是没那么就什么也不做,如果不是,那么就把字符串全部转化为小写字母,并且打印出来。

第二部分也不复杂,检验一下昨天arrays package, Person类中overridden的两个函数:toString() and equals()是否能正常工作。

这个类importarrays.Person类,说明等一下还会创建Person的实例。Object类是所有类的父类,所以Object类型的指针也能指向所有类的实例,比如String and Integer类的实例,这种现象一言以蔽之就是Polymorphism。另外的,Integer类是一种wrapper class,和int之间有一点不同,因为int是一种primitive type,而不是class。

toString()函数的返回值是String,所以把返回值赋予String s是没有问题的。o此时指向的是一个Integer类型的object,所以这个toString()函数就是Integer类中的toString()函数,而不是Object类中的toString()函数。只有当Integer类中没有toString()函数,才会继续到Integer类的父类当中去寻找。

然后是用println()打印。有兴趣的宝宝们可以翻阅API,找到println()的函数体,发现里面有一步就是调用toString()函数,把实例转化为字符串打印。不管println()的参数是s还是o,运行代码时打印出来的字符串都是一样的。

这个结果说明了什么?说明了本段代码第一行的“o.toString();”中的toString()函数是Integer类中的。换一句话说,看函数属于哪个类的,要看的是dotted notation之前的object reference所指向的实例的类型,而非object reference的类型。这是因为object reference的类型往往是object reference所指向的实例的类型的父类,根据polymorphism性质。如果现在还对polymorphism性质非常陌生,请一定要移驾理论部分,重头开始学习。

先令o指向新建的Person类的实例。这里的Person类只要叫Person就可以了,因为在文件的开头已经从arrays package导入了这个类。除非导入的其他package中也有Person类,否则一般是用不上fully qualified name的。然后,s1是指向o.toString()的字符串类型的指针。最后,把o和s1打印出来。

此时,toString()方法已经在Person中被重载了,所以打印出来的两个字符串应该都是:123duckGoldenDuck。

如果把Person类中的toString()方法的重载comment out,那么编译器还是先会到Person类中去找toString()函数,这时没有找到,但是编译器不会放弃,继续找Person类的父类,即找到Object class,在这个类里面有,所以不会报出编译错误。假如是Object类中的toString()函数,打印出来的结果会是full qualified name of the class @ hash code,即arrays.Person@2a139a55。当然,hash code是Java virtual machine自己分配的,每次运行的结果、不同机器运行的结构肯定都是不一样的。

新建一个Person类的实例,内容和前面一段代码中的实例一样,并用p指向这个实例。调用函数equals()判定o和p是不是相同。在arrays.Person类的equals()函数中,先会看p是不是一个空指针,再会看p是不是Person类型,如果不是则没有办法比较;如果是,就看o和p两个Person实例的SSN attribute是不是两个相同的字符串。

我们再把arrays.Person类中的equals()函数的重载给comment out,那么就会找到Object类中的equals()函数来执行,此时“o.equals(p)”就相当于“o == p”,即直接判定两个指针是否指向相同的区域,而不是指针指向的实例的内容是否相同。o和p显然是两个存放在不同位置上的实例,所以返回值肯定是false。

在继承这一章中,除了讲到子类可以修改父类中已有的成员,或者增加新的成员,还讲到了一些特殊的classes:abstract class and interfaces.鉴于到勤奋的宝宝们已经看到了本文的第三部分,本黄鸭就顺便把abstract class也讲一讲。

所谓abstract class,就是含有至少一个abstract method的类。所谓abstract method,是因为some methods are too general to be implemented at parent class level,所以只有函数头没有函数体,所有这个abstract class的子类都必须要overriding父类中所有的abstract methods。

在编写abstract class的时候,需要强调一下的是the keyword "abstract"isadded to: 1, abstract method. 2, class definition。现在,有一个abstract class的例子:

上面这段代码确实符合abstract class的定义,因为evaluate()函数就是一个abstract method。而且evaluate()函数只有函数头。但是,想要给evaluate()函数加上函数体也不是不可以的,比如下面几种方案:

加上函数体的evaluate()也可以被child class overridden。但是,在we do not know how to evaluate the expressions的情况下,avoiding to write something stupid/ useless是一个非常中肯的建议。另外,有函数体的evaluate()函数不强制child class必须要overriding,但是没有函数体的必须overriding,否则会出现编译错误。

欢迎使用本黄鸭编写的小程序~

微信公众号二维码:

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180930G1DTRC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券