首页 理论教育 大话设计模式:构造型模式

大话设计模式:构造型模式

时间:2023-11-03 理论教育 版权反馈
【摘要】:第十三章可恶的皇帝——构造型模式13.1可恶的皇帝时间:12月29日地点:大B房间人物:大B,小A大B:“在遥远的过去,有这么一个与世无争的小村子,村里有一个村长、村子之外有一个可恶的皇帝和很多的村民。在这个小村子,发生了许许多多的故事。”

大话设计模式:构造型模式

第十三章 可恶的皇帝——构造型模式

13.1 可恶的皇帝

时间:12月29日  地点:大B房间  人物:大B,小A

大B:“在遥远的过去,有这么一个与世无争的小村子,村里有一个村长(A)、村子之外有一个可恶的皇帝(E)和很多的村民(Bs)。在这个小村子,发生了许许多多的故事。”

小A:“很多的故事?嘿嘿!我最喜欢听故事了,说来听听。”

大B:“皇帝要让所有的村民交租,他要经历下面的流程:他首先跑到村民b1那里收租,村民b1的家里只有门,他就从门进入。他又跑到村民b2那里收租,村民b2家里只有窗,没有门,他就从窗进入。村民b3家里没有门,也没有窗,皇帝只好采用直升飞机空降的方式进入。终于有一天,皇帝再也受不了了,他把村长叫过来,对他说:以后我收租,只找你一个,从门进入,我收谁的你就跑腿。从此之后,可怜的村长就成了跑腿的人。皇帝很高兴,他把自己的创意称之为FACADE。时光如梭,老村长不停的跑腿,终于累死了。于是村民b3当选为村长。这年,皇帝过来收租,发现怎么都进不去村长的家里:村长家里没有门!皇帝想,‘妈的,总不成每次都让我用直升飞机空降来收租吧?’邪恶的皇帝又想了一个主意,他对村民b4说:‘你在村长家外面造一个房子,把他的家全部包围起来,盖一个又宽又大的门,以后我找村长就找你了’皇帝很高兴,他又不用跑腿了,他把自己的想法称之为Adapter,而村长就成了Adaptee时间就像小风一样过着,村长不停的换,而邪恶的皇帝却始终活着,但是他已经厌倦了每次为新的村长找一个Adapter。他又开始思考了。他发现村里面的村民bx和自己一样的长寿。于是他改变了自己的策略,他让bx做这样的事情:准备好村长,准备好门。每次收租的时候,他都只需要去bx那里问一下:‘现在的村长是谁?’皇帝又胜利了,他把自己的方法称之为Bridge。有一天,邻国的女皇想到这个小村子里面参观。皇帝一想,‘坏了,这些村民个个连衣服都买不起,光着屁股,我国的威严何在?’于是,皇帝把秘书d叫过来,对他说:‘你给每个村民一件漂亮的衣服,别让他们给我丢脸!’最后,邻国的女皇看到的全是穿着漂亮衣服的村民。皇帝很高兴,于是他把自己的方法叫做Decorator。村子越来越大,村民越来越多。终于有一天,村子分裂了,变成了两个村子。皇帝想,每次收租我都找两个村长,太麻烦了!于是邪恶的皇帝又有了点子:在村子之上设立乡政府,在乡政府之上设立县政府…… 于是不管将来有多少的村民,自己都很方便管理,他把自己的方法叫做 Composite。终于有一天,皇帝有了自己的王国,村门很多很多,管理起来太过于复杂。皇帝每天要处理每个村民的事情,忙的头昏脑涨的。于是,邪恶的皇帝又有点子了,他成立了一个特殊部门‘部长’,然后又制定了惩罚规定,叫做‘拘留,坐牢,流放,砍头’。每当有一个村民发生问题的时候,皇帝就问‘部长’:他的问题怎么办?部长说:坐牢。又有一个村民发生了问题,皇帝问部长:怎么半?部长说:他的问题以前的不行,我又发明了一种新的处理方法,叫做‘凌迟’。皇帝很高兴,自己终于又可以轻轻松松的管理国家了。他把自己的方法称之为Flyweight。皇帝继续做着自己的美梦。他越来越依赖于自己的宠臣太监t了,不管有什么问题,他都问t,然后t去处理。他问t:我们有多少国民阿?t说,1000万。他问t说,我们有多少收入阿,t说1000万。其实t已经大权独揽,自己腰包里面赚了无数的10000万了。t自己偷偷的大笑:哈哈,我就是传说中的Proxy阿!!”

