第六章复用类

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第六章复用类复用程序代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅是能够复制程序代码并对之加以改变还是不够的,它还必须能够做更多的事情。上述方法常为C这类过程型(procedural)语言所使用,但收效并不是很好。正如Java中所有事物一样,问题解决都是围绕着类(class)展开的。你可以通过创建新类来复用程序代码,而不必再重头开始编写。你可以使用别人业已开发并调试好的类。此方法中的窍门在于使用类而不破坏现有程序代码。你将会在本章中看到两种达到这一目的的方法。第一种方法非常直观:你只需在新的类中产生现有类的对象。由于新的类是由现有类的对象所组成,所以这种方法被称为组合(composition)。该方法只是复用了现有程序代码的功能,而非它的形式。第二种方法则更细致一些,它按照现有类的类型来创建新类。你无需改变旧有类的形式,仅仅只是采用它的形式并在其中添加新代码。这种神奇的方式被称为继承(inheritance),而且编译器可以完成其中大部分工作。继承是面向对象程序的基石之一,我们将在第七章探究其含义与功能。就组合(composition)和继承(inheritance)而言,其语法和行为大多是相似的。由于它们是利用现有类型生成新类型,所以这样做极富意义。在本章中,你将会了解到这两种程序代码重用机制。组合(composition)语法到目前为止,本书已多次使用组合技术。你仅需将对象引用置于新类之中即可。例如,假设你需要某个对象,它需要具有多个string对象、两三个基本类型数据、以及另一个类的对象。对于非基本类型的对象,你必须将其引用置于新的类中,而现在,你只需直接定义基本类型数据://:c06:SprinklerSystem.java//Compositionforcodereuse.importcom.bruceeckel.simpletest.*;classWaterSource{privateStrings;WaterSource(){System.out.println(WaterSource());s=newString(Constructed);}publicStringtoString(){returns;}}publicclassSprinklerSystem{privatestaticTestmonitor=newTest();privateStringvalve1,valve2,valve3,valve4;privateWaterSourcesource;privateinti;privatefloatf;publicStringtoString(){returnvalve1=+valve1+\n+valve2=+valve2+\n+valve3=+valve3+\n+valve4=+valve4+\n+i=+i+\n+f=+f+\n+source=+source;}publicstaticvoidmain(String[]args){SprinklerSystemsprinklers=newSprinklerSystem();System.out.println(sprinklers);monitor.expect(newString[]{valve1=null,valve2=null,valve3=null,valve4=null,i=0,f=0.0,source=null});}}///:~在上面两个类所定义的方法中,有一个很特殊:toString()。不久你将会了解到每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个string而你却只有一个对象时,该方法便会被调用。所以在sprinklerSystem.toString()的表达式中:source=+source;编译器将会得知你想要将一个string对象同watersource相加。由于你只能将一个string和另一个string相加,因此编译器会告诉你:“我将调用toString(),把source转换成为一个string!”这样做之后,它就能够将两个string连接到一起并将结果传递给System.out.println()。每当你想要使你所创建的类具备这样的行为时,你仅需要编写一个toString()方法即可。正如我们在第2章中所提到的,类中的基本类型数据能够自动被初始化为零。但是对象引用会被初始化为null,而且如果你试图为它们调用任何方法,都会得到一个异常(exception)。如果我们可以在不出现异常的前提下将其内容打印出来,将会是件有益并且有用的事情。编译器并不是简单地为每一个引用都创建缺省对象,这一点是很有意义的,因为真要是那样做的话,就会在许多情况下增加不必要的负担。如果你想初始化这些引用,可以在代码中的下列位置进行:1.在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化。2.在类的构造器中。3.就在你确实需要使用这些对象之前。这种方式被称为“惰性初始化(lazyinitialization)”。在不必每次都生成对象的情况下,这种方式可以减少额外的负担。以下是三种示例://:c06:Bath.java//Constructorinitializationwithcomposition.importcom.bruceeckel.simpletest.*;classSoap{privateStrings;Soap(){System.out.println(Soap());s=newString(Constructed);}publicStringtoString(){returns;}}publicclassBath{privatestaticTestmonitor=newTest();privateString//Initializingatpointofdefinition:s1=newString(Happy),s2=Happy,s3,s4;privateSoapcastille;privateinti;privatefloattoy;publicBath(){System.out.println(InsideBath());s3=newString(Joy);i=47;toy=3.14f;castille=newSoap();}publicStringtoString(){if(s4==null)//Delayedinitialization:s4=newString(Joy);returns1=+s1+\n+s2=+s2+\n+s3=+s3+\n+s4=+s4+\n+i=+i+\n+toy=+toy+\n+castille=+castille;}publicstaticvoidmain(String[]args){Bathb=newBath();System.out.println(b);monitor.expect(newString[]{InsideBath(),Soap(),s1=Happy,s2=Happy,s3=Joy,s4=Joy,i=47,toy=3.14,castille=Constructed});}}///:~请注意,在Bath的构造器中,有一行语句在所有初始化产生之前就已经执行了。如果你没有在定义处初始化,那么除非发生了不可避免的运行期异常,否则将不能保证信息在发送给对象引用之前已经被初始化。当toString()被调用时,它将填充s4的值,以确保所有的数据成员(fields)在被使用之时已被妥善初始化了。继承(inheritance)的语法继承是所有OOP语言和Java语言的组成部分。当你在创建一个类时,你总是在继承,因此,除非你已明确指出要从其他类中继承,否则你就是在隐式地从Java的标准根源类object进行继承。组合的语法比较平实,但要执行继承,其语法却要采用截然不同的形式。在继承过程中,你需要先声明:“新类与旧类相似。”通常,你首先给类确定一个名称,但在书写类主体的左边花括号之前,应先写下关键字extends,并在其后写下基类的名称。当你这么做时,会自动得到基类中所有的数据成员和成员方法。例如://:c06:Detergent.java//Inheritancesyntax&properties.importcom.bruceeckel.simpletest.*;classCleanser{protectedstaticTestmonitor=newTest();privateStrings=newString(Cleanser);publicvoidappend(Stringa){s+=a;}publicvoiddilute(){append(dilute());}publicvoidapply(){append(apply());}publicvoidscrub(){append(scrub());}publicStringtoString(){returns;}publicstaticvoidmain(String[]args){Cleanserx=newCleanser();x.dilute();x.apply();x.scrub();System.out.println(x);monitor.expect(newString[]{Cleanserdilute()apply()scrub()});}}publicclassDetergentextendsCleanser{//Changeamethod:publicvoidscrub(){append(Detergent.scrub());super.scrub();//Callbase-classversion}//Addmethodstotheinterface:publicvoidfoam(){append(foam());}//Testthenewclass:publicstaticvoidmain(String[]args){Detergentx=newDetergent();x.dilute();x.apply();x.scrub();x.foam();System.out.println(x);System.out.println(Testingbaseclass:);monitor.expect(newString[]{Cleanserdilute()apply()+Detergent.scrub()scrub()foam(),Testingbaseclass:,});Cleanser.main(args);}}///:~这个程序示范了Java的许多特性。首先,在Cleanser的append()方法中,我们用“+=”操作符将几个String对象连接成s,此操作符是Java设计者重载的用来处理String对象的两个操作符之一(另一个是“+”)。其次,Cleanser和Detergent均含有main()方法。你可以为你的每个类都创建一个main()方法。一般我们都建议以这种方式来编写程序代码,以使测试代码被包装在类中。即使是一个程序中含有多个类,也只有命令行所调用的那个类的main()方法会被调用(只要这个main()是public,其所属的类是否为public则不用考虑)。因此,在此例中,如果你的命令行是javaDetergent,那么Detergent.main()将会被调用。但即使Cleanser不是一个public类,如果你的命令行是javaCleanser,那么Cleanser.main()仍然会被调用。这种在每个类中都设置一个main()方法的技术可使每个类的单元测试都变得简便易行。而且你在完成单元测试之后,也无需删除main(),你可以将其留待下次测试。在此例中,你可以看到Detergent.main()明确调用了Cleanser.main(),并将其从命令行获取的参数传递给了它。当然,你也可以向其传递任意的String数组。C

1 / 30
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功