![面向对象的思考过程(原书第5版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/429/39980429/b_39980429.jpg)
3.1.4 使用多个构造函数
大多数情况下可以用多种方式创建对象。为了适应这种情况,你需要提供多个构造函数。例如,请看Count类:
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/046-i.jpg?sign=1739615649-BCOQQHL0jZrxUoH4ryRy0oHpDkJE9evJ-0-af8bef6a79075d0af3a174fefaef8c69)
一方面,我们可以初始化属性count为0,实现这一点很简单,即可以在一个构造函数中初始化count为0,如下所示:
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/046-2-i.jpg?sign=1739615649-UgDLZt3G2sBCSi7x6EQAIwbpPjt5sCip-0-f5bfc010db48b16a32d890ed1b52ae5d)
另一方面,我们可以传递一个初始化参数,从而可以设置count为其他数字:
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/046-3-i.jpg?sign=1739615649-DuNFBx0k9eZLE51fNwCsKVi0SITGd0zm-0-73b9b17d7bd0858893b710646efefad1)
这叫作重载方法(重载适用于所有方法,不只是构造函数)。大部分的面向对象语言都提供了重载方法的功能。
重载方法
重载可以让程序员重复使用相同的方法名,只要每次方法签名不同即可。方法签名包含了方法名以及参数列表(如图3.1所示)。
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/3-1.jpg?sign=1739615649-VT3X9LfUpy22CTt8GIrFXjKH2ZZFf2GQ-0-e11bdc1258c35fbe660dbc2d66df7af8)
图3.1 签名的组成
所以,以下所有方法均拥有不同的签名:
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/047-i.jpg?sign=1739615649-MLon8SjjBDzjk0VbWccaSmnImvI7zRhB-0-90bbb4bffdd678ccd5bd05547fbdcb9b)
签名
方法签名可能包含返回值类型,也可能不包含返回值类型,这取决于不同的语言。在Java和C#中,返回值类型并不属于签名的一部分。例如,以下代码即使返回值类型不同,也不能通过编译:
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/047-2-i.jpg?sign=1739615649-TbxHakA79XVsid9Dai90ijm0RTGS4X6p-0-8247986487311144534b50c9a6b9f31b)
了解签名的最好方式是编写一些代码然后进行编译。
通过使用不同的签名,你可以根据不同的构造函数来构造对象。如果你不能保证每次都能掌握足够的信息,那么很适合这种方式。例如,当创建一个购物车时,顾客可能已经登录了自己的账号(你会得到顾客的所有信息)。而一个全新的顾客可能会向购物车中放入产品,但没有任何账号信息,这两种情况下构造函数的初始化方式是不同的。
使用UML对类建模
我们再回头看第2章中用到的数据库阅读器例子。构造数据库阅读器有两种方式:
·传入数据库名称和光标在数据库中的起始位置
·传入数据库名称和光标在数据库中的期望位置
图3.2展示了DataBaseReader类的类图。注意图中列出了此类的两个构造函数。尽管该图显示了两个构造函数,但并未包含参数列表,所以无法区分出这两个构造函数。为了区分这两个构造函数,可以查看下面列出的DataBaseReader类的对应代码。
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/3-2.jpg?sign=1739615649-9cxXjzpGW6GlkCaKGXOEI787MwkIckbX-0-cb85b736017f52533023edcfd4c8514c)
图3.2 DataBaseReader类图
无返回值类型
注意在该类图中,构造函数没有返回值类型。除了构造函数之外,其他所有方法必须要有返回值类型。
以下代码片段展示了该类的构造函数,以及构造函数如何初始化属性(参见图3.3):
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/3-3.jpg?sign=1739615649-0ENKmzyfm0CwldcDWWlblyIaWMsuZheR-0-a527b3aea9eba3bfbcb1d6342b8f8ee3)
图3.3 创建新对象
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/048-i.jpg?sign=1739615649-xCBJJne6HKWeIOgGIDLzfc4CqmQAoywn-0-5b5ccbbf98281b9163ddb759a24dfdad)
请注意在两个例子中如何初始化startPosition。如果构造函数没有通过参数列表传递信息,那么startPostion会被初始化为默认值,即0。
如何构造父类
当使用继承时,你必须知道如何构造父类。请记住当使用继承时,也继承了父类的所有东西,因此必须熟悉父类的数据和行为。任何继承的属性都是完全可见的。然而对构造函数的继承则是不可见的。如果遇到了new关键字,那么会分配对象,并发生以下步骤(参见图3.4):
1.在构造函数中会调用父类的构造函数。如果没有显式调用父类的构造函数,那么系统会默认自动调用。不过可以在字节码中看到这段代码。
2.对象中的所有属性都会被初始化。这些属性是类定义中的属性(实例变量),不是构造函数或其他方法中的属性(局部变量)。在DataBaseReader代码中,整数startPosition是类的实例属性。
3.执行构造函数中的其余代码。
![](https://epubservercos.yuewen.com/7DCAEA/20818201101955406/epubprivate/OEBPS/Images/3-4.jpg?sign=1739615649-kbwATZiWH5Geozon8faeOPMeQsDgxPyO-0-801ca5e90ff4a2d5ac2bf18fbdd598dc)
图3.4 构造对象