﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-小猪快跑JAVA技术</title><link>http://www.blogjava.net/fjq639/</link><description>JAVA技术</description><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 06:05:25 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 06:05:25 GMT</pubDate><ttl>60</ttl><item><title>【转载】追MM与Java的23种设计模式 </title><link>http://www.blogjava.net/fjq639/archive/2005/12/26/25448.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Mon, 26 Dec 2005 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/26/25448.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/25448.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/26/25448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/25448.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/25448.html</trackback:ping><description><![CDATA[<P>1、FACTORY?追MM少不了请吃饭了，麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西，虽然口味有所不同，但不管你带MM去麦当劳或肯德基，只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory </P>
<P>　　工厂模式：客户类和工厂类分开。消费者任何时候需要某种产品，只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时，工厂类也要做相应的修改。如：如何创建及如何向客户端提供。 </P>
<P>　　2、BUILDER?MM最爱听的就是“我爱你”这句话了，见到不同地方的MM,要能够用她们的方言跟她说这句话哦，我有一个多种语言翻译机，上面每种语言都有一个按键，见到MM我只要按对应的键，它就能够用相应的语言说出“我爱你”这句话了，国外的MM也可以轻松搞掂，这就是我的“我爱你”builder。（这一定比美军在伊拉克用的翻译机好卖） </P>
<P>　　建造模式：将产品的内部表象和产品的生成过程分割开来，从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化，客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 </P>
<P>　　3、FACTORY METHOD?请MM去麦当劳吃汉堡，不同的MM有不同的口味，要每个都记住是一件烦人的事情，我一般采用Factory Method模式，带着MM到服务员那儿，说“要一个汉堡”，具体要什么样的汉堡呢，让MM直接跟服务员说就行了。 </P>
<P>　　工厂方法模式：核心工厂类不再负责所有产品的创建，而是将具体创建的工作交给子类去做，成为一个抽象工厂角色，仅负责给出具体工厂类必须实现的接口，而不接触哪一个产品类应当被实例化这种细节。 </P>
<P>　　4、PROTOTYPE?跟MM用QQ聊天，一定要说些深情的话语了，我搜集了好多肉麻的情话，需要时只要copy出来放到QQ里面就行了，这就是我的情话prototype了。（100块钱一份，你要不要） </P>
<P>　　原始模型模式：通过给出一个原型对象来指明所要创建的对象的类型，然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类，产品类不需要非得有任何事先确定的等级结构，原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 </P>
<P>　　5、SINGLETON?俺有6个漂亮的老婆，她们的老公都是我，我就是我们家里的老公Sigleton，她们只要说道“老公”，都是指的同一个人，那就是我(刚才做了个梦啦，哪有这么好的事) </P>
<P>　　单例模式：单例模式确保某一个类只有一个实例，而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [b:9ceca65206]结构型模式[/b:9ceca65206] </P>
<P>　　6、ADAPTER?在朋友聚会上碰到了一个美女Sarah，从香港来的，可我不会说粤语，她不会说普通话，只好求助于我的朋友kent了，他作为我和Sarah之间的Adapter，让我和Sarah可以相互交谈了(也不知道他会不会耍我) </P>
<P>　　适配器（变压器）模式：把一个类的接口变换成客户端所期待的另一种接口，从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 </P>
<P>　　7、BRIDGE?早上碰到MM，要说早上好，晚上碰到MM，要说晚上好；碰到MM穿了件新衣服，要说你的衣服好漂亮哦，碰到MM新做的发型，要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题，自己用BRIDGE组合一下不就行了 </P>
<P>　　桥梁模式：将抽象化与实现化脱耦，使得二者可以独立的变化，也就是说将他们之间的强关联变成弱关联，也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系，从而使两者可以独立的变化。 </P>
<P>　　8、COMPOSITE?Mary今天过生日。“我过生日，你要送我一件礼物。”“嗯，好吧，去商店，你自己挑。”“这件T恤挺漂亮，买，这条裙子好看，买，这个包也不错，买。”“喂，买了三件了呀，我只答应送一件礼物的哦。”“什么呀，T恤加裙子加包包，正好配成一套呀，小姐，麻烦你包起来。”“……”，MM都会用Composite模式了，你会了没有？ </P>
<P>　　合成模式：合成模式将对象组织到树结构中，可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 </P>
<P>　　9、DECORATOR?Mary过完轮到Sarly过生日，还是不要叫她自己挑了，不然这个月伙食费肯定玩完，拿出我去年在华山顶上照的照片，在背面写上“最好的的礼物，就是爱你的Fita”，再到街上礼品店买了个像框（卖礼品的MM也很漂亮哦），再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……，我们都是Decorator，最终都在修饰我这个人呀，怎么样，看懂了吗？ </P>
<P>　　装饰模式：装饰模式以对客户端透明的方式扩展对象的功能，是继承关系的一个替代方案，提供比继承更多的灵活性。动态给一个对象增加功能，这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 </P>
<P>　　10、FACADE?我有一个专业的Nikon相机，我就喜欢自己手动调光圈、快门，这样照出来的照片才专业，但MM可不懂这些，教了半天也不会。幸好相机有Facade设计模式，把相机调整到自动档，只要对准目标按快门就行了，一切由相机自动调整，这样MM也可以用这个相机给我拍张照片了。 </P>
<P>　　门面模式：外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口，使得子系统更易于使用。每一个子系统只有一个门面类，而且此门面类只有一个实例，也就是说它是一个单例模式。但整个系统可以有多个门面类。 </P>
<P>　　11、FLYWEIGHT?每天跟MM发短信，手指都累死了，最近买了个新手机，可以把一些常用的句子存在手机里，要用的时候，直接拿出来，在前面加上MM的名字就可以发送了，再不用一个字一个字敲了。共享的句子就是Flyweight，MM的名字就是提取出来的外部特征，根据上下文情况使用。 </P>
<P>　　享元模式：FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部，不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态，它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来，将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象，而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。 </P>
<P>　　12、PROXY?跟MM在网上聊天，一开头总是“hi,你好”,“你从哪儿来呀？”“你多大了？”“身高多少呀？”这些话，真烦人，写个程序做为我的Proxy吧，凡是接收到这些话都设置好了自动的回答，接收到其他的话时再通知我回答，怎么样，酷吧。 </P>
<P>　　代理模式：代理模式给某一个对象提供一个代理对象，并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下，客户不想或者不能够直接引用一个对象，代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象，而仅仅持有一个被代理对象的接口，这时候代理对象不能够创建被代理对象，被代理对象必须有系统的其他角色代为创建并传入。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [b:9ceca65206]行为模式[/b:9ceca65206] </P>
<P>　　13、CHAIN OF RESPONSIBLEITY?晚上去上英语课，为了好开溜坐到了最后一排，哇，前面坐了好几个漂亮的MM哎，找张纸条，写上“Hi,可以做我的女朋友吗？如果不愿意请向前传”，纸条就一个接一个的传上去了，糟糕，传到第一排的MM把纸条传给老师了，听说是个老处女呀，快跑! </P>
<P>　　责任链模式：在责任链模式中，很多对象由每一个对象对其下家的引用而接 </P>
<P>　　起来形成一条链。请求在这个链上传递，直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求，系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择：承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。 </P>
<P>　　14、COMMAND?俺有一个MM家里管得特别严，没法见面，只好借助于她弟弟在我们俩之间传送信息，她对我有什么指示，就写一张纸条让她弟弟带给我。这不，她弟弟又传送过来一个COMMAND，为了感谢他，我请他吃了碗杂酱面，哪知道他说：“我同时给我姐姐三个男朋友送COMMAND，就数你最小气，才请我吃面。”， </P>
<P>　　命令模式：命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开，委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来，使得请求的一方不必知道接收请求的一方的接口，更不必知道请求是怎么被接收，以及操作是否执行，何时被执行以及是怎么被执行的。系统支持命令的撤消。 </P>
<P>　　15、INTERPRETER?俺有一个《泡MM真经》，上面有各种泡MM的攻略，比如说去吃西餐的步骤、去看电影的方法等等，跟MM约会时，只要做一个Interpreter，照着上面的脚本执行就可以了。 </P>
<P>　　解释器模式：给定一个语言后，解释器模式可以定义出其文法的一种表示，并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后，使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构，也就是一系列的组合规则。每一个命令对象都有一个解释方法，代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。 </P>
<P>　　16、ITERATOR?我爱上了Mary，不顾一切的向她求婚。 </P>
<P>　　Mary：“想要我跟你结婚，得答应我的条件” </P>
<P>　　我：“什么条件我都答应，你说吧” </P>
<P>　　Mary：“我看上了那个一克拉的钻石” </P>
<P>　　我：“我买，我买，还有吗？” </P>
<P>　　Mary：“我看上了湖边的那栋别墅” </P>
<P>　　我：“我买，我买，还有吗？” </P>
<P>　　Mary：“我看上那辆法拉利跑车” </P>
<P>　　我脑袋嗡的一声，坐在椅子上，一咬牙：“我买，我买，还有吗？” </P>
<P>　　…… </P>
<P>　　迭代子模式：迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集，聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中，从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象，每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。 </P>
<P>　　17、MEDIATOR?四个MM打麻将，相互之间谁应该给谁多少钱算不清楚了，幸亏当时我在旁边，按照各自的筹码数算钱，赚了钱的从我这里拿，赔了钱的也付给我，一切就OK啦，俺得到了四个MM的电话。 </P>
<P>　　调停者模式：调停者模式包装了一系列对象相互作用的方式，使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时，不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化，把对象在小尺度的行为上与其他对象的相互作用分开处理。 </P>
<P>　　18、MEMENTO?同时跟几个MM聊天时，一定要记清楚刚才跟MM说了些什么话，不然MM发现了会不高兴的哦，幸亏我有个备忘录，刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存，这样可以随时察看以前的记录啦。 </P>
<P>　　备忘录模式：备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下，将一个对象的状态捉住，并外部化，存储起来，从而可以在将来合适的时候把这个对象还原到存储起来的状态。 </P>
<P>　　19、OBSERVER?想知道咱们公司最新MM情报吗？加入公司的MM情报邮件组就行了，tom负责搜集情报，他发现的新情报不用一个一个通知我们，直接发布给邮件组，我们作为订阅者（观察者）就可以及时收到情报啦 </P>
<P>　　观察者模式：观察者模式定义了一种一队多的依赖关系，让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时，会通知所有观察者对象，使他们能够自动更新自己。 </P>
<P>　　20、STATE?跟MM交往时，一定要注意她的状态哦，在不同的状态时她的行为会有不同，比如你约她今天晚上去看电影，对你没兴趣的MM就会说“有事情啦”，对你不讨厌但还没喜欢上的MM就会说“好啊，不过可以带上我同事么？”，已经喜欢上你的MM就会说“几点钟？看完电影再去泡吧怎么样？”，当然你看电影过程中表现良好的话，也可以把MM的状态从不讨厌不喜欢变成喜欢哦。 </P>
<P>　　状态模式：状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里，每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候，其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时，系统便改变所选的子类。 </P>
<P>　　21、STRATEGY?跟不同类型的MM约会，要用不同的策略，有的请电影比较好，有的则去吃小吃效果不错，有的去海边浪漫最合适，单目的都是为了得到MM的芳心，我的追MM锦囊中有好多Strategy哦。 </P>
<P>　　策略模式：策略模式针对一组算法，将每一个算法封装到具有共同接口的独立的类中，从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类，各种算法在具体的策略类中提供。由于算法和环境独立开来，算法的增减，修改都不会影响到环境和客户端。 </P>
<P>　　22、TEMPLATE METHOD??看过《如何说服女生上床》这部经典文章吗？女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method)，但每个步骤针对不同的情况，都有不一样的做法，这就要看你随机应变啦(具体实现)； </P>
<P>　　模板方法模式：模板方法模式准备一个抽象类，将部分逻辑以具体方法以及具体构造子的形式实现，然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法，从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架，而将逻辑的细节留给具体的子类去实现。 </P>
<P>　　23、VISITOR?情人节到了，要给每个MM送一束鲜花和一张卡片，可是每个MM送的花都要针对她个人的特点，每张卡片也要根据个人的特点来挑，我一个人哪搞得清楚，还是找花店老板和礼品店老板做一下Visitor，让花店老板根据MM的特点选一束花，让礼品店老板也根据每个人特点选一张卡，这样就轻松多了； </P>
<P>　　访问者模式：访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话，接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统，它把数据结构和作用于结构上的操作之间的耦合解脱开，使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易，就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中，而不是分散到一个个的节点类中。当使用访问者模式时，要将尽可能多的对象浏览逻辑放在访问者类中，而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类</P><img src ="http://www.blogjava.net/fjq639/aggbug/25448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-26 15:46 <a href="http://www.blogjava.net/fjq639/archive/2005/12/26/25448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式的原则(转)</title><link>http://www.blogjava.net/fjq639/archive/2005/12/26/25447.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Mon, 26 Dec 2005 07:45:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/26/25447.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/25447.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/26/25447.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/25447.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/25447.html</trackback:ping><description><![CDATA[<BR>&nbsp;&nbsp;&nbsp;近年来，大家都开始注意设计模式。那么，到底我们为什么要用设计模式呢？这么多设计模式为什么要这么设计呢？说实话，以前我还真没搞清楚。就是看大家一口一个"Design&nbsp;pattern"，心就有点发虚。于是就买了本"四人帮"的设计模式，结果看得似懂非懂:看得时候好像是懂了，过一会就忘了。可能是本人比较"愚钝"吧:))最近，有了点感悟。"独乐不如众乐"，与大家分享一下，还望指教!<BR>&nbsp;&nbsp;&nbsp;&nbsp;为什么要提倡"Design&nbsp;Pattern"呢？根本原因是为了代码复用，增加可维护性。那么怎么才能实现代码复用呢？OO界有前辈的几个原则："开－闭"原则(Open&nbsp;Closed&nbsp;Principal)、里氏代换原则、合成复用原则。设计模式就是实现了这些原则，从而达到了代码复用、增加可维护性的目的。<BR>
<H3>&nbsp;&nbsp;&nbsp;&nbsp;一、"开－闭"原则</H3><BR>&nbsp;&nbsp;&nbsp;&nbsp;此原则是由"Bertrand&nbsp;Meyer"提出的。原文是："Software&nbsp;entities&nbsp;should&nbsp;be&nbsp;open&nbsp;for&nbsp;extension,but&nbsp;closed&nbsp;for&nbsp;modification"。就是说模块应对扩展开放，而对修改关闭。模块应尽量在不修改原(是"原"，指原来的代码)代码的情况下进行扩展。那么怎么扩展呢？我们看工厂模式"factory&nbsp;pattern":假设中关村有一个卖盗版盘和毛片的小子，我们给他设计一"光盘销售管理软件"。我们应该先设计一"光盘"接口。如图：<BR>[pre]______________<BR>|&lt;&lt;interface&gt;&gt;|<BR>|&nbsp;光盘&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>|_____________|<BR>|+卖()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>|_____________|[/pre]<BR>而盗版盘和毛片是其子类。小子通过"DiscFactory"来管理这些光盘。代码为：<BR>
<DIV class=codeStyle>
<OL>
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;DiscFactory{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;光盘&nbsp;getDisc(<B><A href="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>&nbsp;name){ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>return</FONT></B>&nbsp;(光盘)<B><A href="http://www.javaresearch.org/source/jdk142/java/lang/Class.java.html" target=_blank><FONT class=classLink><U>Class</U></FONT></A></B>.forName(name).getInstance(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV>有人要买盗版盘，怎么实现呢？<BR>
<DIV class=codeStyle>
<OL>
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;小子{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;main(<B><A href="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html" target=_blank><FONT class=classLink><U>String</U></FONT></A></B>[]&nbsp;args){ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;光盘&nbsp;d=DiscFactory.getDisc(<FONT color=#ff33ff>"盗版盘"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;光盘.卖(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>&nbsp;&nbsp;&nbsp;&nbsp;如果有一天，这小子良心发现了，开始卖正版软件。没关系，我们只要再创建一个"光盘"的子类"正版软件"就可以了。不需要修改原结构和代码。怎么样？对扩展开发，对修改关闭。"开-闭原则"<BR>&nbsp;&nbsp;&nbsp;&nbsp;工厂模式是对具体产品进行扩展，有的项目可能需要更多的扩展性，要对这个"工厂"也进行扩展，那就成了"抽象工厂模式"。<BR>
<H3>&nbsp;&nbsp;&nbsp;&nbsp;二、里氏代换原则</H3><BR>里氏代换原则是由"Barbara&nbsp;Liskov"提出的。如果调用的是父类的话，那么换成子类也完全可以运行。比如：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;光盘&nbsp;d=new&nbsp;盗版盘();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d.卖();<BR>&nbsp;&nbsp;&nbsp;&nbsp;现在要将"盗版盘"类改为"毛片"类，没问题，完全可以运行。Java编译程序会检查程序是否符合里氏代换原则。还记得java继承的一个原则吗？子类overload方法的访问权限不能小于父类对应方法的访问权限。比如"光盘"中的方法"卖"访问权限是"public"，那么"盗版盘"和"毛片"中的"卖"方法就不能是package或private，编译不能通过。为什么要这样呢？你想啊：如果"盗版盘"的"卖"方法是private。那么下面这段代码就不能执行了：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;光盘&nbsp;d=new&nbsp;盗版盘();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d.卖();<BR>可以说：里氏代换原则是继承复用的一个基础。<BR>
<H3>&nbsp;&nbsp;&nbsp;&nbsp;三、合成复用原则</H3><BR>&nbsp;&nbsp;&nbsp;&nbsp;就是说要少用继承，多用合成关系来实现。我曾经这样写过程序：有几个类要与数据库打交道，就写了一个数据库操作的类，然后别的跟数据库打交道的类都继承这个。结果后来，我修改了数据库操作类的一个方法，各个类都需要改动。"牵一发而动全身"!面向对象是要把波动限制在尽量小的范围。<BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在Java中，应尽量针对Interface编程，而非实现类。这样，更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系，"不要与陌生人说话"。这样，城门失火，才不至于殃及池鱼。扩展性和维护性才能提高<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;理解了这些原则，再看设计模式，只是在具体问题上怎么实现这些原则而已。张无忌学太极拳，忘记了所有招式，打倒了"玄幂二老"，所谓"心中无招"。设计模式可谓招数，如果先学通了各种模式，又忘掉了所有模式而随心所欲，可谓OO之最高境界。呵呵，搞笑，搞笑！<BR><BR>这是我的一点心得，大家可能理解得更深刻。还望指教！<BR><BR>我的网站:<A href="http://albert.mycool.net/">http://albert.mycool.net</A><BR>参考图书：《设计模式》、《Java与模式》<img src ="http://www.blogjava.net/fjq639/aggbug/25447.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-26 15:45 <a href="http://www.blogjava.net/fjq639/archive/2005/12/26/25447.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java异常学习心得（转）</title><link>http://www.blogjava.net/fjq639/archive/2005/12/22/25087.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Thu, 22 Dec 2005 07:42:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/22/25087.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/25087.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/22/25087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/25087.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/25087.html</trackback:ping><description><![CDATA[<P>本文重在Java中异常机制的一些概念。写本文的目的在于方便我很长时间后若是忘了这些东西可以通过这片文章迅速回忆起来。</P>
<P>1.&nbsp;异常机制<BR>1.1&nbsp;异常机制是指当程序出现错误后，程序如何处理。具体来说，异常机制提供了程序退出的安全通道。当出现错误后，程序执行的流程发生改变，程序的控制权转移到异常处理器。<BR>1.2&nbsp;传统的处理异常的办法是，函数返回一个特殊的结果来表示出现异常（通常这个特殊结果是大家约定俗称的），调用该函数的程序负责检查并分析函数返回的结果。这样做有如下的弊端：例如函数返回-1代表出现异常，但是如果函数确实要返回-1这个正确的值时就会出现混淆；可读性降低，将程序代码与处理异常的代码混爹在一起；由调用函数的程序来分析错误，这就要求客户程序员对库函数有很深的了解。<BR>1.3&nbsp;异常处理的流程<BR>1.3.1&nbsp;遇到错误，方法立即结束，并不返回一个值；同时，抛出一个异常对象<BR>1.3.2&nbsp;调用该方法的程序也不会继续执行下去，而是搜索一个可以处理该异常的异常处理器，并执行其中的代码</P>
<P>2&nbsp;异常的分类<BR>2.1&nbsp;异常的分类<BR>2.1.1&nbsp;异常的继承结构：基类为Throwable，Error和Exception继承Throwable，RuntimeException和IOException等继承Exception，具体的RuntimeException继承RuntimeException。<BR>2.1.2&nbsp;Error和RuntimeException及其子类成为未检查异常（unchecked），其它异常成为已检查异常（checked）。<BR>2.2&nbsp;每个类型的异常的特点<BR>2.2.1&nbsp;Error体系<BR>Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象（一般是由虚拟机抛出）。如果出现这种错误，除了尽力使程序安全退出外，在其他方面是无能为力的。所以，在进行程序设计时，应该更关注Exception体系。<BR>2.2.2&nbsp;Exception体系<BR>Exception体系包括RuntimeException体系和其他非RuntimeException的体系<BR>2.2.2.1&nbsp;RuntimeException<BR>RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是：如果出现RuntimeException，那么一定是程序员的错误。例如，可以通过检查数组下标和数组边界来避免数组越界访问异常。<BR>2.2.2.2&nbsp;其他（IOException等等）<BR>这类异常一般是外部错误，例如试图从文件尾后读取数据等，这并不是程序本身的错误，而是在应用环境中出现的外部错误。<BR>2.3&nbsp;与C++异常分类的不同<BR>2.3.1&nbsp;其实，Java中RuntimeException这个类名起的并不恰当，因为任何异常都是运行时出现的。（在编译时出现的错误并不是异常，换句话说，异常就是为了解决程序运行时出现的的错误）。<BR>2.3.2&nbsp;C++中logic_error与Java中的RuntimeException是等价的，而runtime_error与Java中非RuntimeException类型的异常是等价的。</P>
<P>3&nbsp;异常的使用方法<BR>3.1&nbsp;声明方法抛出异常&nbsp;<BR>3.1.1&nbsp;语法：throws（略）<BR>3.1.2&nbsp;为什么要声明方法抛出异常？<BR>方法是否抛出异常与方法返回值的类型一样重要。假设方法抛出异常确没有声明该方法将抛出异常，那么客户程序员可以调用这个方法而且不用编写处理异常的代码。那么，一旦出现异常，那么这个异常就没有合适的异常控制器来解决。<BR>3.1.3&nbsp;为什么抛出的异常一定是已检查异常？<BR>RuntimeException与Error可以在任何代码中产生，它们不需要由程序员显示的抛出，一旦出现错误，那么相应的异常会被自动抛出。而已检查异常是由程序员抛出的，这分为两种情况：客户程序员调用会抛出异常的库函数（库函数的异常由库程序员抛出）；客户程序员自己使用throw语句抛出异常。遇到Error，程序员一般是无能为力的；遇到RuntimeException，那么一定是程序存在逻辑错误，要对程序进行修改（相当于调试的一种方法）；只有已检查异常才是程序员所关心的，程序应该且仅应该抛出或处理已检查异常。<BR>3.1.4&nbsp;注意：覆盖父类某方法的子类方法不能抛出比父类方法更多的异常，所以，有时设计父类的方法时会声明抛出异常，但实际的实现方法的代码却并不抛出异常，这样做的目的就是为了方便子类方法覆盖父类方法时可以抛出异常。<BR>3.2&nbsp;如何抛出异常<BR>3.2.1&nbsp;语法：throw（略）<BR>3.2.2&nbsp;抛出什么异常？<BR>对于一个异常对象，真正有用的信息时异常的对象类型，而异常对象本身毫无意义。比如一个异常对象的类型是ClassCastException，那么这个类名就是唯一有用的信息。所以，在选择抛出什么异常时，最关键的就是选择异常的类名能够明确说明异常情况的类。<BR>3.2.3&nbsp;异常对象通常有两种构造函数：一种是无参数的构造函数；另一种是带一个字符串的构造函数，这个字符串将作为这个异常对象除了类型名以外的额外说明。<BR>3.2.4&nbsp;创建自己的异常：当Java内置的异常都不能明确的说明异常情况的时候，需要创建自己的异常。需要注意的是，唯一有用的就是类型名这个信息，所以不要在异常类的设计上花费精力。<BR>3.3&nbsp;捕获异常<BR>如果一个异常没有被处理，那么，对于一个非图形界面的程序而言，该程序会被中止并输出异常信息；对于一个图形界面程序，也会输出异常的信息，但是程序并不中止，而是返回用户界面处理循环中。<BR>3.3.1&nbsp;语法：try、catch和finally（略）<BR>控制器模块必须紧接在try块后面。若掷出一个异常，异常控制机制会搜寻参数与异常类型相符的第一个控制器随后它会进入那个catch 从句，并认为异常已得到控制。一旦catch 从句结束对控制器的搜索也会停止。<BR>3.3.1.1&nbsp;捕获多个异常（注意语法与捕获的顺序）（略）<BR>3.3.1.2&nbsp;finally的用法与异常处理流程（略）<BR>3.3.2&nbsp;异常处理做什么？<BR>对于Java来说，由于有了垃圾收集，所以异常处理并不需要回收内存。但是依然有一些资源需要程序员来收集，比如文件、网络连接和图片等资源。<BR>3.3.3&nbsp;应该声明方法抛出异常还是在方法中捕获异常？<BR>原则：捕捉并处理哪些知道如何处理的异常，而传递哪些不知道如何处理的异常<BR>3.3.4&nbsp;再次抛出异常<BR>3.3.4.1&nbsp;为什么要再次抛出异常？<BR>在本级中，只能处理一部分内容，有些处理需要在更高一级的环境中完成，所以应该再次抛出异常。这样可以使每级的异常处理器处理它能够处理的异常。<BR>3.3.4.2&nbsp;异常处理流程<BR>对应与同一try块的catch块将被忽略，抛出的异常将进入更高的一级。</P>
<P>4&nbsp;关于异常的其他问题<BR>4.1&nbsp;过度使用异常<BR>首先，使用异常很方便，所以程序员一般不再愿意编写处理错误的代码，而仅仅是简简单单的抛出一个异常。这样做是不对的，对于完全已知的错误，应该编写处理这种错误的代码，增加程序的鲁棒性。另外，异常机制的效率很差。<BR>4.2&nbsp;将异常与普通错误区分开<BR>对于普通的完全一致的错误，应该编写处理这种错误的代码，增加程序的鲁棒性。只有外部的不能确定和预知的运行时错误才需要使用异常。<BR>4.3&nbsp;异常对象中包含的信息<BR>一般情况下，异常对象唯一有用的信息就是类型信息。但使用异常带字符串的构造函数时，这个字符串还可以作为额外的信息。调用异常对象的getMessage()、toString()或者printStackTrace()方法可以分别得到异常对象的额外信息、类名和调用堆栈的信息。并且后一种包含的信息是前一种的超集。</P><img src ="http://www.blogjava.net/fjq639/aggbug/25087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-22 15:42 <a href="http://www.blogjava.net/fjq639/archive/2005/12/22/25087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中不同数据库连接字符串</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24974.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 09:42:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24974.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24974.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24974.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24974.html</trackback:ping><description><![CDATA[一.连接oracle数据库<BR>&nbsp;&nbsp;&nbsp; Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();&nbsp;<BR>&nbsp;&nbsp;&nbsp; String url="jdbc:oracle:thin:@服务器ip:1521:数据库名称";<BR>&nbsp;&nbsp;&nbsp; Connection conn=DriverManager.getConnection(url,"用户名","密码");<BR><BR>二.连接mssql数据库<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 连接驱动文件jtds-0.6.jar<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; String url="jdbc:jtds:sqlserver://localhost:1433/‘数据库名´"； <BR>&nbsp;&nbsp;&nbsp;&nbsp; 自带驱动:<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();<BR>&nbsp;&nbsp;&nbsp;&nbsp; String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs"; <BR><BR>三.连接DB2数据库<BR>&nbsp;&nbsp;&nbsp;&nbsp; Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance();&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; String url="jdbc:db2://localhost:5000/数据库名称";<BR>四、jsp连接Informix数据库<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class.forName("com.informix.jdbc.IfxDriver").newInstance(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String url = "jdbc:informix-sqli://123.45.67.89:1533/testDB:INFORMIXSERVER=myserver; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user=testuser;password=testpassword"; <BR>&nbsp;&nbsp;&nbsp;&nbsp; //testDB为你的数据库名 <BR>&nbsp;&nbsp;&nbsp;&nbsp; Connection conn= DriverManager.getConnection(url); <BR>&nbsp;&nbsp;&nbsp;&nbsp; Statement stmt=conn.createStatement <BR><BR>五、jsp连接<A href="http://dev.21tx.com/database/sybase/" target=_blank><FONT color=#3366cc>Sybase</FONT></A>数据库 <BR><BR>&nbsp;&nbsp;&nbsp; Class.forName("com.sybase.jdbc.SybDriver").newInstance(); <BR>&nbsp;&nbsp; String url =" jdbc:sybase:Tds:localhost:5007/tsdata"; <BR>&nbsp;&nbsp; //tsdata为你的数据库名 <BR>&nbsp;&nbsp; Properties sysProps = System.getProperties(); <BR>&nbsp;&nbsp; SysProps.put("user","userid"); <BR>&nbsp;&nbsp; SysProps.put("password","user_password"); <BR>&nbsp;&nbsp; Connection conn= DriverManager.getConnection(url, SysProps); <BR><BR><BR>六、jsp连接MySQL数据库&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName("org.gjt.mm.mysql.Driver").newInstance(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String url ="jdbc:mysql://localhost/softforum? <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user=soft&amp;password=soft1234&amp;useUnicode=true&amp;characterEncoding=8859_1" <BR>&nbsp;&nbsp;&nbsp; //testDB为你的数据库名 <BR>&nbsp;&nbsp;&nbsp;&nbsp; Connection conn= DriverManager.getConnection(url); <BR><BR>未完待续....<BR><BR><BR><BR><img src ="http://www.blogjava.net/fjq639/aggbug/24974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 17:42 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM内存测试</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24965.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24965.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24965.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24965.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24965.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24965.html</trackback:ping><description><![CDATA[<P><BR><A href="/Files/fjq639/TestJvm.rar">例子下载<BR></A>默认的java虚拟机的大小比较小，在对大数据进行处理时java就会报错：java.lang.OutOfMemoryError。</P>
<P>设置jvm内存的方法，对于单独的.class，可以用下面的方法对Test运行时的jvm内存进行设置。<BR>java -Xms64m -Xmx256m Test<BR></P><BR>
<P>默认的java虚拟机的大小比较小，在对大数据进行处理时java就会报错：java.lang.OutOfMemoryError。</P>
<P>设置jvm内存的方法，对于单独的.class，可以用下面的方法对Test运行时的jvm内存进行设置。<BR>java -Xms64m -Xmx256m Test<BR>-Xms是设置内存初始化的大小<BR>-Xmx是设置最大能够使用内存的大小（最好不要超过物理内存大小）</P>
<P>在weblogic中，可以在startweblogic.cmd中对每个domain虚拟内存的大小进行设置，默认的设置是在commEnv.cmd里面。</P><img src ="http://www.blogjava.net/fjq639/aggbug/24965.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 16:26 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24965.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在 Java 的注释上做文章(转)</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24928.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 04:15:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24928.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24928.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24928.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24928.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24928.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top width="57%">
<P align=left><B>目录</B> </P>
<P align=left>　　前言<BR>　　 一<A href="http://outinn.diy.myrice.com/book/javadoc/#t10">. </A>Java 文档和 javadoc<BR>　　 二. 文档注释的格式<BR>　　　　 1. 文档注释的格式化<BR>　　　　 2. 文档注释的三部分 <BR>　　 三. 使用 javadoc 标记<BR>　　　　 http://outinn.diy.myrice.com/book/javadoc/#t31<BR>　　　　 http://outinn.diy.myrice.com/book/javadoc/#t32<BR>　　　　 http://outinn.diy.myrice.com/book/javadoc/#t33<BR>　 四. javadoc 命令<BR></P></TD></TR>
<TR>
<TD colSpan=2 height=44>
<P></P>
<P><BR><B><A name=t00></A>前言</B> 
<P>　　Java 的语法与 C++ 及为相似，那么，你知道 Java 的注释有几种吗？是两种？ 
<P>　　<FONT color=#008000>//</FONT> 注释一行<BR>　　 <FONT color=#008000>/* ...... */</FONT> 注释若干行</P>
<P>　　不完全对，除了以上两种之外，还有第三种，文档注释：</P>
<P>　　<FONT color=#008000>/** ...... */</FONT> 注释若干行，并写入 javadoc 文档</P>
<P>　　通常这种注释的多行写法如下：</P>
<P>　　<FONT color=#008000>/**<BR>　　 * .........<BR>　　 * .........<BR>　　 */</FONT></P>
<P>　　暂停，暂停！这第三种注释有什么用？javadoc 又是什么东西？</P>
<P>　　好，那就让我告诉你—— </P>
<P><BR><B><A name=t10></A>一. Java 文档和 javadoc</B></P>
<P>　　Java 程序员都应该知道使用 JDK 开发，最好的帮助信息就来自 SUN 发布的 Java 文档。它分包、分类详细的提供了各方法、属性的帮助信息，具有详细的类树信息、索引信息等，并提供了许多相关类之间的关系，如继承、实现接口、引用等。</P>
<P>　　Java 文档全是由一些 html 文件组织起来的，在 SUM 的站点上可以下载它们的压缩包。但是你肯定想不到，这些文档我们可以自己生成。——就此打住，再吊一次胃口。</P>
<P>　　安装了 JDK 之后，安装目录下有一个 src.jar 文件或者 src.zip 文件，它们都是以 ZIP 格式压缩的，可以使用 WinZip 解压。解压之后，我们就可以看到分目录放的全是 .java 文件。是了，这些就是 Java 运行类的源码了，非常完整，连注释都写得一清二楚……不过，怎么看这些注释都有点似曾相识的感觉？</P>
<P>　　这就不奇怪了，我们的迷底也快要揭开了。如果你仔细对比一下 .java 源文件中的文档注释 (<FONT color=#008000>/** ... */</FONT>) 和 Java 文档的内容，你会发现它们就是一样的。Java 文档只是还在格式和排版上下了些功夫。再仔细一点，你会发现 .java 源文件中的注释还带有 HTML 标识，如 &lt;B&gt;、&lt;BR&gt;、&lt;Code&gt; 等，在 Java 文档中，该出现这些标识的地方，已经按标识的的定义进行了排版。</P>
<P>　　终于真像大白了，原来 Java 文档是来自这些注释。难怪这些注释叫做文档注释呢！不过，是什么工具把这些注释变成文档的呢？</P>
<P>　　是该请出 javadoc 的时候了。在 JDK 的 bin 目录下你可以找到 javadoc，如果是 Windows 下的 JDK，它的文件名为 javadoc.exe。使用 javdoc 编译 .java 源文件时，它会读出 .java 源文件中的文档注释，并按照一定的规则与 Java 源程序一起进行编译，生成文档。</P>
<P>　　介绍 javadoc 的编译命令之前，还是先了解一下文档注释的格式吧。不过为了能够编译下面提到的若干例子，这里先介绍一条 javadoc 命令：</P>
<P>　　<FONT color=#0000cc>javadoc -d 文档存放目录 -author -version 源文件名.java</FONT></P>
<P>　　这条命令编译一个名为 “源文件名.java”的 java 源文件，并将生成的文档存放在“文档存放目录”指定的目录下，生成的文档中 index.html 就是文档的首页。-author 和 -version 两个选项可以省略。</P>
<P><BR><B><A name=t20></A>二. 文档注释的格式</B></P>
<P>　　文档注释可以用于对类、属性、方法等进行说明。写文档注释时除了需要使用 /** .... */ 限定之外，还需要注意注释内部的一些细节问题。</P>
<P>　　<A name=t21></A>1. 文档和文档注释的格式化</P>
<P>　　生成的文档是 HTML 格式，而这些 HTML 格式的标识符并不是 javadoc 加的，而是我们在写注释的时候写上去的。比如，需要换行时，不是敲入一个回车符，而是写入 &lt;br&gt;，如果要分段，就应该在段前写入 &lt;p&gt;。</P>
<P>　　因此，格式化文档，就是在文档注释中添加相应的 HTML 标识。</P>
<P>　　文档注释的正文并不是直接复制到输出文件 (文档的 HTML 文件)，而是读取每一行后，删掉前导的 * 号及 * 号以前的空格，再输入到文档的。如</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><FONT color=#008000>/**<BR>* This is first line. &lt;br&gt;<BR>***** This is second line. &lt;br&gt;<BR>This is third line.<BR>*/</FONT></TD></TR></TBODY></TABLE>
<P>　　编译输出后的 HTML 源码则是</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD>This is first line. &lt;br&gt;<BR>This is second line. &lt;br&gt;<BR>This is third line.</TD></TR></TBODY></TABLE>
<P>　　前导的 * 号允许连续使用多个，其效果和使用一个 * 号一样，但多个 * 号前不能有其它字符分隔，否则分隔符及后面的 * 号都将作为文档的内容。* 号在这里是作为左边界使用，如上例的第一行和第二行；如果没有前导的 * 号，则边界从第一个有效字符开始，而不包括前面的空格，如上例第三行。</P>
<P>　　还有一点需要说明，文档注释只说明紧接其后的类、属性或者方法。如下例：</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE><FONT color=#008000>/** comment for class */</FONT>
public class Test {

    <FONT color=#008000>/** comment for a attribute */</FONT>
    int number;

    <FONT color=#008000>/** comment for a method */</FONT>
    public void myMethod() { ...... }

    ......
}</PRE></TD></TR></TBODY></TABLE>
<P>　　上例中的三处注释就是分别对类、属性和方法的文档注释。它们生成的文档分别是说明紧接其后的类、属性、方法的。“紧接”二字尤其重要，如果忽略了这一点，就很可能造成生成的文档错误。如</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE>import java.lang.*;

<FONT color=#008000>/** commnet for class */</FONT>

public class Test { ...... }

<FONT color=#008000>// 此例为正确的例子</FONT></PRE></TD></TR></TBODY></TABLE>
<P>　　这个文档注释将生成正确的文档。但只需要改变其中两行的位置，变成下例，就会出错：</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE><FONT color=#008000>/** commnet for class */</FONT>

import java.lang.*;

public class Test { ...... }

<FONT color=#008000>// 此例为错误的例子</FONT></PRE></TD></TR></TBODY></TABLE>
<P>　　这个例子只把上例的 import 语句和文档注释部分交换了位置，结果却大不相同——生成的文档中根本就找不到上述注释的内容了。原因何在？</P>
<P>　　“<FONT color=#008000>/** commnet for class */</FONT>”是对 class Test 的说明，把它放在“public class Test { ...... }”之前时，其后紧接着 class Test，符合规则，所以生成的文档正确。但是把它和“import java.lang.*;”调换了位置后，其后紧接的就是不 class Test 了，而是一个 import 语句。由于文档注释只能说明类、属性和方法，import 语句不在此列，所以这个文档注释就被当作错误说明省略掉了。</P>
<P>　　<A name=t22></A>2. 文档注释的三部分</P>
<P>　　根据在文档中显示的效果，文档注释分为三部分。先举例如下，以便说明。</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE><FONT color=#008000>/**
 * show 方法的简述.
 * &lt;p&gt;show 方法的详细说明第一行&lt;br&gt;
 * show 方法的详细说明第二行
 * @param b true 表示显示，false 表示隐藏
 * @return 没有返回值
 */</FONT>
public void show(boolean b) {
    frame.show(b);
}</PRE></TD></TR></TBODY></TABLE>
<P>　　第一部分是简述。文档中，对于属性和方法都是先有一个列表，然后才在后面一个一个的详细的说明。列表中属性名或者方法名后面那段说明就是简述。如下图中被红框框选的部分：</P>
<P align=center><IMG height=125 src="http://www.java3z.com/cwbwebhome/article/article2/images/001.gif" width=341></P>
<P>　　简述部分写在一段文档注释的最前面，第一个点号 (.) 之前 (包括点号)。换句话说，就是用第一个点号分隔文档注释，之前是简述，之后是第二部分和第三部分。如上例中的 “* show 方法的简述.”。</P>
<P>　　有时，即使正确地以一个点号作为分隔，javadoc 仍然会出错，把点号后面的部分也做为了第一部分。为了解决这个问题，我们可以使用一个 &lt;p&gt; 标志将第二分部分开为下一段，如上例的“* &lt;p&gt;show 方法的详细说明第一行 ....”。除此之外，我们也可以使用 &lt;br&gt; 来分隔。</P>
<P>　　第二部分是详细说明部分。该部分对属性或者方法进行详细的说明，在格式上没有什么特殊的要求，可以包含若干个点号。它在文档中的位置如下图所示：</P>
<P align=center><IMG height=286 src="http://www.java3z.com/cwbwebhome/article/article2/images/002.gif" width=382> </P>
<P>　　这部分文档在上例中相应的代码是：</P>
<P><FONT color=#008000>　　* show 方法的简述.<BR>　　 * &lt;p&gt;show 方法的详细说明第一行&lt;br&gt;<BR>　　 * show 方法的详细说明第二行</FONT></P>
<P>　　发现什么了？对了，简述也在其中。这一点要记住了，不要画蛇添足——在详细说明部分中再写一次简述哦！</P>
<P>　　第三部分是特殊说明部分。这部分包括版本说明、参数说明、返回值说明等。它在文档中的位置：</P>
<P align=center><IMG height=286 src="http://www.java3z.com/cwbwebhome/article/article2/images/003.gif" width=382></P>
<P>　　第三部分在上例中相应的代码是 </P>
<P><FONT color=#008000>　　* @param b true 表示显示，false 表示隐藏<BR>　　 * @return 没有返回值</FONT></P>
<P>　　除了 @param 和 @return 之外，还有其它的一些特殊标记，分别用于对类、属性和方法的说明……不要推我，我马上就说。</P>
<P><BR><B><A name=t30></A>三. 使用 javadoc 标记</B></P>
<P>　　javadoc 标记是插入文档注释中的特殊标记，它们用于标识代码中的特殊引用。javadoc 标记由“@”及其后所跟的标记类型和专用注释引用组成。记住了，三个部分——@、标记类型、专用注释引用。不过我宁愿把它分成两部分：@ 和标记类型、专用注释引用。虽然 @ 和 标记类型之间有时可以用空格符分隔，但是我宁愿始终将它们紧挨着写，以减少出错机会。</P>
<P>　　javadoc 标记有如下一些：</P>
<TABLE borderColor=#000000 cellSpacing=1 cellPadding=1 align=center border=1>
<TBODY>
<TR>
<TD borderColor=#ffffee>
<TABLE border=0>
<TBODY>
<TR align=middle bgColor=#eeeeee>
<TD><B>标记</B></TD>
<TD><B>用于</B></TD>
<TD><B>作用</B></TD></TR>
<TR>
<TD><B>@author</B></TD>
<TD>对类的说明</TD>
<TD>标明开发该类模块的作者</TD></TR>
<TR>
<TD><B>@version</B></TD>
<TD>对类的说明</TD>
<TD>标明该类模块的版本</TD></TR>
<TR>
<TD><B>@see</B></TD>
<TD>对类、属性、方法的说明</TD>
<TD>参考转向，也就是相关主题</TD></TR>
<TR>
<TD><B>@param</B></TD>
<TD>对方法的说明</TD>
<TD>对方法中某参数的说明</TD></TR>
<TR>
<TD><B>@return</B></TD>
<TD>对方法的说明</TD>
<TD>对方法返回值的说明</TD></TR>
<TR>
<TD><B>@exception</B></TD>
<TD>对方法的说明</TD>
<TD>对方法可能抛出的异常进行说明</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>　　下面详细说明各标记。</P>
<P>　　<A name=t31></A>1. @see 的使用</P>
<P>　　@see 的句法有三种： </P>
<P><FONT color=#000099>　　@see 类名<BR>　　 @see #方法名或属性名<BR>　　 @see 类名#方法名或属性名</FONT></P>
<P>　　类名，可以根据需要只写出类名 (如 String) 或者写出类全名 (如 java.lang.String)。那么什么时候只需要写出类名，什么时候需要写出类全名呢？</P>
<P>　　如果 java 源文件中的 import 语句包含了的类，可以只写出类名，如果没有包含，则需要写出类全名。java.lang 也已经默认被包含了。这和 javac 编译 java 源文件时的规定一样，所以可以简单的用 javac 编译来判断，源程序中 javac 能找到的类，javadoc 也一定能找到；javac 找不到的类，javadoc 也找不到，这就需要使用类全名了。</P>
<P>　　方法名或者属性名，如果是属性名，则只需要写出属性名即可；如果是方法名，则需要写出方法名以及参数类型，没有参数的方法，需要写出一对括号。如</P>
<TABLE cellSpacing=3 cellPadding=0 align=center border=0>
<TBODY>
<TR align=middle bgColor=#eeeeee>
<TD><B>成员类型</B></TD>
<TD><B>成员名称及参数</B></TD>
<TD><B>@see 句法</B></TD></TR>
<TR>
<TD align=middle>属性</TD>
<TD>number</TD>
<TD>@see number</TD></TR>
<TR>
<TD align=middle>属性</TD>
<TD>count</TD>
<TD>@see count</TD></TR>
<TR>
<TD align=middle>方法</TD>
<TD>count()</TD>
<TD>@see count()</TD></TR>
<TR>
<TD align=middle>方法</TD>
<TD>show(boolean b)</TD>
<TD>@see show(boolean)</TD></TR>
<TR>
<TD align=middle>方法</TD>
<TD>main(String[] args)</TD>
<TD>@see main(String[])</TD></TR></TBODY></TABLE>
<P>　　有时也可以偷懒：假如上例中，没有 count 这一属性，那么参考方法 count() 就可以简写成 @see count。不过，为了安全起见，还是写全 @see count() 比较好。</P>
<P>　　@see 的第二个句法和第三个句法都是转向方法或者属性的参考，它们有什么区别呢？</P>
<P>　　第二个句法中没有指出类名，则默认为当前类。所以它定义的参考，都转向本类中的属性或者方法。而第三个句法中指出了类名，则还可以转向其它类的属性或者方法。</P>
<P>　　关于 @see 标记，我们举个例说明。由于 @see 在对类说明、对属性说明、对方法说明时用法都一样，所以这里只以对类说明为例。</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE><FONT color=#008000>/**
 * @see String
 * @see java.lang.StringBuffer
 * @see #str
 * @see #str()
 * @see #main(String[])
 * @see Object#toString()
 */</FONT>
public class TestJavaDoc {

}</PRE></TD></TR></TBODY></TABLE>
<P>　　生成的文档的相关部分如下图：</P>
<P align=center><IMG height=98 src="http://www.java3z.com/cwbwebhome/article/article2/images/004.gif" width=526></P>
<P>　　String 和 StringBuffer 都是在 java.lang 包中，由于这个包是默认导入了的，所以这两个类可以直接写类名，也可以写类全名。str、str() 为同名属性和方法，所以方法名需要用 () 区分。main 是带参数的方法，所以在 () 中指明了参数类型。toString() 虽然在本类中也有 (从 Object 继承的)，但我们是想参考 Object 类的 toString() 方法，所以使用了 Object#toString()。</P>
<P>　　奇怪的是，为什么其中只有 str、str() 和 main(String[]) 变成了链接呢？那是因为编译时没有把 java.lang 包或者 Stirng、StringBuffer、Object 三个类的源文件一起加入编译，所以，生成的文档没有关于那三个类的信息，也就不可以建立链接了。后面讲解 javadoc 编译命令的时候还会详细说明。</P>
<P>　　上例中如果去把类中的 str 属性去掉，那么生成的文档又会有什么变化呢？你会发现，原来是 str, str()，而现在变成了 str(), str()，因为 str 属性已经没有了，所以 str 也表示方法 str()。</P>
<P>　　<A name=t32></A>2. 使用 @author、@version 说明类</P>
<P>　　这两个标记分别用于指明类的作者和版本。缺省情况下 javadoc 将其忽略，但命令行开关 -author 和 -version 可以修改这个功能，使其包含的信息被输出。这两个标记的句法如下：</P>
<P><FONT color=#000099>　　@author 作者名<BR>　　 @version 版本号</FONT></P>
<P>　　其中，@author 可以多次使用，以指明多个作者，生成的文档中每个作者之间使用逗号 (,) 隔开。@version 也可以使用多次，只有第一次有效，生成的文档中只会显示第一次使用 @version 指明的版本号。如下例</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE><FONT color=#008000>/**
 * @author Fancy
 * @author Bird
 * @version Version 1.00
 * @version Version 2.00
 */</FONT>
public class TestJavaDoc {

}</PRE></TD></TR></TBODY></TABLE>
<P>　　生成文档的相关部分如图：</P>
<P align=center><IMG height=146 src="http://www.java3z.com/cwbwebhome/article/article2/images/005.gif" width=286></P>
<P>　　从生成文档的图示中可以看出，两个 @author 语句都被编译，在文档中生成了作者列表。而两个 @version 语句中只有第一句被编译了，只生成了一个版本号。</P>
<P>　　从图上看，作者列表是以逗号分隔的，如果我想分行显示怎么办？另外，如果我想显示两个以上的版本号又该怎么办？</P>
<P>　　——我们可以将上述两条 @author 语句合为一句，把两个 @version 语句也合为一句：</P>
<P><FONT color=#008000>　　@author Fancy&lt;br&gt;Bird<BR>　　 @version Version 1.00&lt;br&gt;Version 2.00</FONT></P>
<P>　　结果如图：</P>
<P align=center><IMG height=119 src="http://www.java3z.com/cwbwebhome/article/article2/images/006.gif" width=188></P>
<P>　　我们这样做即达到了目的，又没有破坏规则。@author 之后的作者名和 @version 之后的版本号都可以是用户自己定义的任何 HTML 格式，所以我们可以使用 &lt;br&gt; 标记将其分行显示。同时，在一个 @version 中指明两个用 &lt;br&gt; 分隔的版本号，也没有破坏只显示第一个 @version 内容的规则。</P>
<P>　　<A name=t33></A>3. 使用 @param、@return 和 @exception 说明方法</P>
<P>　　这三个标记都是只用于方法的。@param 描述方法的参数，@return 描述方法的返回值，@exception 描述方法可能抛出的异常。它们的句法如下：</P>
<P><FONT color=#000099>　　@param 参数名 参数说明<BR>　　 @return 返回值说明<BR>　　 @exception 异常类名 说明</FONT></P>
<P>　　每一个 @param 只能描述方法的一个参数，所以，如果方法需要多个参数，就需要多次使用 @param 来描述。</P>
<P>　　一个方法中只能用一个 @return，如果文档说明中列了多个 @return，则 javadoc 编译时会发出警告，且只有第一个 @return 在生成的文档中有效。</P>
<P>　　方法可能抛出的异常应当用 @exception 描述。由于一个方法可能抛出多个异常，所以可以有多个 @exception。每个 @exception 后面应有简述的异常类名，说明中应指出抛出异常的原因。需要注意的是，异常类名应该根据源文件的 import 语句确定是写出类名还是类全名。 　　示例如下：</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD><PRE>public class TestJavaDoc {
<FONT color=#008000>
    /**
     * @param n a switch
     * @param b excrescent parameter
     * @return true or false
     * @return excrescent return
     * @exception java.lang.Exception throw when switch is 1
     * @exception NullPointerException throw when parameter n is null
     */</FONT>
    public boolean fun(Integer n) throws Exception {
        switch (n.intValue()) {
        case 0:
            break;
        case 1:
            throw new Exception("Test Only");
        default:
            return false;
        }
        return true;
    }

}</PRE></TD></TR></TBODY></TABLE>
<P>　　使用 javadoc 编译生成的文档相关部分如下图：</P>
<P align=center><IMG height=294 src="http://www.java3z.com/cwbwebhome/article/article2/images/007.gif" width=496></P>
<P>　　可以看到，上例中 @param b excrescent parameter 一句是多余的，因为参数只是一个 n，并没有一个 b?５?是 javadoc 编译时并没有检查。因此，写文档注释时一定要正确匹配参数表与方法中正式参数表的项目。如果方法参数表中的参数是 a，文档中却给出对参数 x 的解释，或者再多出一个参数 i，就会让人摸不着头脑了。@exceptin 也是一样。</P>
<P>　　上例程序中并没有抛出一个 NullPointerException，但是文档注释中为什么要写上这么一句呢，难道又是为了演示？这不是为了演示描述多余的异常也能通过编译，而是为了说明写异常说明时应考运行时 (RunTime) 异常的可能性。上例程序中，如果参数 n 是给的一个空值 (null)，那么程序会在运行的时候抛出一个 NullPointerException，因此，在文档注释中添加了对 NullPointerException 的说明。</P>
<P>　　上例中的 @return 语句有两个，但是根据规则，同一个方法中，只有第一个 @return 有效，其余的会被 javadoc 忽略。所以生成的文档中没有出现第二个 @return 的描述。</P>
<P>　　讲到这里，该怎么写文档注释你应该已经清楚了，下面就开始讲解 javadoc 的常用命令。</P>
<P><BR><B><A name=t40></A>四. javadoc 命令</B></P>
<P>　　运行 javadoc -help 可以看到 javadoc 的用法，这里列举常用参数如下：</P>
<P>用法：<BR>　　 <FONT color=#000099>javadoc [options] [packagenames] [sourcefiles]</FONT></P>
<P>选项：</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD width=30>&nbsp;</TD>
<TD width=130>-public</TD>
<TD>仅显示 public 类和成员 </TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-protected</TD>
<TD>显示 protected/public 类和成员 (缺省)</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-package</TD>
<TD>显示 package/protected/public 类和成员</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-private</TD>
<TD>显示所有类和成员</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-d &lt;directory&gt;</TD>
<TD>输出文件的目标目录</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-version</TD>
<TD>包含 @version 段</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-author</TD>
<TD>包含 @author 段</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-splitindex</TD>
<TD>将索引分为每个字母对应一个文件</TD></TR>
<TR>
<TD width=30>&nbsp;</TD>
<TD>-windowtitle &lt;text&gt;</TD>
<TD>文档的浏览器窗口标题</TD></TR></TBODY></TABLE>
<P>　　javadoc 编译文档时可以给定包列表，也可以给出源程序文件列表。例如在 CLASSPATH 下有两个包若干类如下：</P>
<P>　　fancy.Editor<BR>　　 fancy.Test<BR>　　 fancy.editor.ECommand<BR>　　 fancy.editor.EDocument<BR>　　 fancy.editor.EView</P>
<P>　　这里有两个包 (fancy 和 fancy.editor) 和 5 个类。那么编译时 (Windows 环境) 可以使用如下 javadoc 命令：</P>
<P>　　<FONT color=#000099>javadoc fancy\Test.java fancy\Editor.java fancy\editor\ECommand.java fancy\editor\EDocument.java fancy\editor\EView.java</FONT></P>
<P>　　这是给出 java 源文件作为编译参数的方法，注意命令中指出的是文件路径，应该根据实际情况改变。也可以是给出包名作为编译参数，如：</P>
<P>　　<FONT color=#000099>javadoc fancy fancy.editor</FONT></P>
<P>　　用浏览器打开生成文档的 index.html 文件即可发现两种方式编译结果的不同，如下图：</P>
<P align=center><IMG height=295 src="http://www.java3z.com/cwbwebhome/article/article2/images/008.gif" width=407></P>
<P>　　用第二条命令生成的文档被框架分成了三部分：包列表、类列表和类说明。在包列表中选择了某个包之后，类列表中就会列出该包中的所有类；在类列表中选择了某个类之后，类说明部分就会显示出该类的详细文档。而用第一条命令生成的文档只有两部分，类列表和类说明，没有包列表。这就是两种方式生成文档的最大区别了。</P>
<P>　　下面再来细说选项。</P>
<P>　　-public、-protected、-package、-private 四个选项，只需要任选其一即可。它们指定的显示类成员的程度。它们显示的成员多少是一个包含的关系，如下表：</P>
<TABLE borderColor=#000000 cellSpacing=3 align=center border=1>
<TBODY>
<TR>
<TD borderColor=#ffffee height=22>-private (显示所有类和成员)</TD></TR>
<TR>
<TD borderColor=#ffffee>
<TABLE borderColor=#000000 cellSpacing=3 border=1>
<TBODY>
<TR>
<TD borderColor=#ffffee height=22>-package (显示 package/protected/public 类和成员)</TD></TR>
<TR>
<TD borderColor=#ffffee>
<TABLE borderColor=#000000 cellSpacing=3 border=1>
<TBODY>
<TR>
<TD borderColor=#ffffee height=22>-protected (显示 protected/public 类和成员)</TD></TR>
<TR>
<TD borderColor=#ffffee height=22>
<TABLE borderColor=#000000 border=1>
<TBODY>
<TR>
<TD borderColor=#ffffee>-public (仅显示 public 类和成员)</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>　　-d 选项允许你定义输出目录。如果不用 -d 定义输出目录，生成的文档文件会放在当前目录下。-d 选项的用法是</P>
<P>　　<FONT color=#000099>-d 目录名</FONT></P>
<P>　　目录名为必填项，也就是说，如果你使用了 -d 参数，就一定要为它指定一个目录。这个目录必须已经存在了，如果还不存在，请在运行 javadoc 之前创建该目录。</P>
<P>　　-version 和 -author 用于控制生成文档时是否生成 @version 和 @author 指定的内容。不加这两个参数的情况下，生成的文档中不包含版本和作者信息。</P>
<P>　　-splitindex 选项将索引分为每个字母对应一个文件。默认情况下，索引文件只有一个，且该文件中包含所有索引内容。当然生成文档内容不多的时候，这样做非常合适，但是，如果文档内容非常多的时候，这个索引文件将包含非常多的内容，显得过于庞大。使用 -splitindex 会把索引文件按各索引项的第一个字母进行分类，每个字母对应一个文件。这样，就减轻了一个索引文件的负担。</P>
<P>　　-windowtitle 选项为文档指定一个标题，该标题会显示在窗口的标题栏上。如果不指定该标题，而默认的文档标题为“生成的文档（无标题）”。该选项的用法是：</P>
<P>　　<FONT color=#000099>-windowtitle 标题</FONT></P>
<P>　　标题是一串没有包含空格的文本，因为空格符是用于分隔各参数的，所以不能包含空格。同 -d 类似，如果指定了 -windowtitle 选项，则必须指定标题文本。</P>
<P>　　到此为止，Java 文档和 javadoc 就介绍完了。javadoc 真的能让我们在 Java 注释上做文章——生成开发文档。</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/fjq639/aggbug/24928.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 12:15 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24928.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中静态方法和非静态方法覆盖的区别</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24924.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 03:16:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24924.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24924.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24924.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24924.html</trackback:ping><description><![CDATA[<P>　 <SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>首先我们提供两个类，基类为</FONT>Parent<FONT face=宋体>，派生类为</FONT>Child<FONT face=宋体>。在</FONT>Parent<FONT face=宋体>中我们提供两个方法，一个是静态方法</FONT>staticMethod(),<FONT face=宋体>一个是非静态方法</FONT>nonStaticMethod()<FONT face=宋体>。在</FONT>Child</SPAN></P><SPAN style="FONT-SIZE: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial"><FONT face=宋体><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">类中我们覆盖着两个方法。 </SPAN></FONT></SPAN>
<P></P>
<P></P>
<P>class Parent{ 
<P></P>
<P></P>
<P>&nbsp;public void nonStaticMethod()&nbsp;{ 
<P></P>
<P></P>
<P><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;System.out.println("Parent's Non-Static Method is Called"); </SPAN></SPAN>
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;&nbsp;}&nbsp;&nbsp; 
<P></P>
<P></P>
<P><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void staticMethod()&nbsp;{&nbsp; </SPAN></SPAN>
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;System.out.println("parent's static method is called");&nbsp;&nbsp; 
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">} 
<P></P>
<P></P>
<P>} 
<P></P>
<P></P>
<P>class Child extends Parent{ 
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;public void nonStaticMethod()&nbsp;{&nbsp; 
<P></P>
<P></P>
<P style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt">System.out.println("child's non-static method is called");&nbsp; 
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;}&nbsp; 
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;public static void staticMethod()&nbsp;{ 
<P></P>
<P><SPAN style="mso-tab-count: 2"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("child's static method is called");&nbsp; </SPAN></SPAN>
<P></P>
<P></P>
<P><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; </SPAN></SPAN>
<P></P>
<P></P>
<P>} 
<P></P>
<P></P>
<P><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>在</FONT>Test<FONT face=宋体>类中我们分别使用</FONT>Parent p1 = new Parent(),Parent p2 = new Child(),Child c = new Child()</SPAN> <SPAN style="FONT-SIZE: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial"><FONT face=宋体><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">得到三个实例，并分别调用静态方法和非静态方法，我们来看程序的运行结果 </SPAN></FONT></SPAN>
<P></P>
<P></P><SPAN style="FONT-SIZE: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>在</FONT>Test<FONT face=宋体>类中我们分别使用</FONT>Parent p1 = new Parent(),Parent p2 = new Child(),Child c = new Child()得到三个实例，并分别调用静态方法和非静态方法，我们来看程序的运行结果 </SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt">public class Test{ 
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String args[])&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp; 
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Parent p1 = new Parent();&nbsp;&nbsp; </SPAN></SPAN>
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Parent p2 = new Child();&nbsp;&nbsp; </SPAN></SPAN>
<P></P>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Child c = new Child(); </SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("Parent.static: "); </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-spacerun: yes"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Parent.staticMethod();&nbsp; </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("p1.static: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 42pt; TEXT-INDENT: 21pt">p1.staticMethod();&nbsp; 
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("p2.static: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 42pt; TEXT-INDENT: 21pt">p2.staticMethod(); 
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("p1.nonStatic: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt"><SPAN style="mso-spacerun: yes"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p1.nonStaticMethod(); </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("p2.nonStatic: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt"><SPAN style="mso-spacerun: yes"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p2.nonStaticMethod(); </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("Child.static: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 42pt; TEXT-INDENT: 21pt">Child.staticMethod(); 
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("c.static: "); </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-spacerun: yes"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.staticMethod();&nbsp; </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="mso-tab-count: 1"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("c.nonStatic: "); </FONT></SPAN></SPAN>
<P></P>
<P style="MARGIN-LEFT: 21pt; TEXT-INDENT: 21pt"><SPAN style="mso-spacerun: yes"><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial"><FONT face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.nonStaticMethod();&nbsp; </FONT></SPAN></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<P></P>
<P style="TEXT-INDENT: 21pt">} 
<P></P>
<P style="TEXT-INDENT: 21pt"><SPAN style="FONT-SIZE: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial"><FONT face=宋体><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">程序的运行结果为： </SPAN></FONT></SPAN>
<P></P>
<P style="TEXT-INDENT: 21pt">Parent.static: parent's static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">p1.static: parent's static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">p2.static: parent's static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">p1.nonStatic: Parent's Non-Static Method is Calle 
<P></P>
<P style="TEXT-INDENT: 21pt">p2.nonStatic: child's non-static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">Child.static: child's static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">c.static: child's static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt">c.nonStatic: child's non-static method is called 
<P></P>
<P style="TEXT-INDENT: 21pt"><FONT face=宋体><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">值得注意的是p2实际上是一个Child的类型的引用，然而在调用静态方法的时候，它执行的却是父类的静态方法，而不是Child的静态方法，而调用 p2的非静态方法的时候执行的是Child</SPAN></FONT> <SPAN style="FONT-SIZE: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial"><FONT face=宋体><SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial">的非静态方法，为什么呢？<FONT color=#ff0000>原因是静态方法是在编译的时候把静态方法和类的引用类型进行匹配，而不是在运行的时候和类引用进行匹配。因此我们得出结论：当我们在子类中创建的静态方法，它并不会覆盖父类中相同名字的静态方法。 </FONT></SPAN></FONT></SPAN></P><img src ="http://www.blogjava.net/fjq639/aggbug/24924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 11:16 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解构造器</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24920.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 02:51:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24920.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24920.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24920.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24920.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24920.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top width=413>
<P>&nbsp;要学习Java,你必须理解构造器。因为构造器可以提供许多特殊的方法，这个对于初学者经常混淆。但是，构造器和方法又有很多重要的区别。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们说构造器是一种方法，就象讲澳大利亚的鸭嘴兽是一种哺育动物。（按：老外喜欢打比喻，我也就照着翻译）。要理解鸭嘴兽，那么先必须理解它和其他哺育动物的区别。同样地，要理解构造器，那么就要了解构造器和方法的区别。所有学习java的人，尤其是对那些要认证考试的，理解构造器是非常重要的。下面将简单介绍一下 ，最后用一个表作了些简单的总结。 </P></TD></TR>
<TR>
<TD colSpan=2 height=20>
<P><BR></P></TD></TR></TBODY></TABLE>
<P>一、<FONT color=#0000ff><SPAN class=style1>功能和作用的不同</SPAN><BR></FONT>构造器是为了创建一个类的实例。这个过程也可以在创建一个对象的时候用到：Platypus p1 = new Platypus(); <BR></P>
<P>相反，方法的作用是为了执行java代码。 </P>
<P><FONT color=#0000ff><SPAN class=style1>二、修饰符，返回值和命名的不同</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;构造器和方法在下面三个方面的区别：修饰符，返回值，命名。和方法一样，构造器可以有任何访问的修饰： public, protected, private或者没有修饰（通常被package 和 friendly调用）. 不同于方法的是，构造器不能有以下非访问性质的修饰： abstract, final, native, static, 或者 synchronized。 </P>
<P>返回类型也是非常重要的。方法能返回任何类型的值或者无返回值（void），构造器没有返回值，也不需要void。 </P>
<P>最后，谈谈两者的命名。构造器使用和类相同的名字，而方法则不同。按照习惯，方法通常用小写字母开始，而构造器通常用大写字母开始。构造器通常是一个名词，因为它和类名相同；而方法通常更接近动词，因为它说明一个操作。 </P>
<P><FONT color=#0000ff><SPAN class=style1>三、"this"的用法</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;构造器和方法使用关键字this有很大的区别。方法引用this指向正在执行方法的类的实例。静态方法不能使用this关键字，因为静态方法不属于类的实例，所以this也就没有什么东西去指向。构造器的this指向同一个类中，不同参数列表的另外一个构造器，我们看看下面的代码： </P>
<P>public class Platypus { </P>
<P>String name; </P>
<P>Platypus(String input) { <BR>&nbsp;&nbsp;name = input; <BR>} </P>
<P>Platypus() { <BR>&nbsp;&nbsp;&nbsp;this("John/Mary Doe"); <BR>} </P>
<P>&nbsp;&nbsp;public static void main(String args[]) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;Platypus p1 = new Platypus("digger"); <BR>&nbsp;&nbsp;&nbsp;Platypus p2 = new Platypus(); <BR>&nbsp;&nbsp;} <BR>} <BR></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;在上面的代码中，有2个不同参数列表的构造器。第一个构造器，给类的成员name赋值，第二个构造器，调用第一个构造器，给成员变量name一个初始值 "John/Mary Doe". </P>
<P>在构造器中，如果要使用关键字this,那么，必须放在第一行，如果不这样，将导致一个编译错误。 </P>
<P><FONT color=#0000ff><SPAN class=style1>四、"super"的用法 </SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;构造器和方法，都用关键字super指向超类，但是用的方法不一样。方法用这个关键字去执行被重载的超类中的方法。看下面的例子： </P>
<P>class Mammal { <BR>&nbsp;&nbsp;&nbsp;void getBirthInfo() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("born alive."); <BR>&nbsp;&nbsp;&nbsp;} <BR>} </P>
<P>class Platypus extends Mammal { <BR>&nbsp;&nbsp;&nbsp;void getBirthInfo() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("hatch from eggs"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.print("a mammal normally is "); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super.getBirthInfo(); <BR>&nbsp;&nbsp;&nbsp;} <BR>} <BR></P>
<P>在上面的例子中，使用super.getBirthInfo()去调用超类Mammal中被重载的方法。 </P>
<P>构造器使用super去调用超类中的构造器。而且这行代码必须放在第一行，否则编译将出错。看下面的例子： </P>
<P>public class SuperClassDemo { <BR>&nbsp;&nbsp;SuperClassDemo() {} <BR>} </P>
<P>class Child extends SuperClassDemo { <BR>&nbsp;&nbsp;Child() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(); <BR>&nbsp;&nbsp;} <BR>} <BR></P>
<P>在上面这个没有什么实际意义的例子中，构造器 Child()包含了 super,它的作用就是将超类中的构造器SuperClassDemo实例化，并加到 Child类中。 </P>
<P><FONT color=#0000ff><SPAN class=style1>五、编译器自动加入代码</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译器自动加入代码到构造器，对于这个，java程序员新手可能比较混淆。当我们写一个没有构造器的类，编译的时候，编译器会自动加上一个不带参数的构造器，例如：public class Example {} <BR>编译后将如下代码： </P>
<P>public class Example { <BR>&nbsp;&nbsp;&nbsp;&nbsp;Example() {} <BR>} <BR></P>
<P>在构造器的第一行，没有使用super，那么编译器也会自动加上，例如： </P>
<P>public class TestConstructors { <BR>&nbsp;&nbsp;TestConstructors() {} <BR>} <BR></P>
<P>编译器会加上代码，如下： </P>
<P>public class TestConstructors { <BR>&nbsp;&nbsp;TestConstructors() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;super(); <BR>&nbsp;&nbsp;} <BR>} <BR></P>
<P>仔细想一下，就知道下面的代码 </P>
<P>public class Example {} <BR></P>
<P>经过会被编译器加代码形如： </P>
<P>public class Example { <BR>&nbsp;&nbsp;&nbsp;Example() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(); <BR>&nbsp;&nbsp;&nbsp;} <BR>} <BR></P>
<P><FONT color=#0000ff><SPAN class=style1>六、继承</SPAN><BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;构造器是不能被继承的。子类可以继承超类的任何方法。看看下面的代码： </P>
<P>public class Example { <BR>&nbsp;&nbsp;public void sayHi { <BR>&nbsp;&nbsp;&nbsp;&nbsp;system.out.println("Hi"); <BR>&nbsp;&nbsp;} </P>
<P>&nbsp;&nbsp;Example() {} <BR>} </P>
<P>public class SubClass extends Example { <BR>} <BR></P>
<P>类 SubClass 自动继承了父类中的sayHi方法，但是，父类中的构造器 Example()却不能被继承。 </P>
<P>总结 <BR><BR></P>
<TABLE width=648 border=1>
<TBODY>
<TR>
<TD width=90>主题</TD>
<TD width=362>构造器</TD>
<TD width=174>方法</TD></TR>
<TR>
<TD>功能</TD>
<TD>建立一个类的实例 </TD>
<TD>java功能语句</TD></TR>
<TR>
<TD>修饰</TD>
<TD>不能用bstract, final, native, static, or synchronized</TD>
<TD>能</TD></TR>
<TR>
<TD>返回类型 </TD>
<TD>没有返回值，没有void</TD>
<TD>有返回值，或者void </TD></TR>
<TR>
<TD>命名</TD>
<TD>和类名相同；通常为名词，大写开头</TD>
<TD>通常代表一个动词的意思，小写开头</TD></TR>
<TR>
<TD>this</TD>
<TD><BR>指向同一个类中另外一个构造器，在第一行 </TD>
<TD>指向当前类的一个实例，不能用于静态方法</TD></TR>
<TR>
<TD>super</TD>
<TD>调用父类的构造器，在第一行</TD>
<TD><BR>调用父类中一个重载的方法 </TD></TR>
<TR>
<TD>继承</TD>
<TD>构造器不能被继承 </TD>
<TD>方法可以被继承 </TD></TR>
<TR>
<TD><BR>编译器自动加入一个缺省的构造器</TD>
<TD>自动加入（如果没有）</TD>
<TD>不支持 </TD></TR>
<TR>
<TD>编译器自动加入一个缺省的调用到超类的构造器</TD>
<TD>自动加入（指super(),如果没有）</TD>
<TD>不支持 </TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/fjq639/aggbug/24920.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 10:51 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24920.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java类的初始化顺序</title><link>http://www.blogjava.net/fjq639/archive/2005/12/21/24904.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Wed, 21 Dec 2005 01:12:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/21/24904.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24904.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/21/24904.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24904.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24904.html</trackback:ping><description><![CDATA[<PRE>abstract class base
{
    public int age=getNumber(100);
    static
    {
        System.out.println("base static block");
    }
    {
        System.out.println("base nonstatic block");
    }
    static int sage=getNumber(50);
    base()
    {
        System.out.println(age);
        System.out.println("base start");
        draw();//会调用子类覆盖后的方法
        System.out.println("base end");
        
    }
    static int getNumber(int base)
    {
        System.out.println("base.getNumber int"+base);
        return base;
    }
    public  void draw()
    {
        System.out.println("base.draw");
    }
}
public class initializeOrder extends base{
    public int age=getNumber(1001);
    private int _radius=getNumber(10);
    static int sage=getNumber(250);
    static
    {
        System.out.println("subclass static block");
    }
    {
        System.out.println("subclass nonstatic block");
    }
    initializeOrder(int radius)
    {
        _radius=radius;
        System.out.println(age);
        System.out.println("initializeOrder initialized");
    }
    public void draw()
    {
        System.out.println("initializeOrder.draw "+_radius);
    }
    /** *//**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new initializeOrder(1000);
    }
}<BR>
 运行结果：<BR><BR>C:\java&gt;java    initializeOrder<BR>base static block<BR>base.getNumber int50<BR>base.getNumber int250<BR>subclass static block<BR>base.getNumber int100<BR>base nonstatic block<BR>100<BR>base start<BR>initializeOrder.draw 0<BR>base end<BR>base.getNumber int1001<BR>base.getNumber int10<BR>subclass nonstatic block<BR>1001<BR>initializeOrder initialized</PRE>
<P>C:\java&gt;</P>
<P>总结：<BR>1、类只有在使用New调用创建的时候才会被JAVA类装载器装入<BR>2、JAVA类首次装入时，会对静态成员变量或方法进行一次初始化,但方法不被调用是不会执行的，静态成员变量和静态初始化块级别相同，非静态成员变量和非静态初始化块级别相同。<BR>先初始化父类的静态代码---&gt;初始化子类的静态代码--&gt;<BR>初始化父类的非静态代码---&gt;初始化父类构造函数---&gt;<BR>初始化子类非静态代码---&gt;初始化子类构造函数<BR>3、创建类实例时，首先按照父子继承关系进行初始化<BR>4、类实例创建时候，首先初始化块部分先执行，然后是构造方法；然后从<BR>本类继承的子类的初始化块执行，最后是子类的构造方法<BR>5、类消除时候，首先消除子类部分，再消除父类部分<BR><STRONG>测试:<BR><BR>package com.javabase;</STRONG></P>
<P><STRONG>/**<BR>&nbsp;* &lt;p&gt;Title: 平时小例子练习&lt;/p&gt;<BR>&nbsp;*<BR>&nbsp;* &lt;p&gt;Description: 平时小例子练习&lt;/p&gt;<BR>&nbsp;*<BR>&nbsp;* &lt;p&gt;Copyright: Copyright (c) 2005&lt;/p&gt;<BR>&nbsp;*<BR>&nbsp;* &lt;p&gt;Company: 北京开元东方科技有限公司&lt;/p&gt;<BR>&nbsp;*<BR>&nbsp;* @author 樊建强<BR>&nbsp;* @version 1.0<BR>&nbsp;* 测试java类加载初始化顺序<BR>&nbsp;*/<BR>abstract class base<BR>{<BR>&nbsp;&nbsp;&nbsp; public int age=getNumber(100);<BR>&nbsp;&nbsp;&nbsp; static<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base static block");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base nonstatic block");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; static int sage=getNumber(50);<BR>&nbsp;&nbsp;&nbsp; base()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(age);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base start");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; draw();//会调用子类覆盖后的方法，哪批怕写在游离块中也会执行子类覆盖后的方法<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base end");</STRONG></P>
<P><STRONG>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; static int getNumber(int base)<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base.getNumber int"+base);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return base;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public&nbsp; void draw()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("base.draw");<BR>&nbsp;&nbsp;&nbsp; }<BR>}</STRONG></P>
<P><STRONG>public class JavaBase extends base{<BR>&nbsp;&nbsp;&nbsp; public int age=getNumber(1001);<BR>&nbsp;&nbsp;&nbsp; private int _radius=getNumber(10);<BR>&nbsp;&nbsp;&nbsp; static int sage=getNumber(250);<BR>&nbsp;&nbsp;&nbsp; static<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("subclass static block");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("subclass nonstatic block");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; JavaBase(int radius)<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _radius=radius;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(age);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("initializeOrder initialized");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; public void draw()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("initializeOrder.draw "+_radius);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; /** *//**<BR>&nbsp;&nbsp;&nbsp;&nbsp; * @param args<BR>&nbsp;&nbsp;&nbsp;&nbsp; */<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // TODO Auto-generated method stub<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-------------类初始化开始-------------");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new JavaBase(1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-------------类初始化结束-------------");<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR><BR>执行结果:<BR><BR>base static block</STRONG></P>
<P><STRONG>base.getNumber int50</STRONG></P>
<P><STRONG>base.getNumber int250</STRONG></P>
<P><STRONG>subclass static block</STRONG></P>
<P><STRONG>-------------类初始化开始-------------</STRONG></P>
<P><STRONG>base.getNumber int100</STRONG></P>
<P><STRONG>base nonstatic block</STRONG></P>
<P><STRONG>initializeOrder.draw 0</STRONG></P>
<P><STRONG>100</STRONG></P>
<P><STRONG>base start</STRONG></P>
<P><STRONG>base end</STRONG></P>
<P><STRONG>base.getNumber int1001</STRONG></P>
<P><STRONG>base.getNumber int10</STRONG></P>
<P><STRONG>subclass nonstatic block</STRONG></P>
<P><STRONG>1001</STRONG></P>
<P><STRONG>initializeOrder initialized</STRONG></P>
<P><STRONG>-------------类初始化结束-------------<BR>更正:static</STRONG>静态成员变量和静态初始化是在new对象之前进行的.<BR><BR>后待添加更正***************************************************************<BR><BR>Static详解:<BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top width=413>&nbsp;一、static 
<P>　　请先看下面这段程序：</P>
<P>&nbsp; public class Hello{<BR>&nbsp;&nbsp;&nbsp; public static void main(String[] args){ //(1)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Hello,world!");&nbsp;&nbsp; //(2)<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp; }</P>
<P>　　看过这段程序，对于大多数学过Java 的从来说，都不陌生。即使没有学过Java，而学过其它的高级语言，例如C，那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”，一点别的用处都没有，然而，它却展示了static关键字的主要用法。</P><BR>
<P></P></TD></TR>
<TR>
<TD colSpan=2 height=20>
<P><BR></P></TD></TR></TBODY></TABLE></P>
<P>　　在1处，我们定义了一个静态的方法名为main，这就意味着告诉Java编译器，我这个方法不需要创建一个此类的对象即可使用。你还记得你是怎么运行这个程序吗？一般，我们都是在命令行下，打入如下的命令：</P>
<P>javac Hello.java<BR>java Hello<BR>Hello,world!</P>
<P>　　这就是你运行的过程，第一行用来编译Hello.java这个文件，执行完后，如果你查看当前，会发现多了一个Hello.class文件，那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。执行结果如你所料。在2中，你可能会想，为什么要这样才能输出。好，我们来分解一下这条语句。（如果没有安装Java文档，请到Sun的官方网站浏览J2SE API）首先，System是位于java.lang包中的一个核心类，如果你查看它的定义，你会发现有这样一行：public static final PrintStream out;接着再进一步，点击PrintStream这个超链接，在METHOD页面，你会看到大量定义的方法，查找println，会有这样一行：</P>
<P>public void println(String x)。</P>
<P>　　好了，现在你应该明白为什么我们要那样调用了，out是System的一个静态变量，所以可以直接使用，而out所属的类有一个println方法。</P>
<P><STRONG>静态方法</STRONG></P>
<P>　　通常，在一个类中定义一个方法为static，那就是说，用类名而无需本类的对象即可调用此方法。如下所示：</P>
<P>class Simple{<BR>&nbsp;&nbsp; static void go(){<BR>&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Go...");<BR>&nbsp;&nbsp; }<BR>}<BR>public class Cal{<BR>&nbsp; public static void main(String[] args){<BR>&nbsp;&nbsp;&nbsp; Simple.go();<BR>&nbsp; }<BR>}</P>
<P>　　调用一个静态方法就是“类名.方法名”,静态方法的使用很简单如上所示。一般来说，静态方法常常为应用程序中的其它类提供一些实用工具所用，在Java的类库中大量的静态方法正是出于此目的而定义的。</P>
<P><STRONG>静态变量</STRONG></P>
<P>　　静态变量与静态方法类似。所有此类实例共享此静态变量，也就是说在类装载时，只分配一块存储空间，所有此类的对象都可以操控此块存储空间，当然对于final则另当别论了。看下面这段代码：</P>
<P>class Value{<BR>&nbsp; static int c=0;<BR>&nbsp; static void inc(){<BR>&nbsp;&nbsp;&nbsp; c++;<BR>&nbsp; }<BR>}<BR>class Count{<BR>&nbsp; public static void prt(String s){<BR>&nbsp;&nbsp;&nbsp; System.out.println(s);<BR>&nbsp; }<BR>&nbsp; public static void main(String[] args){<BR>&nbsp;&nbsp;&nbsp; Value v1,v2;<BR>&nbsp;&nbsp;&nbsp; v1=new Value();<BR>&nbsp;&nbsp;&nbsp; v2=new Value();<BR>&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp; v1.inc();<BR>&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);&nbsp; <BR>&nbsp; }<BR>}</P>
<P>　　结果如下：</P>
<P>v1.c=0&nbsp; v2.c=0<BR>v1.c=1&nbsp; v2.c=1</P>
<P>由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序：</P>
<P>class Value{<BR>&nbsp; static int c=0;<BR>&nbsp; Value(){<BR>&nbsp;&nbsp;&nbsp; c=15;<BR>&nbsp; }<BR>&nbsp; Value(int i){<BR>&nbsp;&nbsp;&nbsp; c=i;<BR>&nbsp; }<BR>&nbsp; static void inc(){<BR>&nbsp;&nbsp;&nbsp; c++;<BR>&nbsp; }<BR>}<BR>class Count{<BR>&nbsp; public static void prt(String s){<BR>&nbsp;&nbsp;&nbsp; System.out.println(s);<BR>&nbsp; }<BR>&nbsp;&nbsp;&nbsp; Value v=new Value(10);<BR>&nbsp;&nbsp;&nbsp; static Value v1,v2;<BR>&nbsp;&nbsp;&nbsp; static{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v1=new Value(27);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v2=new Value(15);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp; public static void main(String[] args){<BR>&nbsp;&nbsp;&nbsp; Count ct=new Count();<BR>&nbsp;&nbsp;&nbsp; prt("ct.c="+ct.v.c);<BR>&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp; v1.inc();<BR>&nbsp;&nbsp;&nbsp; prt("v1.c="+v1.c+"&nbsp; v2.c="+v2.c);<BR>&nbsp;&nbsp;&nbsp; prt("ct.c="+ct.v.c);<BR>&nbsp; }<BR>}</P>
<P>运行结果如下：</P>
<P>v1.c=0&nbsp; v2.c=0<BR>v1.c=27&nbsp; v2.c=27<BR>v1.c=15&nbsp; v2.c=15<BR>ct.c=10<BR>v1.c=10&nbsp; v2.c=10<BR>v1.c=11&nbsp; v2.c=11<BR>ct.c=11</P>
<P>　　这个程序展示了静态初始化的各种特性。如果你初次接触Java，结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是，static定义的变量会优先于任何其它非static变量，不论其出现的顺序如何。正如在程序中所表现的，虽然v出现在v1和v2的前面，但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码，这是用来进行显式的静态变量初始化，这段代码只会初始化一次，且在类被第一次装载时。如果你能读懂并理解这段代码，会帮助你对static关键字的认识。在涉及到继承的时候，会先初始化父类的static变量，然后是子类的，依次类推。</P>
<P>　　通常一个普通类不允许声明为静态的，只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用，而不需实例一个外部类。如下代码所示：</P>
<P>public class StaticCls{<BR>&nbsp; public static void main(String[] args){<BR>&nbsp;&nbsp;&nbsp; OuterCls.InnerCls oi=new OuterCls.InnerCls();<BR>&nbsp; }<BR>}<BR>class OuterCls{<BR>&nbsp; public static class InnerCls{<BR>&nbsp;&nbsp;&nbsp; InnerCls(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("InnerCls");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp; }<BR>}</P>
<P>　　输出结果会如你所料：</P>
<P>InnerCls</P><img src ="http://www.blogjava.net/fjq639/aggbug/24904.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-21 09:12 <a href="http://www.blogjava.net/fjq639/archive/2005/12/21/24904.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Junit概述</title><link>http://www.blogjava.net/fjq639/archive/2005/12/20/24833.html</link><dc:creator>黑石</dc:creator><author>黑石</author><pubDate>Tue, 20 Dec 2005 10:02:00 GMT</pubDate><guid>http://www.blogjava.net/fjq639/archive/2005/12/20/24833.html</guid><wfw:comment>http://www.blogjava.net/fjq639/comments/24833.html</wfw:comment><comments>http://www.blogjava.net/fjq639/archive/2005/12/20/24833.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/fjq639/comments/commentRss/24833.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/fjq639/services/trackbacks/24833.html</trackback:ping><description><![CDATA[1、概述<BR>　　Junit测试是程序员测试，即所谓白盒测试，因为程序员知道被测试的软件如何（How）完成功能和完成什么样（What）的功能。<BR>　　Junit本质上是一套框架，即开发者制定了一套条条框框，遵循这此条条框框要求编写测试代码，如继承某个类，实现某个接口，就可以用Junit进行自动测试了。<BR>　　由于Junit相对独立于所编写的代码，可以测试代码的编写可以先于实现代码的编写，XP 中推崇的 test first design的实现有了现成的手段：用Junit写测试代码，写实现代码，运行测试，测试失败，修改实现代码，再运行测试，直到测试成功。以后对代码的修改和优化，运行测试成功，则修改成功。<BR>　　Java 下的 team 开发，采用 cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时，通过对ant的配置，可以很简单地实现测试自动化。<BR><BR>　　对不同性质的被测对象，如Class，Jsp，Servlet，Ejb等，Junit有不同的使用技巧，以后慢慢地分别讲叙。以下以Class测试为例讲解，除非特殊说明。<BR><BR>2、下载安装<BR><BR><BR>去Junit主页下载最新版本3.8.1程序包junit-3.8.1.zip<BR><BR>用winzip或unzip将junit-3.8.1.zip解压缩到某一目录名为$JUNITHOME<BR><BR>将junit.jar和$JUNITHOME/junit加入到CLASSPATH中，加入后者只因为测试例程在那个目录下。<BR><BR>注意不要将junit.jar放在jdk的extension目录下<BR><BR>运行命令,结果如下图。<BR>java junit.swingui.TestRunner junit.samples.AllTests<BR><BR><BR><BR>3、Junit架构<BR>　　下面以Money这个类为例进行说明。<BR><BR>public class Money {<BR>&nbsp; &nbsp; private int fAmount;//余额<BR>&nbsp; &nbsp; private String fCurrency;//货币类型<BR><BR>&nbsp; &nbsp; public Money(int amount, String currency) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;fAmount= amount;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;fCurrency= currency;<BR>&nbsp; &nbsp; }<BR><BR>&nbsp; &nbsp; public int amount() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return fAmount;<BR>&nbsp; &nbsp; }<BR><BR>&nbsp; &nbsp; public String currency() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return fCurrency;<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public Money add(Money m) {//加钱<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return new Money(amount()+m.amount(), currency());<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public boolean equals(Object anObject) {//判断钱数是否相等<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (anObject instanceof Money) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Money aMoney= (Money)anObject;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return aMoney.currency().equals(currency())<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &amp;&amp; amount() == aMoney.amount();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return false;<BR>&nbsp; &nbsp; }&nbsp; &nbsp; <BR>}<BR><BR><BR>　　Junit本身是围绕着两个设计模式来设计的：命令模式和集成模式.<BR><BR>命令模式<BR>　　利用TestCase定义一个子类，在这个子类中生成一个被测试的对象，编写代码检测某个方法被调用后对象的状态与预期的状态是否一致，进而断言程序代码有没有bug。<BR>　　当这个子类要测试不只一个方法的实现代码时，可以先建立测试基础，让这些测试在同一个基础上运行，一方面可以减少每个测试的初始化，而且可以测试这些不同方法之间的联系。<BR>　　例如，我们要测试Money的Add方法，可以如下:<BR>public class MoneyTest extends TestCase { //TestCase的子类<BR>&nbsp; &nbsp; public void testAdd() { //把测试代码放在testAdd中<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money m12CHF= new Money(12, "CHF");&nbsp;&nbsp;//本行和下一行进行一些初始化<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money m14CHF= new Money(14, "CHF");&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money expected= new Money(26, "CHF");//预期的结果<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money result= m12CHF.add(m14CHF);&nbsp; &nbsp; //运行被测试的方法<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));&nbsp; &nbsp;&nbsp;&nbsp;//判断运行结果是否与预期的相同<BR>&nbsp; &nbsp; }<BR>}<BR><BR>　　如果测试一下equals方法，用类似的代码，如下：<BR>public class MoneyTest extends TestCase { //TestCase的子类<BR>&nbsp; &nbsp; public void testEquals() { //把测试代码放在testEquals中<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money m12CHF= new Money(12, "CHF"); //本行和下一行进行一些初始化<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money m14CHF= new Money(14, "CHF");<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!m12CHF.equals(null));//进行不同情况的测试<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(m12CHF, m12CHF);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(m12CHF, new Money(12, "CHF")); // (1)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!m12CHF.equals(m14CHF));<BR>&nbsp; &nbsp; }<BR>}<BR><BR><BR>　　当要同时进行测试Add和equals方法时，可以将它们的各自的初始化工作，合并到一起进行，形成测试基础,用setUp初始化，用tearDown清除。如下：<BR>public class MoneyTest extends TestCase {//TestCase的子类<BR>&nbsp; &nbsp; private Money f12CHF;//提取公用的对象<BR>&nbsp; &nbsp; private Money f14CHF;&nbsp; &nbsp;<BR><BR>&nbsp; &nbsp; protected void setUp() {//初始化公用对象<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;f12CHF= new Money(12, "CHF");<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;f14CHF= new Money(14, "CHF");<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; public void testEquals() {//测试equals方法的正确性<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(null));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF, f12CHF);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF, new Money(12, "CHF"));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(f14CHF));<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public void testSimpleAdd() {//测试add方法的正确性<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money expected= new Money(26, "CHF");<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money result= f12CHF.add(f14CHF);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));<BR>&nbsp; &nbsp; }<BR>}<BR><BR><BR>　　将以上三个中的任一个TestCase子类代码保存到名为MoneyTest.java的文件里，并在文件首行增加<BR>import junit.framework.*;<BR>，都是可以运行的。关于Junit运行的问题很有意思，下面单独说明。<BR>　　上面为解释概念“测试基础(fixture)”，引入了两个对两个方法的测试。命令模式与集成模式的本质区别是，前者一次只运行一个测试。<BR><BR>集成模式<BR>　　利用TestSuite可以将一个TestCase子类中所有test***()方法包含进来一起运行，还可将TestSuite子类也包含进来，从而行成了一种等级关系。可以把TestSuite视为一个容器，可以盛放TestCase中的test***()方法，它自己也可以嵌套。这种体系架构，非常类似于现实中程序一步步开发一步步集成的现况。<BR>　　对上面的例子，有代码如下：<BR>public class MoneyTest extends TestCase {//TestCase的子类<BR>&nbsp; &nbsp; ....<BR>&nbsp; &nbsp; public static Test suite() {//静态Test<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;TestSuite suite= new TestSuite();//生成一个TestSuite<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;suite.addTest(new MoneyTest("testEquals")); //加入测试方法<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;suite.addTest(new MoneyTest("testSimpleAdd"));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return suite;<BR>&nbsp; &nbsp; }<BR>}<BR><BR>　　从Junit2.0开始，有列简捷的方法:<BR>public class MoneyTest extends TestCase {//TestCase的子类<BR>&nbsp; &nbsp; ....<BR>&nbsp; &nbsp; public static Test suite() {静态Test<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return new TestSuite(MoneyTest.class); //以类为参数<BR>&nbsp; &nbsp; }<BR>}<BR><BR>　　TestSuite见嵌套的例子，在后面应用案例中有。<BR>　　<BR><BR>4、测试代码的运行<BR>　　先说最常用的集成模式。<BR>　　测试代码写好以后，可以相应的类中写main方法，用java命令直接运行；也可以不写main方法，用Junit提供的运行器运行。Junit提供了textui,awtui和swingui三种运行器。<BR>　　以前面第2步中的AllTests运行为例，可有四种：<BR><BR>java junit.textui.TestRunner junit.samples.AllTests<BR>java junit.awtui.TestRunner junit.samples.AllTests<BR>java junit.swingui.TestRunner junit.samples.AllTests<BR>java junit.samples.AllTests<BR><BR>　　main方法中一般也都是简单地用Runner调用suite()，当没有main时，TestRunner自己以运行的类为参数生成了一个TestSuite.<BR>　　<BR>　　对于命令模式的运行，有两种方法。<BR><BR>静态方法<BR><BR>TestCase test= new MoneyTest("simple add") {<BR>public void runTest() {<BR>testSimpleAdd();<BR>}<BR>};<BR><BR><BR>动态方法<BR><BR>TestCase test= new MoneyTest("testSimpleAdd");<BR><BR>　　我试了一下，好象有问题，哪位朋友成功了，请指点我一下。确实可以。<BR><BR>import junit.framework.*;<BR><BR>public class MoneyTest extends TestCase {//TestCase的子类<BR>&nbsp; &nbsp; private Money f12CHF;//提取公用的对象<BR>&nbsp; &nbsp; private Money f14CHF;&nbsp; &nbsp;<BR>&nbsp; &nbsp; public MoneyTest(String name){<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;super(name);<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; protected void setUp() {//初始化公用对象<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;f12CHF= new Money(12, "CHF");<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;f14CHF= new Money(14, "CHF");<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; public void testEquals() {//测试equals方法的正确性<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(null));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF, f12CHF);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertEquals(f12CHF, new Money(12, "CHF"));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(!f12CHF.equals(f14CHF));<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public void testAdd() {//测试add方法的正确性<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money expected= new Money(26, "CHF");<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Money result= f12CHF.add(f14CHF);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Assert.assertTrue(expected.equals(result));<BR>&nbsp; &nbsp; }<BR>//&nbsp; &nbsp; public static void main(String[] args) {<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;TestCase test=new MoneyTest("simple add") {<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; public void runTest() {<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;testAdd();<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; }<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;};<BR>//&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;junit.textui.TestRunner.run(test);<BR>//&nbsp; &nbsp; }<BR>&nbsp; &nbsp; public static void main(String[] args) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;TestCase test=new MoneyTest("testAdd");<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;junit.textui.TestRunner.run(test);<BR>&nbsp; &nbsp; }<BR>}<BR><BR><BR>再给一个静态方法用集成测试的例子：<BR>public static Test suite() {<BR>&nbsp; &nbsp; TestSuite suite= new TestSuite();<BR>&nbsp; &nbsp; suite.addTest(<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;new testCar("getWheels") {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;protected void runTest() { testGetWheels(); }<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; );<BR><BR>&nbsp; &nbsp; suite.addTest(<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;new testCar("getSeats") {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;protected void runTest() { testGetSeats(); }<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; );<BR>&nbsp; &nbsp; return suite;<BR>}<BR><BR><BR>5、应用案例<BR><BR><BR>Junit Primer例程，运行如下：<BR>java com.hedong.JunitLearning.Primer.ShoppingCartTest<BR><BR><BR>Ant+Junit+Mailto实现自动编译、调试并发送结果的build.xml<BR><BR>JUnit实施,写得很棒，理解也深刻。例程运行如下：<BR>java com.hedong.JunitLearning.car.testCarNoJunit<BR>java junit.swingui.TestRunner com.hedong.JunitLearning.car.testCar<BR><BR><BR>Junit与log4j结合，阿菜的例程运行：<BR>cd acai<BR>ant junit<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>6、一些问题<BR>　　有人在实践基础上总结出一些非常有价值的使用技巧，我没有经过一一“测试”，暂列在此。<BR><BR>不要用TestCase的构造函数初始化Fixture，而要用setUp()和tearDown()方法。<BR><BR>不要依赖或假定测试运行的顺序，因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。不知3.8中是不是还是如此，不过它提供的例子有一个是指定用VectorSuite的，如果不指定呢？<BR><BR>避免编写有副作用的TestCase。例如：如果随后的测试依赖于某些特定的交易数据，就不要提交交易数据。简单的回滚就可以了。<BR><BR>当继承一个测试类时，记得调用父类的setUp()和tearDown()方法。<BR><BR>将测试代码和工作代码放在一起，一边同步编译和更新。（使用Ant中有支持junit的task.）<BR><BR>测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。<BR><BR>确保测试与时间无关，不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。<BR><BR>如果你编写的软件面向国际市场，编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。<BR><BR>尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法，可以使代码更为简洁。<BR><BR>测试要尽可能地小，执行速度快。<BR><BR>把测试程序建立在与被测对象相同的包中<BR><BR>在你的原始代码目录中避免测试码出现，可在一个源码镜像目录中放测试码<BR><BR>在自己的应用程序包中包含一个TestSuite测试类<BR><BR><BR><BR><BR><BR>7、相关资源下载<BR>以下jar包，我只是做了打包、编译和调试的工作，供下载学习之用，相关的权利属于原作者。<BR><BR>可运行例程.jar<BR><BR>Build.xml<BR><BR>阿菜的例程<BR><BR>Junit API 汉译(pdf)<BR><BR><BR>8、未完成的任务<BR><BR><BR>httpunit<BR><BR>cactus<BR><BR>将Junit用链接池测试<BR><BR><BR>主要参考文献：<BR><BR><BR><BR>JUnit入門<BR>[url]http://www.dotspace.twmail.org/Test/JUnit_Primer.htm[/url]<BR><BR>怎样使用Junit Framework进行单元测试的编写<BR>[url]http://www.chinaunix.net/bbsjh/14/546.html[/url]<BR><BR>Ant+Junit+Log4J+CVS进行XP模式开发的建立<BR>[url]http://ejb.cn/modules/tutorials/printpage.php?tid=4[/url]<BR><BR>用HttpUnit测试Web应用程序<BR>[url]http://www.zdnet.com.cn/developer/code/story/0[/url],2000081534,39033726,00.htm<BR><BR>有没有用过Cactus的，Web层的测试是Cactus还是JUnit？<BR>[url]http://www.jdon.com/jive/thread.jsp?forum=16&amp;thread=9156[/url]<BR><BR>Ant+junit的测试自动化 biggie（原作）<BR>[url]http://www.csdn.net/Develop/article/19%5C19748.shtm[/url]<BR><BR>JUnit实施<BR>[url]http://www.neweasier.com/article/2002-08-07/1028723459.html[/url]<BR><BR>JUnitTest Infected: Programmers Love Writing Tests<BR>[url]http://junit.sourceforge.net/doc/testinfected/testing.htm[/url]<BR><BR>JUnit Cookbook<BR>[url]http://junit.sourceforge.net/doc/cookbook/cookbook.htm[/url]<BR><BR>JUnit Primer<BR>[url]http://www.itu.dk/~lthorup/JUnitPrimer.html[/url]<BR><BR>IBM DevelopWorks<BR>[url]http://www-106.ibm.com/search/searchResults.jsp?query=junit&amp;searchScope=dW&amp;[/url]<BR>searchType=1&amp;searchSite=dWChina&amp;pageLang=zh&amp;langEncoding=gb2312&amp;Search.x=0&amp;<BR>Search.y=0&amp;Search=Search<img src ="http://www.blogjava.net/fjq639/aggbug/24833.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/fjq639/" target="_blank">黑石</a> 2005-12-20 18:02 <a href="http://www.blogjava.net/fjq639/archive/2005/12/20/24833.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>