13.2 构造器

小A:“什么是构造器?”

大B:“首先要注意的是Java的构造器并不是函数,所以他并不能被继承,这在我们extends的时候写子类的构造器时比较的常见,即使子类构造器参数和父类的完全一样,我们也要写super就是因为这个原因。构造器的修饰符比较的有限,仅仅只有public private protected这三个,其他的例如任何修饰符都不能对其使用,也就是说构造器不允许被成名成抽象、同步、静态等等访问限制以外的形式。

因为构造器不是函数,所以它是没有返回值的,也不允许有返回值。但是这里要说明一下,构造器中允许存在return语句,但是return什么都不返回,如果你指定了返回值,虽然编译器不会报出任何错误,但是JVM会认为他是一个与构造器同名的函数罢了,这样就会出现一些莫名其妙的无法找到构造器的错误,这里是要加倍注意的。”

小A:“在我们extends一个子类的时候经常会出现一些意想不到的问题,你能和我说说一些和构造器有关的吗?”

大B:“首先说一下Java在构造实例时的顺序(不讨论装载类的过程),构造的粗略过程如下 1、分配对象空间,并将对象中成员初始化为0或者空,Java不允许用户操纵一个不定值的对象。2、执行属性值的显式初始化(这里有一点变化,一会解释,但大体是这样的)3、执行构造器4、将变量关联到堆中的对象上。”

小A:“能介绍一下准备知识吗?以备一会来详细说明这个的流程。”

大B:“this() super()是你如果想用传入当前构造器中的参数或者构造器中的数据调用其他构造器或者控制父类构造器时使用的,在一个构造器中你只能使用this()或者super()之中的一个,而且调用的位置只能在构造器的第一行, 在子类中如果你希望调用父类的构造器来初始化父类的部分,那就用合适的参数来调用super(),如果你用没有参数的super()来调用父类的构造器(同时也没有使用this()来调用其他构造器),父类缺省的构造器会被调用,如果父类没有缺省的构造器,那编译器就会报一个错误,注意这里,我们经常在继承父类的时候构造器中并不写和父类有关的内容,此时如果父类没有缺省构造器,就会出现编译器添加的缺省构造器给你添麻烦的问题了哦。例如:Class b extends a{public b(){}}就没有任何有关父类构造器的信息,这时父类的缺省构造器就会被调用。”

举个SL-275中的例子

大B:“你必须在构造器的第一行放置super或者this构造器,否则编译器会自动地放一个空参数的super构造器的,其他的构造器也可以调用super或者this,调用成一个递归构造链,最后的结果是父类的构造器(可能有多级父类构造器)始终在子类的构造器之前执行,递归的调用父类构造器。在具体构造类实例的过程中,上边过程的第二步和第三步是有一些变化的,这里的顺序是这样的,分配了对象空间及对象成员初始化为默认值之后,构造器就递归的从继承树由根部向下调用,每个构造器的执行过程是这样的:1、Bind构造器的参数2、如果显式的调用了this,那就递归调用this构造器然后跳到步骤5。3、递归调用显式或者隐式的父类构造器,除了Object以外,因为它没有父类4、执行显式的实例变量初始化(也就是上边的流程中的第二步,调用返回以后执行,这个步骤相当于在父构造器执行后隐含执行的,看样子像一个特殊处理)5、执行构造器的其它部分。”

小A:“好像有点明白了。”

大B:“这里的步骤很重要哦!从这个步骤中可以很明显的发现这个实例初始化时的递归调用过程。”

13.3 使用构造器中的注意事项

小A:“在使用构造器中的要注意哪些事项?”

大B:“1、构造器中一定不要创建自身的实例,否则会造成调用栈溢出错误。这个规则也适用于对象的实例变量,如果对象中有自身的引用,这个引用一定不能在定义中或者构造器中初始化。”

