??xml version="1.0" encoding="utf-8" standalone="yes"?> public class FlyNoWay implements FlyBehavior{ public void fly() { public class FlyWithWings implements FlyBehavior{ public void fly() { public class Quack implements QuackBehavior{ public void quack() { public class muteQuack implements QuackBehavior{ public void quack() { } public class Squeak implements QuackBehavior{ public void quack() { import java.lang.reflect.Method; public class Notifier { 使用 DOM、SAX ?JDOM 处理 XML 文的内宏V? 如果使用 DOM ?SAXQ则使用 JAXP 创徏解析器? 如果使用 JDOMQ则 JDOM 库ؓ您创析器?br />
XML文g的类型: Q如果您有一?XML 文档W合 XML 的基本规则,那么它就是一?l构良好?/em> 文。如果该文q满x的公司所定义的支出帐目文档规则,那么它也?有效?/em>?Q?/p>
如果 XML 解析器发?XML 文不是l构良好的,XML Specification 要求解析器报告一个致命错误。但验证是一个不同的问题?strong>验证解析?/strong> 在解析时验证 XML 文Q?非验证解析器 不验证文。换句话_如果一?XML 文是结构良好的Q那么非验证解析器ƈ不关心文是否符?DTD 或模式中定义的规则,甚至不关心该文档是否有这L规则Q原因:验证需要大量工作) 1首先误?Apache XML Project (http://xml.apache.org/xerces2-j/) 上的 Xerces XML 解析器主?/a>。您也可以直接去 下蝲面 (http://xml.apache.org/xerces2-j/download.cgi)? 2解压?Apache 下蝲的文件。根据解析器版本的不同,q样会创徏名ؓ 3讉K JDOM 目站点 q下载最新版本的 JDOM (http://jdom.org/)? 4解压?JDOM 下蝲的文Ӟq样徏立名? 5最后请下蝲本教E的CZ压羃文g 6把当前目?( 1创徏一个解析器对象 2使解析器指向您的 XML 文 3处理l果 当?SAX 解析器解析一?XML 文Ӟ解析器在d文档的过E中会生成一pd的事件。至于如何处理这些事件则取决于您。下面列Z一部分您?XML 文时可能遇到的事gQ?/p>
?DOM 一PSAX 解析器也忽略了某些细节,如属性出现的序?/p>
SAX ?DOM 都过于复杂, ?DOM cMQJDOM 也提供一个对象树表示 XML 文Q但是这些对象工作的方式?Java E序员更直观。要CQJDOM 在背后包含用普?SAX ?DOM 解析器的适配器;JDOM Ҏ有主要的Q和几个ơ要的) Java XML 解析器都提供了适配器,因此不必担心您的 Java XML 解析器是否支?JDOM。JDOM 在幕后用一个解析器不需要您的干涉?/p>
DOM 当?DOM 解析器解析一?XML 文Ӟ您得C个层ơ化的数据结构(DOM 树)Q它表示解析器在 XML 文中发现的所有内宏V然后您可以使用 DOM 函数操纵q棵树。您可以搜烦树中的内宏V移动分支、增加新的分支或者删除树的一部分?/p>
DOM 解析器返回一个树状结构,q个 DOM 树包含一? DOM Level 1 q定义了 注意Q?/strong>“元素”?#8220;标签”q两个词有不同的含义?strong>元素 是指起始元素、结束元素以及两者之间的一切内容,包括属性、文本、注释以及子元素?strong>标签 DOM l常要用C下方法: W一个应用程序是
跟朋友讨Z一?大体上猜出几个,也有两个不知道何?如果有知道正解的请不吝赐?
AN-要求分析
BD-基本设计
FD-l构设计
DD-详细设计
C -~码
UT-单体试
CT-不知?br />
TT-l合试
IT-pȝ试
OP-发布
以上只是个h理解,像OPq样的纯属猜?..Ƣ迎指摘!
]]>
设计原则:针对接口~程,而不是针对实现编E?
背景:需要设计一个鸭子类,有飞行和呱呱叫两U行?可是后来引入了橡皮鸭,D传统的承处理无法?不能让橡皮鸭也会?而且q可能会有既不会飞也不会叫的鸭子,或者需要引入不同种鸭子的具体的飞行q行,叫的声音......
{略模式:把易变的动作部分抽象出来成ؓ单独的类(接口).
定义:定义了算法族,分别装h,让它们之间可以互相替?此模式让法的变化独立于使用法的客?
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void swim(){
System.out.println("All ducks float,even decoys!");
}
}
public interface FlyBehavior {
public void fly();
}
System.out.println("I can't fly");
}
}
System.out.println("I'm flying!!");
}
}
public void quack();
}
System.out.println("Quack");
}
}
System.out.println("<<Silence>>");
}
System.out.println("Squeak");
}
}
public MallardDuck(){
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a real Mallard duck.");
}
}
试c?
public class MiniDuckSimulator {
public static void main(String[] args){
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
]]>
...notifier.addListener( someObject );
...notifier.notify( new ActionEvent(this) );只要几行代码p够完成一切?br />
下面的Notifiercd辑ֈ了这个目的:
package com.generationjava.lang;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
private ArrayList listeners = new ArrayList();
private String listenerMethod;
public Notifier(String name) {
this.listenerMethod = name;
}
public void addListener(Object not) {
this.listeners.add(not);
}
public void removeListener(Object not) {
this.listeners.remove(not);
}
public void notify(EventObject event) {
Iterator itr = listeners.iterator();
while(itr.hasNext()) {
try {
Object listener = itr.next();
Class clss = listener.getClass();
Method method = clss.getMethod(this.listenerMethod,new Class[] { event.getClass() });
method.invoke( listener, new Object[] { event } );
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
以下内容摘自CSDCC: (http://topic.csdn.net/t/20020918/10/1032825.html)
其实java的事件机制十分简单,你完全可以实现自q事g驱动Q?
主要涉及到如下几个类Q可以参考键盘事件的有关c)Q?
1.事gcXXXEventQ其中包含你所规定的事件的一些信息,如事件源Q事件名U等{,如KeyEventQ中有eventSourceQkeyCode{等
2.事g监听接口QXXXEventListenerQ其中包含捕获到事g的类要作的事情,如KeyListenerQ其中包?keyPress,keyReleased,{等
如:
public interface XXXListener{
public void doXXXEvent(xxxEvent e);
}
3.发出事g的类Q可以是L的类Q在q个cMQ可以发事gXXXEventQ比如可以在q个cMd一个fireXXXEventҎQ在q个Ҏ中去手工发出事gQ如Q?
public void fireXXXEvent(){
java.util.Vector targets;
synchronized (this) {
targets = (java.util.Vector) xxxListeners.clone();
}
XXXEvent evt = new XXXEvent(.....);
for (int i = 0; i < targets.size(); i++) {
XXXListener target = (XXXListener) targets.elementAt(i);
target.doXXXEvent(evt);
}
}
此外在这个类里要有一个监听列表即上面的xxxlistenersQ在q个cMq要实现一个addXXXListrener(XXXListener)ҎQ来把一个实CXXXListenr的类作ؓ监听者加到该cM?
]]>
dXML文g内容Q就需要解析器?br />
最普通的 XML 处理工作?解析 XML 文。解析包括读?XML 文q确定其l构和内宏V?br />
XML ~程的一个好处是可以使用开放源代码的、免费的 XML 解析器读?XML 文?br />
Z化编写处?XML ?Java E序Q已l徏立了多种~程接口?br />
主要的四个接口:
解析器的cdQ?br />
讄Q?br />
xerces-2_5_0
或者类似名U的目录。所需要的 JAR 文gQ?code>xercesImpl.jar ?xml-apis.jar
Q应该出现在 Xerces 根目录下?jdom-b9
或者类似名U的目录。所需要的 JAR 文gQ?code>jdom.jarQ应该在 build
目录中?.
)?code>xercesImpl.jar?code>xml-apis.jar ?jdom.jar
d?CLASSPATH
变量中?br />
使用解析器:
SAXQ?br />
Simple API for XML (SAX) API 是处?XML 文档内容的一U替代方法。它的设计目标是更少的内存占用,但是把更多的工作交给了程序员。SAX ?DOM 是互补的Q有各自的适用环境?br />
startDocument
事g?
startElement
事gQ元素结束时?endElement
事g?
characters
事gQ对于子元素出?startElement
?endElement
事gQ依此类推?
endDocument
事g?
DOM 是处?XML 文档l构的一U接口。作Z?W3C 目QDOM 的设计目标是提供一l对象和ҎQɽE序员的工作更轻松?br />
Node
。从 Java 语言的角度看Q?code>Node 是一个接口?code>Node ?DOM 的基本数据类型,DOM 树中的所有事物都是这U或那种cd?Node
?br />
Node
接口的几U子接口Q?/p>
Element
Q表C源文中的一?XML 元素?
Attr
Q表C?XML 元素的一个属性?
Text
Q一个元素的内容。这意味着带有文本的元素包含文本节点孩子,元素的文?不是 元素本n的一个属性?
Document
Q表C整?XML 文档。解析的每个 XML 文档中有且只有一?Document
对象。给定一?Document
对象可以找?DOM 树的根,从这个根可以使用 DOM 函数d操纵树?<p class="blue">
是一个标{,</p>
也是Q?<p class="blue">The quick brown fox</p>
是一个元素?/p>
Document.getDocumentElement()
Q返?DOM 树的栏V(该函数是 Document
接口的一个方法,没有定义其他?Node
子类型。)
Node.getFirstChild()
?Node.getLastChild()
Q返回给?Node
的第一个和最后一个孩子?
Node.getNextSibling()
?Node.getPreviousSibling()
Q返回给?Node
的下一个和上一个兄弟?
Element.getAttribute(String attrName)
Q对于给定的 Element
Q返回名?attrName
的属性的倹{如果需?"id"
属性的|可以使用 Element.getAttribute("id")
。如果该属性不存在Q该Ҏq回一个空字符?(""
)?DomOne.java
Q这D늮单的 Java 代码完成四g事:
W?1 步:扫描命o?/strong>
扫描命o行相对而言非常单。只需要查看一个参敎ͼq假定该参数是一个文件名?URI。如果命令行中没有参敎ͼ则打C个错误消息ƈ退出:
public static void main(String argv[])
{
if (argv.length == 0 ||
(argv.length == 1 && argv[0].equals("-help")))
{
System.out.println("\nUsage: java DomOne uri");
System.out.println(" where uri is the URI of the XML " +
"document you want to print.");
System.out.println(" Sample: java DomOne sonnet.xml");
System.out.println("\nParses an XML document, then writes " +
"the DOM tree to the console.");
System.exit(1);
}
DomOne d1 = new DomOne();
d1.parseAndPrint(argv[0]);
}
W?2 步:创徏一个解析器对象 |
public void parseAndPrint(String uri)
{
Document doc = null;
try
{
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
. . .
W?3 步:解析 XML 文g
现在您已l创Z解析器,告诉它解析哪个文Ӟ?URIQ很单:
doc = db.parse(uri);
W?4 步:打印 DOM 树的内容
最后一Q务是打印 DOM 树的内容。因?DOM 树中的一切事物都是这U或那种cd?Node
Q您需要用递归的方法遍历ƈ打印其中的内宏V策略是调用该方法打C个节炏V该Ҏ打印那个节点,q对该节点的每个孩子调用自n。如果这些孩子还有孩子,该方法将对这些孩子调用自w。下面是一个例子:
if (doc != null)
printDomTree(doc);
. . .
}
public void printDomTree(Node node)
{
int type = node.getNodeType();
switch (type)
{
// print the document element
case Node.DOCUMENT_NODE:
{
System.out.println("<?xml version=\"1.0\" ?>");
printDomTree(((Document)node).getDocumentElement());
break;
}
printDomTree
Ҏ以一?Node
作ؓ参数。如果该 Node
是一个文节点,打C?XML 声明Q然后对文元素Q包含文其他内容的元素Q调?printDomTree
。文元素基本上L有其他孩子,因此 printDomTree
也将对这些孩子调用自w。这U递归法可以遍历整个 DOM 树ƈ把内Ҏ印到命o行中?/p>
以下?printDomTree
对元素节点的处理Q?/p>
case Node.ELEMENT_NODE:
{
System.out.print("<");
System.out.print(node.getNodeName());
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
printDomTree(attrs.item(i));
System.out.print(">");
if (node.hasChildNodes())
{
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
printDomTree(children.item(i));
}
System.out.print("</");
System.out.print(node.getNodeName());
System.out.print('>');
break;
}
对于元素节点Q您需要打C个左括号和l点名称。如果该元素有属性,对每个属性调?printDomTree
。(技术上Ԍq里可以使用 Attr.getName()
?Attr.getValue()
打印属性,我这样做是ؓ了说?DOM 树中的一切都?Node
。)一旦打印完所有的属性,可以对该节Ҏ包含的所有子元素再次调用 printDomTree
。最后一步是在处理完所有的子元素之后打印结束元素?/p>
处理属性节点和文本节点很简单。对于属性,需要输Z个空根{属性名、等受开双引受属性值和闭双引号。(q里化了Q属性值可以用单引号和双引号Q如果愿意您完全可以修正q个问题。)
处理文本节点最单,只需要输出文本就可以了。以下是q两U节点类型的代码Q?/p>
case Node.ATTRIBUTE_NODE:
{
System.out.print(" " + node.getNodeName() + "=\"" +
((Attr)node).getValue() + "\"");
break;
}
case Node.TEXT_NODE:
{
System.out.print(node.getNodeValue());
break;
}
管q里只是打印 DOM 树的内容Q但多数围绕着 DOM 建立?XML 应用E序都用这U四个步骤的模式Q第四步是递归处理例程?/font>
q行 DomOne |
在您解压CZ文g的目录下键入Q?/p>
javac DomOne.java
Q记?Java 语言是区分大写的。)如果没有M错误Q键入:
java DomOne sonnet.xml
解析q显C文?sonnet.xml
?/p>
如果一切都和预期的一P您应该看到类DL内容Q?/p>
C:\xml-prog-java>java DomOne sonnet.xml
<?xml version="1.0" ?>
<sonnet type="Shakespearean">
<author>
<lastName>Shakespeare</lastName>
<firstName>William</firstName>
<nationality>British</nationality>
<yearOfBirth>1564</yearOfBirth>
<yearOfDeath>1616</yearOfDeath>
</author>
<title>Sonnet 130</title>
<lines>
<line>My mistress' eyes are nothing like the sun,</line>
<line>Coral is far more red than her lips red.</line>
<line>If snow be white, why then her breasts are dun,</line>
<line>If hairs be wires, black wires grow on her head.</line>
. . .
</lines>
待箋
摘自IBM在线学习
整个体系架构非常清晰Q按照我们的要求实现了全部功能,而且相当E_。但是打开具体的代码一看,拖沓冗长Q水q不咋样。我们自q一些程序员有怪话了,说他们水q真低。但是!印度够把软g整体把握得很好,能够完成软gQƈ得到相当好的设计文档?
而中国h在那里琢数据结构、算法,界面人员p没编码就想着是Outlook式的q是Visual Studio式的界面。到最后就成ؓCode高手Q对?
些特定的开发工L通,但是是不能保证能够把一个Y件稳当、完整的开发出来?
举个单的例子Q?
软g中需要一个列表,用来表示我们处理的事务。该c表在业务繁忙的时候将变得很大。中国hq双向链表Q抱着《数据结构》书在那里写
链表的类。印度h开了一个大数组Q然后就开始干。ؓ什么印度h不用链表Q他们说Q?
1.你们l出的设备(型机)Q最具?12M内存Q浪费一些没有什么?
2.数组方式讉K方便、效率高?
看出了一拿到东西吭哧吭哧作CodeQ和好好q行软g分析的不同了吗?正好前几天我有几个同事从印度回来和我们交,那家公司是CMM4U?
公司。我感受的几点:
1.程重于目
2.QCQ就是QAQ独立于研发部门Q专门检查研发部门的开发流E是不是按照既定程走。如果QC觉得程不对Q他会直接上报高层,目肯定此停止?
3.所谓的目l理QPCQ一般也是从~码人员升上来的Qƈ不是所谓的不懂技术,一般都臛_有四q以上的l验
4.PC主要是制定开发计划,负责协调Q填写各U表根{?
5.所有的东西Q包括草E)都有文?
6.详细文要求辑ֈ只有q个文可以编码的E度Q一般写文档旉?0%Q编码时间极?
7.有各U详l的reviewQ同行评审)Q项目组内的Q项目组之间的,客户?#8230;…
8.计划很详l,的确能达到小时Q但是实际情况还是误差比较大Q所以他们也有加班?
先学习UML和Rose以及RUPQ不要L要找着证据。在中国的Y件开发水q下Q很隄你一个好的例子,OKQ中国hL要看C个东西有了试
验田Q而且Ed长得好,才换Eȝ。要知道在国外上q的软g开发模式的应用Q大可以看看 Rational|页上的story。Just do itQ一句话Q中国的软g开发水q低得很。赶不上印度人,印度的Y件公司可以让高中生编代码Q它的Y件工E水q_惌知?
当然Q你如果是个很牛的程序员。估计够呛,因ؓ中国的气氛中Q很牛的E序员都很难接受软g工程的。你可以试一下自己,看看自己适不适合现在学习软g工程
1.你是不是不能忍受一个编E序不如你的人做你的目l理Q?
2.你是不是觉得你的老板对客户吹牛皮、夸大自p感C舒服Q?
3.你是不是一个拿C个需求脑袋里W一念头是如何实现的hQ?
4.你是不是很崇拜StallmanQLinusQ很讨厌MicrosoftQ?
5.你是不是曄在深夜编码的时候,H然感觉CU乏呻I对Code的生涯感CU无?我们现在处于深深的自卑当中,感到中国的Y件工E水q的低下已经是牵涉到民族劣根性的问题了?
1.他们的Y件教育水qI我们招聘印度人,l应聘者出了一份与国内差不多的试卷Q有基础概念和编E题目。等C们完成后Q我们这些中?
的自认高手惊呆了Q他们的~程题目直象是抄袭的Q?E序l构Q注释,变量命名׃说了吧,全部都是极其cMQ?
反观中国的牛人、高手,每个人有自己的一套。到了新的岗位,先把前Q的程序贬损一通,然后自己再开发更多的问题的代码来代替。我的公
司统计,一个Y件中?个以上CSocket版本Q每个h都觉得别人做得差Q自己再搞一套。中国hQ就是这个样子,q会辩解?#8220;我们q样有创?
?#8221;。其实Y件发展,早就走过了求伯君那个~码英雄的年代,E序员已l是个坐办公室的蓝领了。你具备拧好一个螺丝钉的能力就可以了,Code
是最低的事情了?
2.他们许多公司的项目经理根本就不懂技术。中国的目l理如果不能在技术上压服下属Q那么下属将与他搞鬼Q越是高手越喜欢搞鬼Q根本不知道作Y件的l极目的是从别h兜里掏钱Q而在内部搞不团结。技术高手都会纠集一些对他技术上崇拜的菜鸟,与管理层作对?
而印度的软gl理Ҏ׃懂正在做的东西,许多甚至直接是MBAQ或者是领域专家Q工业设计、地理专家等Q,而不是编码的专家。但是却
能够领导大群素质良好的程序员把工作做好,没有内部不团l的情况。许多印度的E序员加入一个公司很长时_都不知道自己整天~的代码是干
什么用的。给他们的Q务可能就是一个函数的声明以及该函数要实现的功能。我们呢Q?
3.他们的编Eh员的动率达?30%Q他们的~程人员动率(包括内部目之间的流动)高达 30%Q可以想见他们的文水^如何。他们的
产品不依赖Q何一个hQ谁都可以立卌职,产品的开发还是会正常q行。而中国,是老板怕d。技术骨q拥兵自重,抗拒理。Q何制定好的计
划,都有可能被技术h员推L者跟你消极怠工?
4.他们的开发计划能够做到小时别。如果一个印度公司的目l理没有上班Q那么他的下属将可能不知道作什么。他们的计划一般都定到天,
每个基层开发h员每天的工作量就?8时。而我们能够给出月度计划的公司很,而给出的月度计划要么不可能实玎ͼ要么可能被取消。开发h员被初略的给个Q务,他在月初Q可以慢慢琢是做成什么样子,然后上上|,聊聊天。到了月中和月末Q就开始熬夜编码?
看到每年Q从各大高校不尽牛h滚滚来,我们是不得不要召人,同时又是不抱希望。我公司现在有意以后核心Y件开发外包给印度公司Q中
国hQ做做界面吧Q中国h做界面会极尽奇技淫yQ搞得花里胡哨的。BTWQ大家不要误会我有什么种族歧视。但是我们现在就是对自己歧视Q自卑得很。中U院那么多研IQ连个能用的操作pȝ
都搞不定?
北大开发一些东西,比如什么青鸟CASEQ就是给一帮h评职U的。杨芙清院士整天搞来搞去Q搞Z什么东西?B大,T大的人最隄理,牛得看不见h。中国的E序员骂微YQ追 Linux是全世界最狠的Q可是我们除了汉化LinuxQ做了什么东西出来。CDE 是瑞思h写的QLinus是芬兰的QGNome是墨西哥人写的。哎Q我们曾l是多么的瞧不v印度?.....
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=2048678
再{自:http://blog.csdn.net/liukai721/archive/2008/01/17/2048678.aspx
![]() |
h和响?/strong> |
直到今天Q生成动态内容的最普通方法是通过 Common Gateway Interface, 也称?CGI。CGI E序(通常?C 或?Perl ~写) 通过?HTML 表单形式d用户的输入,然后q回定制?HTML 面。但是,CGI 有一个缺点:对于用户的每个请求,CGI 脚本必须l过“载入”?#8220;q行” ?#8220;调出”。这非常低效?/p>
![]() |
静态和动态响?/strong> |
另一U生成动态内容的Ҏ是?Java servlet。servlet ?Java E序可以被蝲入到一个应用服务器中,例如WebSphere Application Server?servlet 可以完成 CGI 脚本的功能,但是它们ȝ在服务器的内存中。因此,servlet 要对用户的响应更为及时?/p>
JSP 技术提供了一U合HTML ?Java servlet ~程的环境。JSP 面是纯文本文g而且看v来很cM HTML 面。HTML 通过新的标记得以扩展Q新的标记指明一?servlet 控制动态内容的生成的程序逻辑?br />
如果用户h的信息被包含在驻留于 HTTP 服务器的静态页面中Q响应将是驻留页面的 HTML 版本。对动态响应而言Q将?HTTP 服务器生一个调用到 WebSphere Application ServerQ或其他L一U应用服务器Q它们将理 JSP 面?servlet。应用服务器可以被配|成预先载入 Java servlet 来实现对用户响应速度的提高,甚至对于W一个用Lh?/p>
JSP 面被一ơ编译ؓ servlet q被载入到内存。如?JSP 面有了新版本,应用服务器将~译新的版本然后载入对应的新 servlet?/font>
Z么要使用JSP呢?
首先QJava ~程语言h "Write Once, Run Anywhere(tm)"(书写一ơ,Lq行) 的特性?br />
|嗦的注释一般比较难l护Q通常不及一个小的、编写良好的Ҏ那么好地表达意图Qƈ且很快就会过期。根本不要过分依赖注释?/p>
6Q用一致的风格。惟一l对的风D则是一致?/em>。如果一个项目上的每个h都用不同的风|那么阅读代码变得很困难。挑选一U风格ƈ且不要改变?br />
7Q避免switch?
8QҎ?public
的,变量?protected
的,直到您有一个很好的理由限制讉K。如果您对代码中您设惛_他h不应该访问的东西限制讉KQ您是在设惌己无所不知。这在大多数时候是一个危险的假设。不要以“q段代码不应该被调用”、或?#8220;没有Z用这D代?#8221;为理由气l将代码设ؓpublic?/p>
1.垃圾攉法的核心思想
Java语言建立了垃圾收集机Ӟ用以跟踪正在使用的对象和发现q回收不再?引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽Q以及不恰当的内存释放所造成的内存非法引用?/font>
垃圾攉法的核心思想是:对虚拟机可用内存I间Q即堆空间中的对象进行识别,如果对象正在被引用,那么U其为存zd象,反之Q如果对象不再被引用Q则为垃圑֯象,可以回收其占据的I间Q用于再分配。垃圾收集算法的选择和垃圾收集系l参数的合理调节直接影响着pȝ性能Q因此需要开发h员做比较深入的了解?/font>
2.触发主GC(Garbage Collector)的条?/strong>
JVMq行ơGC的频率很?但因UGC占用旉极短,所以对pȝ产生的媄响不大。更值得x的是主GC的触发条?因ؓ它对pȝ影响很明显。ȝ来说,有两个条件会触发主GC:
①当应用E序I闲?x有应用线E在q行?GC会被调用。因为GC在优先最低的U程中进?所以当应用忙时,GCU程׃会被调用,但以下条仉外?/font>
②Java堆内存不x,GC会被调用。当应用U程在运?q在q行q程中创建新对象,若这时内存空间不?JVM׃强制地调用GCU程,以便回收内存用于新的分配。若GC一ơ之后仍不能满内存分配的要?JVM会再q行两次GC作进一步的试,若仍无法满要求,?JVM报“out of memory”的错?Java应用停止?/font>
׃是否q行主GC由JVMҎpȝ环境军_,而系l环境在不断的变化当?所以主GC的运行具有不定?无法预计它何时必然出?但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的?/font>
3.减少GC开销的措?/strong>
Ҏ上述GC的机?E序的运行会直接影响pȝ环境的变?从而媄响GC的触发。若不针对GC的特点进行设计和~码,׃出现内存ȝ{一pd负面影响。ؓ了避免这些媄?基本的原则就是尽可能地减垃圑֒减少GCq程中的开销。具体措施包括以下几个方?
(1)不要昑ּ调用System.gc()
此函数徏议JVMq行主GC,虽然只是而非一?但很多情况下它会触发主GC,从而增加主GC的频?也即增加了间歇性停的ơ数?/font>
(2)量减少临时对象的?/font>
临时对象在蟩出函数调用后,会成为垃?用临时变量q当于减少了垃圄产生,从而g长了出现上述W二个触发条件出现的旉,减少了主GC的机会?/font>
(3)对象不用时最好显式置为Null
一般而言,为Null的对象都会被作ؓ垃圾处理,所以将不用的对象显式地设ؓNull,有利于GC攉器判定垃?从而提高了GC的效率?/font>
(4)量使用StringBuffer,而不用String来篏加字W串(详见blog另一文章JAVA中String与StringBuffer)
׃String是固定长的字W串对象,累加String对象?q在一个String对象中扩?而是重新创徏新的String对象,如Str5=Str1+Str2+Str3+Str4,q条语句执行q程中会产生多个垃圾对象,因ؓҎ?#8220;+”操作旉必须创徏新的String对象,但这些过渡对象对pȝ来说是没有实际意义的,只会增加更多的垃圾。避免这U情况可以改用StringBuffer来篏加字W串,因StringBuffer是可变长?它在原有基础上进行扩?不会产生中间对象?/font>
(5)能用基本cd如Int,Long,׃用Integer,Long对象
基本cd变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好用基本变量?/font>
(6)量用静态对象变?/font>
静态变量属于全局变量,不会被GC回收,它们会一直占用内存?/font>
(7)分散对象创徏或删除的旉
集中在短旉内大量创建新对象,特别是大对象,会导致突焉要大量内?JVM在面临这U情冉|,只能q行主GC,以回收内存或整合内存片,从而增加主GC的频率。集中删除对?道理也是一L。它使得H然出现了大量的垃圾对象,I闲I间必然减少,从而大大增加了下一ơ创建新对象时强制主GC的机会?/font>
4.gc与finalizeҎ
⑴gcҎh垃圾回收
使用System.gc()可以不管JVM使用的是哪一U垃圑֛收的法Q都可以hJava的垃圑֛收。需要注意的是,调用System.gc()也仅仅是一个请求。JVM接受q个消息后,q不是立卛_垃圾回收Q而只是对几个垃圾回收法做了加权Q垃圾回收操作Ҏ发生Q或提早发生Q或回收较多而已?/font>
⑵finalizeҎ透视垃圾攉器的q行
在JVM垃圾攉器收集一个对象之?Q一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下QJava提供了缺省机制来l止化该对象释放资源Q这个方法就是finalize()。它的原型ؓQ?/font>
protected void finalize() throws Throwable
在finalize()Ҏq回之后Q对象消失,垃圾攉开始执行。原型中的throws Throwable表示它可以抛ZQ何类型的异常?/font>
因此Q当对象卛_被销毁时Q有旉要做一些善后工作。可以把q些操作写在finalize()Ҏ里?/font>
⑶代码示?br />
5.Java 内存泄漏
׃采用了垃圑֛收机ӞM不可辑֯?对象不再被引?都可以由垃圾攉U程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发h员本来已l对对象使用完毕Q却因ؓ~码的错误而意外地保存了对该对象的引用(q个引用的存在ƈ不是~码人员的主观意?Q从而得该对象一直无法被垃圾回收器回收掉Q这U本来以为可以释放掉的却最l未能被释放的空间可以认为是?#8220;泄漏?#8221;?/font>
考虑下面的程?在ObjStackcM,使用push和popҎ来管理堆栈中的对象。两个方法中的烦?index)用于指示堆栈中下一个可用位|。pushҎ存储Ҏ对象的引用ƈ增加索引?而popҎ减小索引值ƈq回堆栈最上面的元素。在mainҎ?创徏了容量ؓ64的栈,q?4ơ调用pushҎ向它d对象,此时index的gؓ64,随后?2ơ调用popҎ,则index的值变?2,出栈意味着在堆栈中的空间应该被攉。但事实?popҎ只是减小了烦引?堆栈仍然保持着寚w些对象的引用。故32个无用对象不会被GC回收,造成了内存渗漏?/font>
6.如何消除内存泄漏
虽然Java虚拟?JVM)及其垃圾攉?garbage collectorQGC)负责理大多数的内存dQJava软gE序中还是有可能出现内存泄漏。实际上Q这在大型项目中是一个常见的问题。避免内存泄漏的W一步是要弄清楚它是如何发生的。本文介l了~写Java代码的一些常见的内存泄漏陷阱Q以及编写不泄漏代码的一些最佛_c一旦发生了内存泄漏Q要指出造成泄漏的代码是非常困难的。因此本文还介绍了一U新工具Q用来诊断泄漏ƈ指出Ҏ原因。该工具的开销非常,因此可以使用它来L处于生中的pȝ的内存泄漏?/font>
垃圾攉器的作用
虽然垃圾攉器处理了大多数内存管理问题,从而ɾ~程人员的生zd得更L了,但是~程人员q是可能犯错而导致出现内存问题。简单地_GC循环地跟t所有来?#8220;?#8221;对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,q将所有它所能到辄对象标记为活动的。程序只可以操纵q些对象;其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,q么做就是安全的?/font>
虽然内存理可以说是自动化的Q但是这q不能ɾ~程人员免受思考内存管理问题之苦。例如,分配(以及释放)内存M有开销Q虽然这U开销对编Eh员来说是不可见的。创Z太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一?在其他条件相同的情况??/font>
而且Q与本文更ؓ密切相关的是Q如果忘?#8220;释放”先前分配的内存,可能造成内存泄漏。如果程序保留对永远不再使用的对象的引用Q这些对象将会占用ƈ耗尽内存Q这是因动化的垃圾收集器无法证明q些对象不再用。正如我们先前所说的Q如果存在一个对对象的引用,对象p定义为活动的Q因此不能删除。ؓ了确保能回收对象占用的内存,~程人员必须保该对象不能到达。这通常是通过对象字D设|ؓnull或者从集合(collection)中移除对象而完成的。但是,注意Q当局部变量不再用时Q没有必要将其显式地讄为null。对q些变量的引用将随着Ҏ的退自动清除?/font>
概括地说Q这是内存托管语言中的内存泄漏产生的主要原因:保留下来却永q不再用的对象引用?/font>
典型泄漏
既然我们知道了在Java中确实有可能发生内存泄漏Q就让我们来看一些典型的内存泄漏及其原因?/font>
全局集合
在大的应用程序中有某U全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在q些情况下,必须注意理储存库的大小。必L某种机制从储存库中移除不再需要的数据?/font>
q可能有多种ҎQ但是最常见的一U是周期性运行的某种清除d。该d验证储存库中的数据QƈU除M不再需要的数据?/font>
另一U管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零Ӟ该元素就可以从集合中U除了?/font>
~存
~存是一U数据结构,用于快速查扑ַl执行的操作的结果。因此,如果一个操作执行v来很慢,对于常用的输入数据,可以将操作的结果缓存,q在下次调用该操作时使用~存的数据?/font>
~存通常都是以动态方式实现的Q其中新的结果是在执行时d到缓存中的。典型的法是:
查结果是否在~存中,如果在,p回结果?/font>
如果l果不在~存中,p行计?/font>
计出来的l果d到缓存中Q以便以后对该操作的调用可以使用?/font>
该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有相当多的不同输入Q就有相当多的l果存储在缓存中。很明显q不是正的Ҏ?/font>
Z预防q种h潜在破坏性的设计Q程序必ȝ保对于缓存所使用的内存容量有一个上限。因此,更好的算法是Q?/font>
查结果是否在~存中,如果在,p回结果?/font>
如果l果不在~存中,p行计?/font>
如果~存所占的I间q大Q就U除~存最久的l果?/font>
计出来的l果d到缓存中Q以便以后对该操作的调用可以使用?/font>
通过始终U除~存最久的l果Q我们实际上q行了这L假设Q在来Q比L存最久的数据Q最q输入的数据更有可能用到。这通常是一个不错的假设?/font>
新算法将保~存的容量处于预定义的内存范围之内。确切的范围可能很难计算Q因为缓存中的对象在不断变化Q而且它们的引用包|万象。ؓ~存讄正确的大是一w常复杂的dQ需要将所使用的内存容量与索数据的速度加以q?/font>
解决q个问题的另一U方法是使用java.lang.ref.SoftReferencec跟t缓存中的对象。这U方法保证这些引用能够被U除Q如果虚拟机的内存用而需要更多堆的话?/font>
ClassLoader
Java ClassLoaderl构的用ؓ内存泄漏提供了许多可乘之机。正是该l构本n的复杂性ClassLoader在内存泄漏方面存在如此多的问题。ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,q涉及元对象引用Q比如:字段、方法和cR这意味着只要有对字段、方法、类或ClassLoader的对象的引用QClassLoader׃ȝ在JVM中。因为ClassLoader本n可以兌许多cd光态字D,所以就有许多内存被泄漏了?/font>
定泄漏的位|?/font>
通常发生内存泄漏的第一个迹象是Q在应用E序中出COutOfMemoryError。这通常发生在您最不愿意它发生的生产环境中Q此时几乎不能进行调试。有可能是因为测试环境运行应用程序的方式与生产系l不完全相同Q因而导致泄漏只出现在生产中。在q种情况下,需要用一些开销较低的工h监控和查扑ֆ存泄漏。还需要能够无需重启pȝ或修改代码就可以这些工兯接到正在q行的系l上。可能最重要的是Q当q行分析Ӟ需要能够断开工具而保持系l不受干扰?/font>
虽然OutOfMemoryError通常都是内存泄漏的信P但是也有可能应用E序实正在使用q么多的内存;对于后者,或者必d加JVM可用的堆的数量,或者对应用E序q行某种更改Q它用较的内存。但是,在许多情况下QOutOfMemoryError都是内存泄漏的信受一U查明方法是不间断地监控GC的活动,定内存使用量是否随着旉增加。如果确实如此,可能发生了内存泄漏?/font>
教学目的Q?/em> 掌握哈希表的概念作用及意义,哈希表的构造方?/p>
教学重点Q?/em> 哈希表的构造方?/p>
教学隄Q?/em> 哈希表的构造方?/p>
授课内容Q?/em>
一、哈希表的概念及作用
一般的U性表Q树中,记录在结构中的相对位|是随机的,卛_记录的关键字之间不存在确定的关系Q因此,在结构中查找记录旉q行一pd和关键字的比较。这一cL找方法徏立在“比较“的基上,查找的效率依赖于查找q程中所q行的比较次数?
理想的情冉|能直接找到需要的记录Q因此必d记录的存储位|和它的关键字之间徏立一个确定的对应关系fQ每个关键字和l构中一个唯一的存储位|相对应?/p>
哈希表最常见的例子是以学生学号ؓ关键?/font>的成l表Q1号学生的记录位置在第一条,Q0号学生的记录位置在第Q0?..
如果我们以学生姓名ؓ关键字,如何建立查找表,使得Ҏ姓名可以直接扑ֈ相应记录呢?
a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
刘丽 刘宏?/td> 吴军 吴小?/td> 李秋?/td> 陈伟 ... 姓名中各字拼音首字母 ll lhy wj wxy lqm cw ... 用所有首字母~号值相加求?/td> 24 46 33 72 42 26 ... 最值可能ؓ3 最大值可能ؓ78 可放75个学?/p>
用上q得到的数g为对应记录在表中的位|,得到下表Q?br>
成W一 成W?.. 3 ... ... ... 24 刘丽 82 95 25 ... 26 陈伟 ... ... 33 吴军 ... ... 42 李秋?/td> ... ... 46 刘宏?/td> ... ... 72 吴小?/td> ... ... 78 ... 上面q张表即哈希?/font>?/p>
如果来要查李秋梅的成WQ可以用上述Ҏ求出该记录所在位|:
李秋?lqm 12+17+13=42 取表中第42条记录即可?/p>
问题Q如果两个同学分别叫 刘丽 刘兰 该如何处理这两条记录Q?/p>
q个问题是哈希表不可避免的,?font color=#ff0033>冲突
现象Q对不同的关键字可能得到同一哈希地址?/p>
二、哈希表的构造方?/p>
Q、直接定址?/p>
例如Q有一个从1?00岁的人口数字l计表,其中Q年龄作为关键字Q哈?a class=channel_keylink >函数取关键字自n?/p>
地址 01 02 ... 25 26 27 ... 100 q龄 1 2 ... 25 26 27 ... ... 人数 3000 2000 ... 1050 ... ... ... ... ... Q、数字分析法
有学生的生日数据如下Q?/p>
q???/p>
75.10.03
75.11.23
76.03.02
76.07.12
75.04.21
76.02.15
...l分?W一位,W二位,W三位重复的可能性大Q取q三位造成冲突的机会增加,所以尽量不取前三位Q取后三位比较好?/p>
Q、^方取中法
取关键字qx后的中间几位为哈希地址?/p>
Q、折叠法
关键字分割成位数相同的几部分(最后一部分的位数可以不同)Q然后取q几部分的叠加和Q舍去进位)作ؓ哈希地址Q这ҎUCؓ折叠法?/p>
例如Q每一U西文图书都有一个国际标准图书编P它是一?0位的十进制数字,若要以它作关键字建立一个哈希表Q当馆藏书种cM?0,000Ӟ可采用此法构造一个四位数的哈?a class=channel_keylink >函数。如果一本书的编号ؓ0-442-20586-4,则:
5864 5864 4220 0224 +) 04 +) 04 ----------- ----------- 10088 6092 H(key)=0088 H(key)=6092 (a)UM叠加 (b)间界叠加Q、除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余Cؓ哈希地址?/p>
H(key)=key MOD p (p<=m)
Q、随机数?/p>
选择一个随机函敎ͼ取关键字的随?a class=channel_keylink >函数gؓ它的哈希地址Q即
H(key)=random(key) ,其中random为随?a class=channel_keylink >函数。通常用于关键字长度不{时采用此法?/p>
三、ȝ
哈希表的优缺?/p>
四、作?/p>
预习如何处理冲突及哈希表的查找?/p>
http://www.bc-cn.net/Article/kfyy/sjjg/200411/261.html
哈希表(二)教学目的Q?/em> 掌握哈希表处理冲H的Ҏ及哈希表的查扄?/p>
教学重点Q?/em> 哈希表处理冲H的Ҏ
教学隄Q?/em> 开攑֮址?/p>
授课内容Q?/em>
一、复习上ơ课内容
什么是哈希表?如何构造哈希表Q?/p>
提出问题Q如何处理冲H?
二、处理冲H的Ҏ
成W一 成W?.. 3 ... ... ... 24 刘丽 82 95 25 ... 26 陈伟 ... ... 33 吴军 ... ... 42 李秋?/td> ... ... 46 刘宏?/td> ... ... 72 吴小?/td> ... ... 78 ... 如果两个同学分别?刘丽 刘兰Q当加入刘兰Ӟ地址24发生了冲H,我们可以以某U规律用其它的存储位置Q如果选择的一个其它位|仍有冲H,则再选下一个,直到扑ֈ没有冲突的位|。选择其它位置的方法有Q?/p>
Q、开攑֮址?/p>
Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
其中m长,di为增量序?/p>
如果di值可能ؓ1,2,3,...m-1Q称U性探再散列?/p>
如果di取值可能ؓ1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2)
U?font color=#ff0033>二次探测再散?/font>?/p>
如果di取值可能ؓ伪随机数?/font>。称伪随机探再散列?/p>
例:在长度ؓ11的哈希表中已填有关键字分别ؓ17,60,29的记录,现有W四个记录,其关键字?8Q由哈希函数得到地址?Q若用线性探再散列Q如下:
0 1 2 3 4 5 6 7 8 9 10 60 17 29 (a)插入?/p>
0 1 2 3 4 5 6 7 8 9 10 60 17 29 38 (b)U性探再散列
0 1 2 3 4 5 6 7 8 9 10 60 17 29 (c)二次探测再散?/p>
0 1 2 3 4 5 6 7 8 9 10 38 60 17 29 (d)伪随机探再散列
伪随机数列ؓ9,5,3,8,1...
Q、再哈希?/p>
当发生冲H时Q用第二个、第三个、哈?a class=channel_keylink >函数计算地址Q直到无冲突时。缺点:计算旉增加?/p>
Q、链地址?/p>
所有关键字为同义词的记录存储在同一U性链表中?/p>
Q、徏立一个公共溢出区
假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表Q另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录?/p>
三、哈希表的查?/p>
//开攑֮址哈希表的存储l构
int hashsize[]={997,...};
typedef struct{
ElemType *elem;
int count;
int sizeindex;
}HashTable;
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
Status SearchHash(HashTable H,KeyType K,int &p,int &c){
p=Hash(K);
while(H.elem[p].key!=NULLKEY && !EQ(K,H.elem[p].key))
collision(p,++c);
if(EQ(K,H.elem[p].key)
return SUCCESS;
else return UNSUCCESS;
}
Status InsertHash(HashTable &H,EleType e){
c=0;
if(SearchHash(H,e.key,p,c))
return DUPLICATE;
else if(c<hashsize[H.sizeindex]/2){
H.elem[p]=e; ++H.count; return OK;
}
else RecreateHashTable(H);
}
四、ȝ
处理冲突的要求是什么?
cd(Class diagram)p多(静态)说明性的模型元素Q例如类、包和它们之间的关系Q这些元素和它们的内容互相连接)l成。类囑֏以组l在Qƈ且属于)包中Q仅昄特定包中的相兛_宏V?/font>
cd(Class diagram)是最常用?a target=_new>UML图,昄出类、接口以及它们之间的静态结构和关系Q它用于描述pȝ的结构化设计?/font>
cd(Class diagram)最基本的元素是cL者接口?/font>
cd通常包含如下的内?/strong>
c?br> 接口
协作
关系
同其他的图一Pcd也可以包含注解和限制?/font>
cd中也可以包含包和子系l?/font>Q这两者用来将元素分组。有时后你也可以类的实例放到类图中?/font>
注:lg?/font>和分布图和类囄|虽然他们不包含类而是分别包含lg和节炏V?/font>
你通常通过下面三种方式使用cdQ?/p>
1Qؓpȝ词汇建模?br> 为系l的词汇建模实际上是从词汇表中发现类Q发现它的责仅R?/p>
2Q模型化单的协作
协作是指一些类、接口和其他的元素一起工作提供一些合作的行ؓQ这些行Z是简单地元素加能得到的。例如:当你Z个分布式的系l中的事务处理过E徏模型Ӟ你不可能只通过一个类来明白事务是怎样q行的,事实上这个过E的执行涉及Cpd的类的协同工作。用类图来可视化这些类和他们的关系?/p>
3Q模型化一个逻辑数据?/font>模式
惌模式是概念上设计数据库的蓝图。在很多领域Q你想保存持久性数据到关系数据库活面向对象的数据库。你可以用类图ؓq些数据库模式徏立模型?/p>
1. c(ClassQ?/strong>
一般包?个组成部分。第一个是cdQ第二个是属性(attributesQ;W三个是该类提供的方法( cȝ性质可以攑֜W四部分Q如果类中含有内部类Q则会出现第五个l成部分Q。类名部分是不能省略的,其他l成部分可以省略?/p>
cd书写规范Q正体字说明cL可被实例化的Q斜体字说明cMؓ抽象cR?/p>
属性和Ҏ书写规范Q修饰符 [描述信息] 属性、方法名U?[参数] [Q返?a target=_new>cd|cd]
属性和Ҏ之前可附加的可见性修饰符Q?/p>
加号Q?Q表CpublicQ减P-Q表CprivateQ?可CprotectedQ省略这些修饰符表示hpackageQ包Q别的可见性?/p>
如果属性或Ҏh下划U,则说明它是静态的?/p>
描述信息使用 << 开头和使用 >> l尾?/p>
cȝ性质是由一个属性、一个赋值方法和一个取值方法组成。书写方式和ҎcM?/p>
2. 包(PackageQ?/strong>
包是一U常规用途的l合机制。UML中的一个包直接对应于Java中的一个包。在Java中,一个包可能含有其他包、类或者同时含有这两者。进行徏模时Q通常使用逻辑性的包,用于Ҏ型进行组l;使用物理性的包,用于转换成系l中的Java包。每个包的名U对q个包进行了惟一性的标识?/p>
3. 接口QInterfaceQ?/strong>
接口是一pd操作的集合,它指定了一个类所提供的服务。它直接对应于Java中的一个接口类型。接口的表示有大概两U方式。具体画法见下例Q?/p>
4. 关系
常见的关pLQ一般化关系QGeneralizationQ,兌关系QAssociationQ,聚合关系QAggregationQ,合成关系QCompositionQ,依赖关系QDependencyQ?/p>
其中Q聚合关p(AggregationQ,合成关系QCompositionQ属于关联关p(AssociationQ?/p>
一般关p表Cؓl承或实现关p?is a)Q关联关p表Cؓ变量(has a )Q依赖关p表Cؓ函数中的参数(use a)?/p>
一般化关系Q表CZؓcMcM间的l承关系Q接口与接口之间的承,cd接口的实现关pR?br> 表示ҎQ?用一个空心箭_实线Q箭头指向父cR或I心头Q虚U,如果父类是接口?/p>
兌关系Q类与类之间的联接,它一个类知道另一个类的属性和Ҏ?br> 表示ҎQ用 实线Q箭_ 头指向被用的cR?/p>
聚合关系Q是兌关系的一U,是强的关联关pR聚合关pL整体和个体的关系。关联关pȝ两个cd于同一层次上,啊聚合关pM个类处于不同的层ơ,一个是整体Q一个是部分?br> 表示ҎQ空心菱形+实线Q箭_头指向部分?/p>
合成关系Q是兌关系的一U,是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期Q合成关pM能共享?br> 表示ҎQ实心菱形+实线Q箭_
依赖关系Q是cMcM间的q接Q表CZ个类依赖于另一个类的定义。例如如果A依赖于BQ则B体现为局部变量,Ҏ的参数、或静态方法的调用?br> 表示ҎQ虚U+头
通用建模技?/strong>
没有cL单独存在的,他们通常和别的类协作Q创造比单独工作更大的语义。因此,除了捕获pȝ的词汇以外,q要注意力集中到这些类是如何在一起工作的。用类图来表达q种协作?/p>
l 定你徏模的机制。机制代表了部分你徏模的pȝ的一些功能和行ؓQ这些功能和行ؓ是一l类、接口和其他事物怺作用的结果?/p>
l 对于每个机制Q确定类、接口和其他的参与这个协作的协作。同时确定这些事物之间的关系?/p>
l 用场景来预排q些事物Q沿着q条路你发现模型中忽略的部分和定义错误的部分?/p>
l 定用这些事物的内容来填充它们。对于类Q开始于获得一个责任(cȝ职责Q,然后Q将它{化ؓ具体的属性和Ҏ?/p>
?-1是一个自?a target=_new>机器?/font>的类图。这张的囄点聚集那些让机器人在路上行走的机制对应的cM。你可以发现一个虚cMotor和两个从它派生出来的c:SteeringMotor和MainMotor。这两个c都从它的父亲Motorl承了五个方法。这两个cd是另一个类Driver的一部分。类PathAgent和Driver有一??的关p,和CollisionSensor?对n的关pR?/p>
在这个系l中其实q有很多其他的类Q但q张囄重点是放在那些将机器人移动的cM的。在其他的图中你可能也会看到q些cR通过焦Ҏ在不通的功能上,可以获得从不通的角度Ҏ个系l的认识Q最l达到认识整个系l。
很多pȝ都是有持久性数据的Q也是说要这些数据保存到数据库中以便下一ơ用。通常你会使用关系型数据库或面向对象的数据库,或其它类型的数据库来保存数据。UML很适合为逻辑数据库模式徏模?/p>
UML的类图是E-R图(为逻辑数据库徏模的通用工具Q的集Q尽E-R囄重点是数据,cd的扩展允许模型化行ؓ。在物理数据库中q些逻辑操作一半{化ؓ触发器或存储q程?/p>
l 定那些状态比其生命周期要长的cR?br> l 创徏一张包含这些类的图Q标记它们ؓ持久性的?br> l 详细定义它们的属性?br> l 对于使得物理数据库设?/font>复杂的模式如Q@环关pR??关系、N元关p,考虑创徏中间抽象来得逻辑l构复杂?br> l 详细定义q些cȝ操作Q特别是那些讉K数据和涉及数据完整性的Ҏ?br> l 如果可能的话使用工具来将你的逻辑设计转化为物理设计?/p>
建模是重要的Q但要记住的是对于开发组来说软g才是主要的品,而不是图。当Ӟd的主要目的是Z更好地理解系l,预测什么时候可以提供什么样的Y件来满用户的需要。基于这个理由,让你ȝ囑֯开发有指导意义是很重要的?/p>
某些时候,使用UML。你的模型ƈ不能直接映射成ؓ代码。例如,如果你在使用zd?/font>Z个商业过E徏模,很多zd实际上涉及h而不?a target=_new>计算?/font>?/p>
很多时候,你创建的囑Ş可以被映成Z码。UMLq不是专门ؓ面向对象的语a设计的,它支持多U语aQ但使用面向对象的语a会更直观些,特别是类囄映射Q它的内容可以直接映成?a target=_new>面向对象语言的内宏V如QCQ+QSMALLTALK、ADA、ObjectPascal、Eiffel和Forte。UMLq支持如Visual Basicq样的面向对象的语言?/p>
正向工程Q是从图C码的q程。通过Ҏ中特定语a的映可以从UML的图得到该语a的代码。正向工E会丢失信息Q这是因为UML比Q何一U?a target=_new>E序语言的语义都丰富。这也正是ؓ什么你需要UML模型的原因。结构特性、协作、交互等可以通过UML直观地表辑և来,使用代码׃是那么明显了?/p>
对类囄正向工程Q?/p>
l 选择图形映到哪一U程序语a?br> l Ҏ你选择的语a的语义,你可能要对用某写UML的特性加以限制。例如:UML允许你用多重承,而SmallTalk只允怸重ѝ?br> l 使用标记值来指定比的目的语言。你可以在类U进行也可以在协作或包的层次上进行?br> l 使用工具来对你的模型q行正向工程?/p>
反向工程Q反向工E是从代码到模型的过E?/p>
q行反向工程Q?/p>
l 定你的程序语a的代码反向成模型的规则?br> l 使用工具QRose C++ AnalyzerQ进行反向工E?/p>
提示和技?/strong>
一个结构化好的cdQ?/p>
l 焦点攑֜pȝ静?a target=_new>设计视图的一个方面?br> l 只包含ؓ了理解该斚w而应该存在的元素?br> l 提供_的信息来理解该图?br> l 不让读者生错误的信息?/p>
当你ȝ囄时候:
l l它起一个名字,q个名字能表辄囄用途?br> l 用最的交叉U来l织它的元素?
一Qrun()和start()
CZ1Q?
public class ThreadTest extends Thread
{
public void run()
{
for(int i=0;i<10;i++)
{
System.out.print(" " + i);
}
}
public static void main(String[] args)
{
new ThreadTest().start();
new ThreadTest().start();
}
}
q是个简单的多线E程序。run()和start()是大安很熟悉的两个Ҏ。把希望q行处理?br>代码都放在run()中;stat()用于自动调用run()Q这是JAVA 的内在机制规定的。ƈ且run()
的访问控制符必须是publicQ返回值必LvoidQ这U说法不准确Qrun()没有q回|Qrun()
不带参数?br>q些规定惛_大家都早已知道了Q但你是否清楚ؓ什么run Ҏ必须声明成这L形式Q这?br>及到JAVA 的方法覆盖和重蝲的规定。这些内容很重要Q请读者参考相兌料?/p>
二:关键字synchronized
有了synchronized 关键字,多线E程序的q行l果变得可以控制。synchronized 关键
字用于保护共享数据。请大家注意"׃n数据"Q你一定要分清哪些数据是共享数据,JAVA 是面?br>对象的程序设计语aQ所以初学者在~写多线E程序时Q容易分不清哪些数据是共享数据。请看下
面的例子Q?
CZ2Q?
public class ThreadTest implements Runnable
{ public synchronized void run()
{
for(int i=0;i<10;i++)
{
System.out.print(" " + i);
}
}
public static void main(String[] args)
{
Runnable r1 = new ThreadTest();
Runnable r2 = new ThreadTest();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
在这个程序中Qrun()被加上了synchronized 关键字。在main Ҏ中创Z两个U程。你
可能会认为此E序的运行结果一定ؓQ? 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。但?br>错了Q这个程序中synchronized 关键字保护的不是׃n数据Q其实在q个E序中synchronized
关键字没有vCQ何作用,此程序的q行l果是不可预先确定的Q。这个程序中的t1,t2 是两?nbsp; 对象Qr1,r2Q的U程。JAVA
是面向对象的E序设计语言Q不同的对象的数据是不同的,r1,r2 ?br>各自的run()ҎQ而synchronized 使同一个对象的多个U程Q在某个时刻只有其中的一个线E?br>可以讉Kq个对象的synchronized 数据。每个对象都有一?锁标?Q当q个对象的一个线E?br>讉Kq个对象的某个synchronized 数据Ӟq个对象的所有被synchronized 修饰的数据将?br>上锁Q因?锁标?被当前线E拿CQ,只有当前U程讉K完它要访问的synchronized 数据
Ӟ当前U程才会释放"锁标?Q这样同一个对象的其它U程才有Z讉Ksynchronized 数据?br>CZ3Q?
public class ThreadTest implements Runnable
{
public synchronized void run()
{
for(int i=0;i<10;i++)
{
System.out.print(" " + i);
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start(); t2.start();
}
}
如果你运?000 ơ这个程序,它的输出l果也一定每ơ都是:0 1 2 3 4 5 6 7 8 9 0 1
2 3 4 5 6 7 8 9。因里的synchronized 保护的是׃n数据。t1,t2 是同一个对象(rQ?
的两个线E,当其中的一个线E(例如Qt1Q开始执行run()ҎӞ׃run()受synchronized
保护Q所以同一个对象的其他U程Qt2Q无法访问synchronized ҎQrun ҎQ。只有当t1
执行完后t2 才有Z执行?br>CZ4Q?
public class ThreadTest implements Runnable
{
public void run()
{
synchronized(this)
{
for(int i=0;i<10;i++)
{
System.out.print(" " + i);
}
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
q个E序与示? 的运行结果一栗在可能的情况下Q应该把保护范围~到最,可以用示?br>4 的Ş式,this 代表"q个对象"。没有必要把整个run()保护hQrun()中的代码只有一个for 循环Q所以只要保护for 循环可以了?br>CZ5Q?
public class ThreadTest implements Runnable
{
public void run()
{
for(int k=0;k<5;k++)
{
System.out.println(Thread.currentThread().getName()
+ " : for loop : " + k); }
synchronized(this)
{
for(int k=0;k<5;k++)
{
System.out.println(Thread.currentThread().getName()
+ " : synchronized for loop : " + k);
}
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1_name");
Thread t2 = new Thread(r,"t2_name");
t1.start();
t2.start();
}
}
q行l果Q?
t1_name : for loop : 0
t1_name : for loop : 1
t1_name : for loop : 2
t2_name : for loop : 0
t1_name : for loop : 3
t2_name : for loop : 1
t1_name : for loop : 4
t2_name : for loop : 2
t1_name : synchronized for loop : 0
t2_name : for loop : 3
t1_name : synchronized for loop : 1
t2_name : for loop : 4
t1_name : synchronized for loop : 2
t1_name : synchronized for loop : 3
t1_name : synchronized for loop : 4
t2_name : synchronized for loop : 0
t2_name : synchronized for loop : 1
t2_name : synchronized for loop : 2
t2_name : synchronized for loop : 3
t2_name : synchronized for loop : 4
W一个for 循环没有受synchronized 保护。对于第一个for 循环Qt1,t2 可以同时讉K?br>q行l果表明t1 执行Ck=2 Ӟt2 开始执行了。t1 首先执行完了W一个for 循环Q此时t2
q没有执行完W一个for 循环Qt2 刚执行到k=2Q。t1 开始执行第二个for 循环Q当t1 的第?br>个for 循环执行到k=1 Ӟt2 的第一个for 循环执行完了。t2 惛_始执行第二个for 循环Q但
׃t1 首先执行了第二个for 循环Q这个对象的锁标志自然在t1 手中Qsynchronized Ҏ?br>执行权也pCt1 手中Q,在t1 没执行完W二个for 循环的时候,它是不会释放锁标志的。所
以t2 必须{到t1 执行完第二个for 循环后,它才可以执行W二个for 循环?/p>
三:sleep()
CZ6Q?
public class ThreadTest implements Runnable
{
public void run()
{
for(int k=0;k<5;k++)
{
if(k == 2)
{
try
{
Thread.currentThread().sleep(5000);
}
catch(Exception e)
{}
}
System.out.print(" " + k);
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t = new Thread(r);
t.start();
}
}
sleep Ҏ会当前的线E暂停执行一定时_l其它线E运行机会)。读者可以运行示?Q?
看看l果明白了。sleep Ҏ会抛出异常,必须提供捕获代码?br>CZ7Q?
public class ThreadTest implements Runnable
{
public void run()
{ for(int k=0;k<5;k++)
{
if(k == 2)
{
try
{
Thread.currentThread().sleep(5000);
}
catch(Exception e)
{}
}
System.out.println(Thread.currentThread().getName()
+ " : " + k);
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1_name");
Thread t2 = new Thread(r,"t2_name");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
t1 被设|了最高的优先U,t2 被设|了最低的优先U。t1 不执行完Qt2 没有机会执行?br>但由于t1 在执行的中途休息了5 U中Q这使得t2 有Z执行了。读者可以运行这个程序试试看?br>CZ8Q?
public class ThreadTest implements Runnable
{
public synchronized void run()
{
for(int k=0;k<5;k++)
{
if(k == 2)
{
try
{
Thread.currentThread().sleep(5000);
}
catch(Exception e)
{} }
System.out.println(Thread.currentThread().getName()
+ " : " + k);
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1_name");
Thread t2 = new Thread(r,"t2_name");
t1.start();
t2.start();
}
}
误者首先运行示? E序Q从q行l果上看Q一个线E在sleep 的时候,q不会释放这个对
象的锁标志?/p>
四:join()
CZ9Q?
public class ThreadTest implements Runnable
{
public static int a = 0;
public void run()
{
for(int k=0;k<5;k++)
{
a = a + 1;
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t = new Thread(r);
t.start();
System.out.println(a);
}
}
请问E序的输出结果是5 吗?{案是:有可能。其实你很难遇到输出5 的时候,通常情况下都
不是5。这里不讲解Z么输出结果不?Q我要讲的是Q怎样才能让输出结果ؓ5Q其实很单,
join()Ҏ提供了这U功能。join()ҎQ它能够使调用该Ҏ的线E在此之前执行完毕?br>把示? 的main()Ҏ该成如下q样Q?public static void main(String[] args) throws Exception
{
Runnable r = new ThreadTest();
Thread t = new Thread(r);
t.start();
t.join();
System.out.println(a);
}
q时Q输出结果肯定是5Qjoin()Ҏ会抛出异常,应该提供捕获代码。或留给JDK 捕获?br>CZ10Q?
public class ThreadTest implements Runnable
{
public void run()
{
for(int k=0;k<10;k++)
{
System.out.print(" " + k);
}
}
public static void main(String[] args) throws Exception
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t1.join();
t2.start();
}
}
q行q个E序Q看看结果是否与CZ3 一P
五:yield()
yield()Ҏ与sleep()Ҏ怼Q只是它不能qh定线E暂停多长时间。按照SUN ?br>说法Qsleep
Ҏ可以使低优先U的U程得到执行的机会,当然也可以让同优先和高优先U的U程有执行的Z。而yield()Ҏ只能使同优先U的U程有执行的Z?br>CZ11Q?
public class ThreadTest implements Runnable
{
public void run()
{ for(int k=0;k<10;k++)
{
if(k == 5 && Thread.currentThread().getName().equals("t1"))
{
Thread.yield();
}
System.out.println(Thread.currentThread().getName()
+ " : " + k);
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1");
Thread t2 = new Thread(r,"t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
输出l果Q?
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t2 : 0
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9
多次q行q个E序Q输Z是一栗这说明Qyield()Ҏ不会使不同优先的线E有执行?br>Z?/p>
六:wait(),notify(),notifyAll()
首先说明Qwait(),notify(),notifyAll()q些Ҏ由java.lang.Object cL供,而上?br>讲到的方法都是由java.lang.Thread cL供(Thread cdCRunnable 接口Q?br>wait(),notify(),notifyAll()q三个方法用于协调多个线E对׃n数据的存取,所以必
dsynchronized 语句块内使用q三个方法。先看下面了例子Q?
CZ12Q?
public class ThreadTest implements Runnable
{
public static int shareVar = 0;
public synchronized void run()
{
if(shareVar == 0)
{
for(int i=0;i<10;i++)
{
shareVar++ ;
if(shareVar == 5)
{
try
{
this.wait();
}
catch(Exception e)
{}
}
}
}
if(shareVar != 0)
{
System.out.print(Thread.currentThread().getName());
System.out.println(" shareVar = " + shareVar);
this.notify();
}
}
public static void main(String[] args)
{
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1"); Thread t2 = new Thread(r,"t2");
t1.start();
t2.start();
}
}
q行l果Q?
t2 shareVar = 5
t1 shareVar = 10
t1U程最先执行。由于初始状态下shareVar ?Qt1 shareVar q箋?Q当shareVar
的gؓ5 Ӟt1 调用wait()ҎQt1 处于休息状态,同时释放锁标志。这时t2 得到了锁标志
开始执行,shareVar 的值已l变?Q所以t2 直接输出shareVar 的|然后再调用notify()
Ҏ唤醒t1。t1 接着上次休息前的q度l箋执行Q把shareVar 的g直加?0Q由于此?br>shareVar 的g?Q所以t1 输出此刻shareVar 的|然后再调用notify()ҎQ由?br>此刻已经没有{待锁标志的U程Q所以此调用语句不vM作用?br>q个E序单的C了wait(),notify()的用法,读者还需要在实践中l摸索?/p>
七:关于U程的补?br>~写一个具有多U程能力的程序可以承Thread c,也可以实现Runnable 接口。在q两?br>Ҏ中如何选择呢?从面向对象的角度考虑Q作者徏议你实现Runnable 接口。有时你也必d?br>Runnable 接口Q例如当你编写具有多U程能力的小应用E序的时候?br>U程的调度:
一个Thread 对象在它的生命周期中会处于各U不同的状态,上图形象地说明了q点。wa in
调用start()Ҏ使线E处于可q行状态,q意味着它可以由JVM 调度q执行。这q不意味
着U程׃立即q行?br>实际上,E序中的多个U程q不是同时执行的。除非线E正在真正的多CPU 计算机系l上执行Q?
否则U程使用单CPU 必须轮流执行。但是,׃q发生的很快Q我们常常认些线E是同时执行
的?br>JAVA q行时系l的计划调度E序是抢占性的。如果计划调度程序正在运行一个线Eƈ且来了另
一个优先更高的线E,那么当前正在执行的线E就被暂时终止而让更高优先U的U程执行?br>JAVA 计划调度E序不会Z当前U程h同样优先U的另一个线E去抢占当前的线E。但是,
管计划调度E序本n没有旉片(卛_没有l相同优先的线E以执行用的旉片)Q但?br>Thread cMؓ基础的线E的pȝ实现可能会支持时间片分配。这依赖具体?作系l,Windows ?br>UNIX 在这个问题上的支持不会完全一栗?br>׃你不能肯定小应用E序运行在什?作系l上Q因此你不应该编写出依赖旉片分配的
E序。就是说Q应该用yield Ҏ以允许相同优先的线E有Z执行而不是希望每一个线E都自动得到一DCPU 旉片?br>Thread cL供给你与pȝ无关的处理线E的机制。但是,U程的实际实现取决于JAVA q行
所在的*作系l。因此,U程化的E序实是利用了支持U程?作系l?br>当创建线E时Q可以赋予它优先U。它的优先高Q它p能媄响运行系l。JAVA q行pȝ
使用一个负责在所有执行JAVA E序内运行所有存在的计划调度E序。该计划调度E序实际上?br>一个固定优先的算法来保证每个E序中的最高优先的线E得到CPU――允许最高优先的线E?br>在其它线E之前执行?br>对于在一个程序中有几个相同优先的线E等待执行的情况Q该计划调度E序循环地选择它们Q?
当进行下一ơ选择旉择前面没有执行的线E,h相同优先U的所有的U程都受到^{的对待?br>较低优先U的U程在较高优先的线E已l死亡或者进入不可执行状态之后才能执行?br>l箋讨论wait(),notify(),notifyAll()Q?
当线E执行了对一个特定对象的wait()调用Ӟ那个U程被放C那个对象相关的等待池中?br>此外Q调用wait()的线E自动释攑֯象的锁标志?br>可以调用不同的wait()Qwait() 或wait(long timeout)
对一个特定对象执行notify()调用Ӟ从对象的等待池中移C个Q意的U程Qƈ攑ֈ?br>标志{待池中Q那里的U程一直在{待Q直到可以获得对象的锁标志。notifyAll()Ҏ从对象
{待池中U走所有等待那个对象的U程q放到锁标志{待池中。只有锁标志{待池中的线E能获取
对象的锁标志Q锁标志允许U程从上ơ因调用wait()而中断的地方开始l运行?br>在许多实Cwait()/notify()机制的系l中Q醒来的U程必定是那个等待时间最长的U程?br>然而,在Java 技术中Qƈ不保证这炏V?br>注意Q不是否有U程在等待,都可以调用notify()。如果对一个对象调用notify()ҎQ?
而在q个对象的锁标志{待池中q没有线E,那么notify()调用不起Q何作用?br>在JAVA 中,多线E是一个神奇的主题。之所以说?奇"Q是因ؓ多线E程序的q行l果?br>可预,但我们又可以通过某些Ҏ控制多线E程序的执行。要想灵zM用多U程Q读者还需要大
量实c?br>另外Q从JDK 1.2 开始,SUN ׃使用resume(),stop(),suspend()了?/p>
?a mce_>http://vegetable318.bokee.com
(1)泛化(Generalization)
(2)兌(Association)
(3)依赖(Dependency)
(4)聚合(Aggregation)
UML图与应用代码例子:
[具体表现]
父类 父类实例Qnew 子类()
[UML图](?.1)
?.1 AnimalcMTigerc?Dogcȝ泛化关系
[代码表现]
[具体表现]
依赖关系表现?font color=#ff0000>局部变?/font>Q?font color=#ff0000>Ҏ的参?/font>Q以及对静态方法的调用
[现实例子]
比如说你要去拧螺丝,你是不是要借助(也就是依?Z刀(Screwdriver)来帮助你完成拧螺?screw)的工?/p>
[UML表现](?.2)
?.2 PersoncMScrewdrivercȝ依赖关系
[代码表现]
[具体表现]
兌关系是?font color=#ff0000>实例变量
[现实例子]
比如客户和订单,每个订单对应特定的客P每个客户对应一些特定的订单Q再例如公司和员工,每个公司对应一些特定的员工Q每个员工对应一特定的公?/p>
[UML图] (?.3)
?.3 公司和员工的兌关系
[具体表现]
与关联关pMP聚合关系也是通过实例变量来实现这样关pȝ。关联关pd聚合关系来语法上是没办法区分的,?font color=#ff0000>语义
[兌与聚合的区别]
(1)兌关系所涉及的两个对象是处在同一个层ơ上?/span>。比如h和自行R是一U关联关p,而不是聚合关p,因ؓZ是由自行车组成的?br>聚合关系涉及的两个对象处于不q等的层ơ上Q一个代表整体,一个代表部?/span>。比如电脑和它的昄器、键盘、主板以及内存就是聚集关p,因ؓL是电脑的l成部分?br>(2)对于h聚集关系Q尤其是集关p)的两个对象,整体对象会制U它的组成对象的生命周期。部分类的对象不能单独存在,它的生命周期依赖于整体类的对象的生命周期Q当整体消失Q部分也随之消失。比如张三的电脑被偷了,那么电脑的所有组件也不存在了Q除非张三事先把一些电脑的lgQ比如硬盘和内存Q拆了下来?/p>
[UML图](?.4)
?.3 电脑和组件的聚合关系
[代码表现]
转CSDN
转自http://www.aygfsteel.com/cheneyfree/archive/2007/05/24/119670.html
]]>