大B:“以上三种情况都会造成栈溢出,呵呵,这样会造成一个无穷递归的调用栈。2、如果父类是一个抽象类,那通过调用父类的构造器,也可以将它初始化,并且初始化其中的数据。3、如果你要在构造器中调用一个方法时,将该方法声明为private。对于这个规则是需要一些说明的,假使你的父类构造器中要调用一个非静态方法,而这个方法不是private的又被子类所重载,这样在实际创建子类的过程中递归调用到了父类的构造器时,父类构造器对这个方法的调用就会由于多态而实际上调用了子类的方法,当这个子类方法需要用到子类中实例变量的时候,就会由于变量没有初始化而出现异常(至于为什么子类中的实例变量没有初始化可以参考上边的实例初始化过程),这是Java不想看到的情况。而当父类构造器中调用的方法是一个private方法时,多态就不会出现,也就不会出现父类构造器调用子类方法的情况,这样可以保证父类始终调用自己的方法,即使这个方法中调用了父类中的实例变量也不会出现变量未初始化的情况(变量初始化总是在当前类构造器主体执行之前进行)。”

13.4 理解构造器--构造器和方法的区别

大B:“要学习Java,你必须理解构造器。因为构造器可以提供许多特殊的方法,这个对于初学者经常混淆。但是,构造器和方法又有很多重要的区别。”

小A:“那它的功能和作用的有什么不同?”(www.xing528.com)

大B:“功能和作用的不同下面我就详细给你讲一下。构造器是为了创建一个类的实例。这个过程也可以在创建一个对象的时候用到:Platypus p1 = new Platypus(); 相反,方法的作用是为了执行Java代码。 修饰符,返回值和命名的不同。”

小A:“构造器和方法在这里有什么区别?”

大B:“修饰符,返回值,命名。和方法一样,构造器可以有任何访问的修饰: public, protected, private或者没有修饰(通常被package 和 friendly调用)不同于方法的是,构造器不能有以下非访问性质的修饰:abstract, final,native, static,或者 synchronized。 返回类型也是非常重要的。方法能返回任何类型的值或者无返回值(void),构造器没有返回值,也不需要void。”

小A:“构造器和方法命名有什么不同?”

大B:“构造器使用和类相同的名字,而方法则不同。按照习惯,方法通常用小写字母开始,而构造器通常用大写字母开始。构造器通常是一个名词,因为它和类名相同;而方法通常更接近动词,因为它说明一个操作。‘this’的用法,构造器和方法使用关键字this有很大的区别。方法引用this指向正在执行方法的类的实例。静态方法不能使用this关键字,因为静态方法不属于类的实例,所以this也就没有什么东西去指向。构造器的this指向同一个类中,不同参数列表的另外一个构造器。”

我们看看下面的代码:

大B:“在刚才讲的代码中,有2个不同参数列表的构造器。第一个构造器,给类的成员name赋值,第二个构造器,调用第一个构造器,给成员变量name一个初始值‘John/Mary Doe’。在构造器中,如果要使用关键字this,那么,必须放在第一行,如果不这样,将导致一个编译错误。‘super’的用法,构造器和方法,都用关键字super指向超类,但是用的方法不一样。方法用这个关键字去执行被重载的超类中的方法。”

看下面的例子:

大B:“从我们刚才讲的例子中,使用super.getBirthInfo()去调用超类Mammal中被重载的方法。 构造器使用super去调用超类中的构造器。而且这行代码必须放在第一行,否则编译将出错。”

看下面的例子:

大B:“在上面这个没有什么实际意义的例子中,构造器 Child()包含了 super,它的作用就是将超类中的构造器SuperClassDemo实例化,并加到 Child类中。”

小A:“编译器怎样自动加入代码?”

大B:“编译器自动加入代码到构造器,对于这个,Java程序员新手可能比较混淆。当我们写一个没有构造器的类,编译的时候,编译器会自动加上一个不带参数的构造器。”

例如:public class Example {}

编译后将如下代码:

在构造器的第一行,没有使用super,那么编译器也会自动加上,例如:

编译器会加上代码,如下:

仔细想一下,就知道下面的代码

public class Example {}

经过会被编译器加代码形如:

继承

构造器是不能被继承的。子类可以继承超类的任何方法。看看下面的代码:

大B:“我们来注意一下Java功能语句。修饰 不能用bstract, final, native, static, or synchronized 能 返回类型,没有返回值,没有void ,有返回值,或者void ,命名 和类名相同;通常为名词,大写开头,通常代表一个动词的意思,小写开头,this 指向同一个类中另外一个构造器,在第一行指向当前类的一个实例,不能用于静态方法, super 调用父类的构造器,在第一行 调用父类中一个重载的方法。继承,构造器不能被继承,方法可以被继承 ,编译器自动加入一个缺省的构造器,自动加入(如果没有)不支持 。编译器自动加入一个缺省的调用到超类的构造器,自动加入(如果没有)不支持。”

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