大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
如何写出好的Java代码
创新互联公司网站建设公司是一家服务多年做网站建设策划设计制作的公司,为广大用户提供了网站制作、做网站,成都网站设计,1元广告,成都做网站选创新互联公司,贴合企业需求,高性价比,满足客户不同层次的需求一站式服务欢迎致电。
1. 优雅需要付出代价。
从短期利益来看,对某个问题提出优雅的解决方法,似乎可能花你更多的时间。但当它终于能够正确执行并可轻易套用于新案例中,不需要花上数以时计,甚至以天计或以月计的辛苦代价时,你会看得到先前所花功夫的回报(即使没有人可以衡量这一点)。这不仅给你一个可更容易开发和调试的程序,也更易于理解和维护。这正是它在金钱上的价值所在。这一点有赖某种人生经验才能够了解,因为当你努力让某一段程序代码变得比较优雅时,你并不是处于一种具生产力的状态下。但是,请抗拒那些催促你赶工的人们,因为那么做只会减缓你的速度罢了。
2. 先求能动,再求快。
即使你已确定某段程序代码极为重要,而且是系统的重要瓶颈,这个准则依然成立。尽可能简化设计,让系统能够先正确动作。如果程序的执行不够快,再量测其效能。几乎你总是会发现,你所认为的”瓶颈”其实都不是问题所在。把你的时间花在刀口上吧。
3. 记住”各个击破”的原理。
如果你所探讨的问题过于混杂,试着想像该问题的基本动作会是什么,并假设这一小块东西能够神奇地处理掉最难的部分。这”一小块”东西其实就是对象–请撰写运用该对象的程序代码,然后检视对象,并将其中困难的部分再包装成其他对象,依此类推。
4. 区分class开发者和class使用者(使用端程序员)。
Class 使用者扮演着”客户”角色,不需要(也不知道)class的底层运作方式。Class开发者必须是class设计专家,并撰写class,使它能够尽可能被大多数新手程序员所用,而且在程序中能够稳当执行。一套程序库只有在具备通透性的情况下,使用起来才会容易。
5.当你撰写class时,试着给予明了易懂的名称,减少不必要的注解。
你给客户端程序员的接口,应该保持概念上的单纯性。不了这个目的,当函数的重载(overloading)适合制作出直觉、易用的接口时,请善加使用。
6. 也必你的分析和设计必须让系统中的classes保持最少,须让其Public interfaces保持最少,以及让这些classes和其他classes之间的关联性( 尤其是base classes)保持最少。
如果你的设计所得结果更甚于此,请问问自己,是否其中每一样东西在整个程序生命期中都饶富价值?如果并非如此,那么,维护它们会使你付出代价。开发团队的成员都有不维护”无益于生产力提升”的任何东西的倾向;这是许多设计方法无法解释的现象。
7. 让所有东西尽量自动化。先撰写测试用的程序代码(在你撰写class之前),并让它和class结合在一起。请使用makefile或类似工具,自动进行测试动作。
通过这种方式,只要执行测试程序,所有的程序变动就可以自动获得验证,而且可以立即发现错误。由于你知道的测试架构所具备的安全性,所以当你发现新的需求时,你会更勇于进行全面修改。请记住,程序语言最大的改进,是来自型别检查、异常处理等机制所赋予的内置测试动作。但这些功能只能协助你到达某种程度。开发一个稳固系统时,你得自己验证自己的classes或程序的性质。
8. 在你撰写class之前先写测试码,以便验证你的class 是否设计完备。如果你无法撰写测试码,你便无法知道你的class 的可能长相。撰写测试码通常能够显现出额外的特性(features)或限制 ( constraints)__它们并不一定总是能够在分析和设计过程中出现。测试码也可做为展示class 用法的示例程序。
9. 所有软件设计上的问题,都可以通过”引入额外的概念性间接层(conceptual indirection)”加以简化。这个软件工程上的基础法则是抽象化概念的根据,而抽象化概念正是面向对象程序设计的主要性质。
10. 间接层(indirection)应该要有意义(和准则-9致)。
这里所指的意义可以像”将共用程序代码置于惟一函数”这么简单。如果你加入的间接层(或抽象化、或封装等等)不具意义,它可能就和没有适当的间接层一样糟糕。
11. 让class尽可能微小而无法切割(atomic)。
赋予每个class单一而清楚的用途。如果你的classes或你的系统成长得过于复杂,请将复杂的classes切割成比较简单的几个classes。最明显的一个判断指针就是class的大小:如果它很大,那么它工作量过多的机会就可能很高,那就应该被切割。重新设计class的建议线索是:
1) 复杂的switch语句:请考虑运用多态(Polymorphism)。
2) 许多函数各自处理类型极为不同的动作:请考虑切割为多个不同的(classes)。
12. 小心冗长的引数列(argument lists)。
冗长的引数列会使函数的调用动作不易撰写、阅读、维护。你应该试着将函数搬移到更适当的class中,并尽量以对象为引数。
13. 不要一再重复。
如果某段程序代码不断出现于许多derived class函数中,请将该段程序代码置于某个base class 函数内,然后在derived class函数中调用。这么做不仅可以省下程序代码空间,也可以让修改该段程序代码动作更易于进行。有时候找出此种共通程序代码还可以为接口增加实用功能。
14. 小心switch语句或成串的if-else 子句。
通常这种情况代表所谓的”type-check coding”。也就是说究竟会执行哪一段程序代码,乃是依据某种型别信息来做抉择(最初,确切型别可能不十分明显)。你通常可以使用继承和多态来取代此类程序代码;Polymorphical method (多态函数)的调用会自动执行此类型别检验,并提供更可靠更容易的扩充性。
15. 从设计观点来看,请找出变动的事物,并使它和不变的事物分离。
也就是说,找出系统中可能被你改变的元素,将它们封装于classes中。你可以在《Thinking in Patterns with Java》(可免费下载于 www. BruceEckel. Com)大量学习到这种观念。
16. 不要利用subclassing来扩充基础功能。
如果某个接口元素对class而言极重要,它应该被放在base class 里头,而不是直到衍生(derivation)时才被加入。如果你在继承过程中加入了函数,或许你应该重新思考整个设计。
17. 少就是多。
从class 的最小接口开始妨展,尽可能在解决问题的前提下让它保持既小又单纯。不要预先考量你的class被使用的所有可能方式。一旦class被实际运用,你自然会知道你得如何扩充接口。不过,一旦class被使用后,你就无法在不影响客户程序代码的情况下缩减其接口。如果你要加入更多函数倒是没有问题–不会影响既有的客户程序代码,它们只需重新编译即可。但即使新函数取代了旧函数的功能,也请你保留既有接口。如果你得通过”加入更多引数”的方式来扩充既有函数的接口,请你以新引数写出一个重载化的函数;通过 这种方式就不会影响既有函数的任何客户了。
18. 大声念出你的classes,确认它们符合逻辑。
请base class和derived class 之间的关系是”is-a”(是一种),让class和成员对象之间的关系是”has-a”(有一个)。
19. 当你犹豫不决于继承(inheritance)或合成(组合,composition)时,请你问问自己,是否需要向上转型(upcast)为基础型别。
如果不需要,请优先选择合成(也就是是使用成员对象)。这种作法可以消除”过多基础型别”。如果你采用继承,使用者会认为他们应该可以向上转型。
20. 运用数据成员来表示数值的变化,运用经过覆写的函数(overrided method)来代表行为的变化 。
也就是说,如果你找到了某个 class, 带有一些状态变量,而其函数会依据这些变量值切换不同的行为,那么你或许就应该重新设计,在subclasses 和覆写后的函数(overrided methods)中展现行为止的差异。
21. 小心重载(overloading)。
函数不应该依据引数值条件式地选择执行某一段程序代码。这种情况下你应该撰写两个或更多个重载函数(overloaded methods)
22. 使用异常体系(exception hierarchies)
最好是从Java标准异常体系中衍生特定的classes, 那么,捕捉异常的人便可以捕捉特定异常,之后才捕捉基本异常。如果你加入新的衍生异常,原有的客户端程序仍能通过其基础型别来捕捉它。
23. 有时候简单的聚合(aggregation)就够了。
飞机上的”旅客舒适系统”包括数个分离的元素:座椅、空调、视讯设备等等,你会需要在飞机上产生许多这样的东西。你会将它们声明为Private成员并开发出一个全新的接口吗?不会的,在这个例子中,元素也是Public接口的一部分,所以仍然是安全的。当然啦,简单聚合并不是一个常被运用的解法,但有时候的确是。
24. 试着从客户程序员和程序维护的角度思考。
你的class应该设计得尽可能容易使用。你应该预先考量可能性有的变动,并针对这些 可能的变动进行设计,使这些变动日后可轻易完成。
25. 小心”巨大对象并发症”。
这往往是刚踏OOP领域的过程式(procedural)程序员的一个苦恼,因为他们往往最终还是写出一个过程式程序,并将它们摆放到一个或两个巨大对象中。注意,除了application framework (应用程序框架,译注:一种很特殊的、大型OO程序库,帮你架构程序本体)之外,对象代表的是程序中的观念,而不是程序本身。
26. 如果你得用某种丑陋的方式来达成某个动作,请将丑陋的部分局限在某个class里头。
27. 如果你得用某种不可移植方式来达成某个动作,请将它抽象化并局限于某个class里头。这样一个”额外间接层”能够防止不可移植的部分扩散到整个程序。这种作法的具体呈现便是Bridge设计模式(design pattern)。
28. 对象不应仅仅只用来持有数据。
对象也应该具有定义明确界限清楚的行为。有时候使用”数据对象”是适当的,但只有在通用形容器不适用时,才适合刻意以数据对象来包装、传输一群数据项。
29. 欲从既有的classes身上产生新的classes时,请以组合(composition)为优先考量。
你应该只在必要时才使用继承。如果在组合适用之处你却选择了继承,你的设计就渗杂了非必要的复杂性。
30. 运用继承和函数覆写机制来展现行为上的差异,运用fields(数据成员)来展现状态上的差异。
这句话的极端例子,就是继承出不同的classes表现各种不同的颜色,而不使用”color”field.
31. 当心变异性(variance)。
语意相异的两个对象拥有相同的动作(或说责任)是可能的。OO世界中存在着一种天生的引诱,让人想要从某个class继承出另一个subclass,为的是获得继承带来的福利。这便是所谓”变异性”。但是,没有任何正当理由足以让我们强迫制造出某个其实并不存在的superclass/subclass关系。比较好的解决方式是写出一个共用的base class,它为两个derived classes制作出共用接口–这种方式会耗用更多空间,但你可以如你所盼望地从继承机制获得好处,而且或许能够在设计上获得重大发现。
32. 注意继承上的限制。
最清晰易懂的设计是将功能加到继承得来的class里头;继承过程中拿掉旧功能(而非增加新功能)则是一种可疑的设计。不过,规则可以打破。如果你所处理的是旧有的class程序库,那么在某个class的subclass限制功能,可能会比重新制定整个结构(俾使新class得以良好地相称于旧 class)有效率得多。
33. 使用设计模式(design patterns)来减少”赤裸裸无加掩饰的机能(naked functionality)”。
举个例子,如果你的class只应该产出惟一一个对象,那么请不要以加思索毫无设计的手法来完成它,然后撰写”只该产生一份对象”这样的注解就拍拍屁股走人。请将它包装成singleton(译注:一个有名的设计模式,可译为”单件”)。如果主程序中有多而混乱的”用以产生对象”的程序代码,请找出类似 factory method这样的生成模式(creational patterns),使价钱可用以封装生成动作减少”赤裸裸无加掩饰的机能”(naked functionality)不仅可以让你的程序更易理解和维护,也可以阻止出于好意却带来意外的维护者。
34. 当心”因分析而导致的瘫痪(analysis paralysis)”。
请记住,你往往必须在获得所有信息之前让项目继续前进。而且理解未知部分的最好也最快的方式,通常就是实际前进一步而不只是纸上谈兵。除非找到解决办法,否则无法知道解决办法。Java拥有内置的防火墙,请让它们发挥作用。你在单一class或一组classes中所犯的错误,并不会伤害整个系统的完整性。
35. 当你认为你已经获得一份优秀的分析、设计或实现时,请试着加以演练。
将团队以外的某些人带进来-他不必非得是个顾问不可,他可以是公司其他团队的成员。请那个人以新鲜的姿态审视你们的成果,这样可以在尚可轻易修改的阶段找出问题,其收获会比因演练而付出的时间和金钱代价来得高。实现 (Implementation)
36. 一般来说,请遵守Sun的程序编写习惯。
价钱可以在以下网址找到相关文档:java.sun.com/docs/codeconv/idex.html。本书尽可能遵守这些习惯。众多Java程序员看到的程序代码,都有是由这些习惯构成的。如果你固执地停留在过去的编写风格中,你的(程序代码)读者会比较辛苦。不论你决定采用什么编写习惯,请在整个程序中保持一致。你可以在home.wtal.de/software-solutions/jindent上找到一个用来重排Java程序的免费工具。
37. 无论使用何种编写风格,如果你的团队(或整个公司,那就更好了)能够加以标准化,那么的确会带来显著效果。这代表每个人都可以在其他人不遵守编写风格修改其作品,这是个公平的游戏。标准化的价值在于,分析程序代码时所花的脑力较小,因而可以专心于程序代码的实质意义。
38. 遵守标准的大小写规范。
将 class名称的第一个字母应为大写。数据成员、函数、对象(references)的第一个字母应为小写。所有识别名称的每个字都应该连在一块儿,所有非首字的第一个字母都应该大写。例如: ThisIsAClassName thisIsAMethodOrFieldName 如果你在static final 基本型别的定义处指定了常量初始式(constant initializers),那么该识别名称应该全为大写,代表一个编译期常量。 Packages是个特例,其名称皆为小写,即使非首字的字母亦是如此。域名(org, net, edu 等等)皆应为小写。(这是Java 1.1迁移至Java 2时的一项改变) 。
39、不要自己发明”装饰用的”Private数据成员名称。
通常这种的形式是在最前端加上底线和其他字符,匈牙利命名法(Hungarian notation)是其中最差的示范。在这种命名法中,你得加入额外字符来表示数据的型别、用途、位置等等。仿佛你用的是汇编语言(assembly language)而编译器没有提供任何协肋似的。这样的命名方式容易让人混淆又难以阅读,也不易推行和维护。就让classes和packages来进行”名称上的范
围制定(name scoping)”吧。
40、当你拟定通用性的class时,请遵守正规形式(canonical form)。
包括equals( )、hashCode( )、clone( ) ( 实现出Cloneable),并实现出Comparable和Serialiable等等。
41、对于那些”取得或改变Private数据值”的函数,请使用Java Beans 的”get”、”set”、”is”等命名习惯,即使你当时不认为自己正在撰写Java Bean。这么做不仅可以轻易以Bean的运用方式来运用你的class,也是对此类函数的一种标准命名方式,使读者更易于理解。
42、对于你所拟定的每一个class,请考虑为它加入static public test( ),其中含有class功能测试码。
你不需要移除该测试就可将程序纳入项目。而且如果有所变动,你可以轻易重新执行测试。这段程序代码也可以做为class的使用示例。
43、有时候你需要通过继承,才得以访问base class的protected成员。
这可能会引发对多重基类(multiple base types)的认识需求。如果你不需要向上转型,你可以先衍生新的class发便执行protected访问动作,然后在”需要用到上述 protected成员”的所有classes中,将新class声明为成员对象,而非直接继承。
44、避免纯粹为了效率考量而使用final函数。
只有在程序能动但执行不够快时,而且效能量测工具(profiler)显示某个函数的调用动作成为瓶颈时,才使用final函数。
45、如果两个classes因某种功能性原因而产生了关联(例如容器containers和迭代器iterators),那么请试着让其中某个class成为另一个class 的内隐类(inner class)。
这不仅强调二者间的关联,也是通过”将class名称嵌套置于另一个class 内”而使同一个class 名称在单一Package中可被重复使用。Java 容器库在每个容器类中都定义了一个内隐的(inner)Iterator class,因而能够提供容器一份共通接口。运用内隐类的另一个原因是让它成为private实现物的一部分。在这里,内隐类会为信息隐藏带来好处,而不是对上述的class关联性提供肋益,也不是为了防止命名空间污染问题(namespace pollution)。
46、任何时候你都要注意那些高度耦合(coupling)的 classes.请考虑内隐类(inner classes)为程序拟定和维护带来的好处。内隐类的使用并不是要去除classes间的耦合,而是要让耦合关系更明显也更便利。
47、不要成为”过早最佳化”的牺牲品。
那会让人神经错乱。尤其在系统建构初期,先别烦恼究竟要不要撰写(或避免)原生函数(native methods)、要不要将某些数声明为final、要不要调校程序代码效率等等。你的主要问题应该是先证明设计的正确性,除非设计本身需要某种程度的效率。
48、让范围(作用域,scope)尽可能愈小愈好,这么一来对象的可视范围和寿命都将尽可能地小。
这种作法可降低”对象被用于错误场所,因而隐藏难以察觉的臭虫”的机会。假设你有个容器,以及一段走访该容器的程序片段。如果你复制该段程序代码,将它用于新的容器身上,你可能会不小心以旧容器的大小做为新容器的走访上限值。如果旧容器已不在访问范围内,那么编译期便可找出这样的错误。
49、使用Java 标准程序库提供的容器。
请熟悉他们的用法。你将因此大幅提升你的生产力。请优先选择ArrayList来处理序列(sequences),选择HashSet来处理集合(sets)、选择HashMap来处理关联式数组(associative arrays),选择Linkedlist (而不是Stack) 来处理 shacks和queues。
50、对一个强固的(robust)程序而言,每一个组成都必须强固。
请在你所撰写的每个class中运用Java 提供的所有强固提升工具:访问权限、异常、型别检验等等。通过这种方式,你可以在建构系统时安全地移往抽象化的下一个层次。
51、宁可在编译期发生错误,也不要在执行期发生错误。
试着在最靠近问题发生点的地方处理问题。请优先在”掷出异常之处”处理问题,并在拥有足够信息以处理异常的最接近处理函数(handler)中捕捉异常。请进行现阶段你能够对该异常所做的处理;如果你无法解决问题,应该再次掷出异常。
52、当心冗长的函数定义。
函数应该是一种简短的、”描述并实现class接口中某个可分离部分”的功能单元。过长且复杂的函数不仅难以维护,维护代价也高。或许它尝试做太多事情了。如果你发现这一类函数,代表它应该被切割成多相函数。这种函数也提醒你或许得撰写新的class。小型函数同样能够在你的class中被重复运用。(有时候函数必须很大才行,但它们应该只做一件事情。)
53、尽可能保持”Private”。
一旦你对外公开了程序库的概况(method、Class 或field)。你便再也无法移除它们。因为如果移除它们,便会破坏某个现有的程序代码,使得它们必须重新被编写或重新设计。如果你只公开必要部分,那么你便可以改变其他东西而不造成伤害。设计总是会演化,所以这是个十分重要的自由度。通过这种方式,实现码的更动对derived class 造成的冲击会降最低。在多线程环境下,私密性格外重要-只有private数据可受保护而不被un-synchronized(未受同步控制)的运用所破坏。
54、大量运用注解,并使用javadoc的”注解文档语法”来产生程序的说明文档。
不过注解应该赋予程序代码真正的意义;如果只是重申程序代码已经明确表示的内容,那是很烦人的。请注意,通常Java class和其函数的名称都很长,为的便是降低注解量。
55、避免使用”魔术数字”,也就是那种写死在程序代码里头的数字–如果你想改变它们,它们就会成为你的恶梦,因为你永远都没有办法知道”100″究竟代表” 数组大小”或其他东西。你应该产生具描述性的常量度名称,并在程序中使用该常量名称。这使程序更易于理解也更易于维护。
56、撰写构造函数时,请考虑异常状态。最好情境下,构造函数不执行任何会掷出异常的动作。
次佳情境下,class 只继承自(或合成自)强固的(robust)classes,所以如有任何异常被掷出,并不需要清理。其他情况下,你就得在finally子句清理合成后的classes。如果某个构造函数一定会失败,适当的动作就是掷出异常,使调用者不至于盲目认为对象已被正确产生而继续执行。
57、如果你的class需要在”客户程序员用完对象”后进行清理动作,请将清理动作,放到单一而定义明确的函数中。最好令其名称为cleanup() 以便能够将用途告诉他人。此外请将boolean旗标放到class中,用以代表对象是否已被清理,使finalize()得以检验其死亡条件(请参考第 4章)。
58、finalize() 只可用于对象死亡条件的检验(请参考4章),俾有益于调试。
特殊情况下可能需要释放一些不会被垃圾回收的内存。因为垃圾回收器可能不会被唤起处理你的对象,所以你无法使用finalize()执行必要的清理动作。基于这个原因,你得拟定自己的”清理用”函数。在class finalize()中,请检查确认对象的确已被清理,并在对象尚未被清理时,掷出衍生自Runtime Exception 的异常。使用这种架构前,请先确认finalize()在你的系统上可正常动作(这可能需要调用System.gc()来确认)。
59、如果某个对象在某个特定范围(scope)内必须被清理(cleaned up),而不是被垃圾回收机制收回,请使用以下方法;将对象初始化,成功后立刻进入拥有finally子句的一个try区段内。Finally子句会引发清理动作。
60、当你在继承过程中覆写了finalize(),请记得调用super. Finalize()。
但如果你的”直接上一层superclass”是Object,,就不需要这个动作。你应该让super.finalize() 成为被覆写(overridden)之finalize()的最后一个动作而不是第一个动作,用以确保base class的组件在你需要它们的时候仍然可用。
61、当你撰写固定大小的对象容器,请将它们转换为数组–尤其是从某个函数返回此一容器时。
通过这种方式,你可以获得数组的”编译期型别检验”的好处,而且数组接收者可能不需要”先将数组中的对象加以转型”便能加以使用。请注意,容器库的base class (Java. Util. Collection) 具有两个toArray(),能够达到这个目的。
62、在interface(接口)和abstract class(抽象类)之间,优先选择前者。
如果你知道某些东西即将被设计为一个base class,你的第一选择应该是让它成为interface;只有在一定得放进函数或数据成员时,才应该将它改为abstract class. Interface只和”客户端想进行什么动作”有关,class则比较把重心放在实现细节上。
63、在构造函数中只做惟一必要动作:将对象设定至适当状态。
避免调用其他函数(除了final函数),因为这些函数可能会被其他人覆写因而使你在建构过程中得不可预期的结果(请参考第7章以取得更详细的信息)。小型而简单的构造函数比较不可能掷出异常或引发问题。
64、为了避免一个十分令人泄气的经验,请确认你的classpath中的每个名称,都只有一个未被放到packages里头class。否则编译器会先找到另一个名称相同的class,并回报错误消息。如果你怀疑你的classpath出了问题,试着从classpath中的每个起点查找同名的.class文件。最好还是将所有classes都放到packages里头。
65、留意一不小心犯下的重载(overloading)错误。
如果你覆写base class 函数时没有正确拼写其名称,那么便会增加一个新的函数,而不是覆写原有的函数。但是情况完全合法,所以你不会从编译器或执行期系统得到任何错误消息–你的程序代码只是无法正确作用,如此而已。
66、当心过早最佳化。
先让程序动起来,再让它快–但只有在你必须(也就是说只有在程序被证明在某段程序代码上遭遇效能瓶颈)时才这么做。除非你已经使用效能量测工具(profiler)找出瓶颈所在,否则你可能性只是在浪费你的时间。效能调校的”隐藏成本”便是让你的程序代码变得更不可读、更难维持。
67、记住,程序代码被阅读的时间多于它被撰写的时间。
清晰的设计能够制作出去易懂的程序。注解、细节说明、示例都是无价的。这些东西能够帮助你和你的后继者。如果没有其他信息,那么Java 线上文档找出一些有用的信息时,你所遭遇的挫败应该足以让你相信这一点。
#includestdio.h
#includestring.h
bool g[201][201];
int n,m,ans;
bool b[201];
int link[201];
bool init()
{
int _x,_y;
memset(g,0,sizeof(g));
memset(link,0,sizeof(link));
ans=0;
if(scanf("%d%d",n,m)==EOF)return false;
for(int i=1;i=n;i++)
{
scanf("%d",_x);
for(int j=0;j_x;j++)
{
scanf("%d",_y);
g[ i ][_y]=true;
}
}
return true;
}
bool find(int a)
{
for(int i=1;i=m;i++)
{
if(g[a][ i ]==1!b[ i ])
{
b[ i ]=true;
if(link[ i ]==0||find(link[ i ]))
{
link[ i ]=a;
return true;
}
}
}
return false;
}
int main()
{
while(init())
{
for(int i=1;i=n;i++)
{
memset(b,0,sizeof(b));
if(find(i))ans++;
}
printf("%d\n",ans);
}
}
Pascal:
Program matching;
Const
max = 1000;
Var
map : array [1..max, 1..max] of boolean; {邻接矩阵}
match: array [1..max] of integer; {记录当前连接方式}
chk : array [1..max] of boolean; {记录是否遍历过,防止死循环}
m, n, i, t1, t2, ans,k: integer;
Function dfs(p: integer): boolean;
var
i, t: integer;
begin
for i:=1 to k do
if map[p, i] and not chk[ i ] then
begin
chk[ i ] := true;
if (match[ i ] = 0) or dfs(match[ i ]) then {没有被连过 或 寻找到增广路}
begin
match[ i ] := p;
exit(true);
end;{if}
end;{for}
exit(false);
end;{function}
begin{main}
readln(n, m); {N 为二分图左侧点数 M为可连接的边总数}
fillchar(map, sizeof(map), 0);
k:=0;
for i:=1 to m do{initalize}
begin
readln(t1, t2);
map[t1, t2] := true;
if kt2 then k:=t2;
end;{for}
fillchar(match, sizeof(match), 0);
ans := 0;
for i:=1 to n do
begin
fillchar(chk, sizeof(chk), 0);
if dfs(i) then inc(ans);
end;
writeln(ans);
for i:=1 to 1000 do
if match[ i ] 0 then
writeln(match[ i ], '--', i);
end.
匈牙利命名法
匈牙利命名法是一种编程时的命名规范。基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。命名要基于容易记忆容易理解的原则。保证名字的连贯性是非常重要的。
举例来说,表单的名称为form,那么在匈牙利命名法中可以简写为frm,则当表单变量名称为Switchboard时,变量全称应该为 frmSwitchboard。这样可以很容易从变量名看出Switchboard是一个表单,同样,如果此变量类型为标签,那么就应命名成 lblSwitchboard。可以看出,匈牙利命名法非常便于记忆,而且使变量名非常清晰易懂,这样,增强了代码的可读性,方便各程序员之间相互交流代码。
据说这种命名法是一位叫 Charles Simonyi 的匈牙利程序员发明的,后来他在微软呆了几年,于是这种命名法就通过微软的各种产品和文档资料向世界传播开了。现在,大部分程序员不管自己使用什么软件进行开发,或多或少都使用了这种命名法。这种命名法的出发点是把量名变按:属性+类型+对象描述的顺序组合起来,以使程序员作变量时对变量的类型和其它属性有直观的了解,下面是HN变量命名规范,其中也有一些是我个人的偏向:
属性部分
全局变量
g_
常量
c_
c++类成员变量
m_
静态变量
s_
类型部分
指针
p
函数
fn
无效
v
句柄
h
长整型
l
布尔
b
浮点型(有时也指文件)
f
双字
dw
字符串
sz
短整型
n
双精度浮点
d
计数
c(通常用cnt)
字符
ch(通常用c)
整型
i(通常用n)
字节
by
字
w
实型
r
无符号
u
描述部分
最大
Max
最小
Min
初始化
Init
临时变量
T(或Temp)
源对象
Src
目的对象
Dest
这里顺便写几个例子:
hwnd : h 是类型描述,表示句柄, wnd 是变量对象描述,表示窗口,所以 hwnd 表示窗口句柄;
pfnEatApple : pfn 是类型描述,表示指向函数的指针, EatApple 是变量对象描述,所以它表示
指向 EatApple 函数的函数指针变量。
g_cch : g_ 是属性描述,表示全局变量,c 和 ch 分别是计数类型和字符类型,一起表示变量类
型,这里忽略了对象描述,所以它表示一个对字符进行计数的全局变量。
上面就是HN命名法的一般规则。
小结:匈牙利命名法
匈牙利命名法
MFC、句柄、控件及结构的命名规范 Windows类型 样本变量 MFC类 样本变量
HWND hWnd; CWnd* pWnd;
HDLG hDlg; CDialog* pDlg;
HDC hDC; CDC* pDC;
HGDIOBJ hGdiObj; CGdiObject* pGdiObj;
HPEN hPen; CPen* pPen;
HBRUSH hBrush; CBrush* pBrush;
HFONT hFont; CFont* pFont;
HBITMAP hBitmap; CBitmap* pBitmap;
HPALETTE hPaltte; CPalette* pPalette;
HRGN hRgn; CRgn* pRgn;
HMENU hMenu; CMenu* pMenu;
HWND hCtl; CState* pState;
HWND hCtl; CButton* pButton;
HWND hCtl; CEdit* pEdit;
HWND hCtl; CListBox* pListBox;
HWND hCtl; CComboBox* pComboBox;
HWND hCtl; CScrollBar* pScrollBar;
HSZ hszStr; CString pStr;
POINT pt; CPoint pt;
SIZE size; CSize size;
RECT rect; CRect rect;
一般前缀命名规范 前缀 类型 实例
C 类或结构 CDocument,CPrintInfo
m_ 成员变量 m_pDoc,m_nCustomers
变量命名规范 前缀 类型 描述 实例
ch char 8位字符 chGrade
ch TCHAR 如果_UNICODE定义,则为16位字符 chName
b BOOL 布尔值 bEnable
n int 整型(其大小依赖于操作系统) nLength
n UINT 无符号值(其大小依赖于操作系统) nHeight
w WORD 16位无符号值 wPos
l LONG 32位有符号整型 lOffset
dw DWORD 32位无符号整型 dwRange
p * 指针 pDoc
lp FAR* 远指针 lpszName
lpsz LPSTR 32位字符串指针 lpszName
lpsz LPCSTR 32位常量字符串指针 lpszName
lpsz LPCTSTR 如果_UNICODE定义,则为32位常量字符串指针 lpszName
h handle Windows对象句柄 hWnd
lpfn callback 指向CALLBACK函数的远指针
前缀 符号类型 实例 范围
IDR_ 不同类型的多个资源共享标识 IDR_MAIINFRAME 1~0x6FFF
IDD_ 对话框资源 IDD_SPELL_CHECK 1~0x6FFF
HIDD_ 对话框资源的Help上下文 HIDD_SPELL_CHECK 0x20001~0x26FF
IDB_ 位图资源 IDB_COMPANY_LOGO 1~0x6FFF
IDC_ 光标资源 IDC_PENCIL 1~0x6FFF
IDI_ 图标资源 IDI_NOTEPAD 1~0x6FFF
ID_ 来自菜单项或工具栏的命令 ID_TOOLS_SPELLING 0x8000~0xDFFF
HID_ 命令Help上下文 HID_TOOLS_SPELLING 0x18000~0x1DFFF
IDP_ 消息框提示 IDP_INVALID_PARTNO 8~0xDEEF
HIDP_ 消息框Help上下文 HIDP_INVALID_PARTNO 0x30008~0x3DEFF
IDS_ 串资源 IDS_COPYRIGHT 1~0x7EEF
IDC_ 对话框内的控件 IDC_RECALC 8~0xDEEF
Microsoft MFC宏命名规范 名称 类型
_AFXDLL 唯一的动态连接库(Dynamic Link Library,DLL)版本
_ALPHA 仅编译DEC Alpha处理器
_DEBUG 包括诊断的调试版本
_MBCS 编译多字节字符集
_UNICODE 在一个应用程序中打开Unicode
AFXAPI MFC提供的函数
CALLBACK 通过指针回调的函数
库标识符命名法 标识符 值和含义
u ANSI(N)或Unicode(U)
d 调试或发行:D = 调试;忽略标识符为发行。
静态库版本命名规范 库 描述
NAFXCWD.LIB 调试版本:MFC静态连接库
NAFXCW.LIB 发行版本:MFC静态连接库
UAFXCWD.LIB 调试版本:具有Unicode支持的MFC静态连接库
UAFXCW.LIB 发行版本:具有Unicode支持的MFC静态连接库
动态连接库命名规范 名称 类型
_AFXDLL 唯一的动态连接库(DLL)版本
WINAPI Windows所提供的函数
Windows.h中新的命名规范 类型 定义描述
WINAPI 使用在API声明中的FAR PASCAL位置,如果正在编写一个具有导出API人口点的DLL,则可以在自己的API中使用该类型
CALLBACK 使用在应用程序回叫例程,如窗口和对话框过程中的FAR PASCAL的位置
LPCSTR 与LPSTR相同,只是LPCSTR用于只读串指针,其定义类似(const char FAR*)
UINT 可移植的无符号整型类型,其大小由主机环境决定(对于Windows NT和Windows 9x为32位);它是unsigned int的同义词
LRESULT 窗口程序返回值的类型
LPARAM 声明lParam所使用的类型,lParam是窗口程序的第四个参数
WPARAM 声明wParam所使用的类型,wParam是窗口程序的第三个参数
LPVOID 一般指针类型,与(void *)相同,可以用来代替LPSTR
--------------------------------------------------------------------------------
抨击匈牙利命名法
匈牙利命名法是一种编程时的命名规范。命名规范是程序书写规范中最重要也是最富争议的地方,自古乃兵家必争之地。命名规范有何用?四个字:名正言顺。用二分法,命名规范分为好的命名规范和坏的命名规范,也就是说名正言顺的命名规范和名不正言不顺的命名规范。好的舞鞋是让舞者感觉不到其存在的舞鞋,坏的舞鞋是让舞者带着镣铐起舞。一个坏的命名规范具有的破坏力比一个好的命名规范具有的创造力要大得多。
本文要证明的是:匈牙利命名法是一个坏的命名规范。本文的作用范围为静态强类型编程语言。本文的分析范本为C语言和C++语言。下文中的匈法为匈牙利命名法的简称。
一 匈牙利命名法的成本
匈法的表现形式为给变量名附加上类型名前缀,例如:nFoo,szFoo,pFoo,cpFoo分别表示整型变量,字符串型变量,指针型变量和常指针型变量。可以看出,匈法将变量的类型信息从单一地点(声明变量处)复制到了多个地点(使用变量处),这是冗余法。冗余法的成本之一是要维护副本的一致性。这个成本在编写和维护代码的过程中需要改变变量的类型时付出。冗余法的成本之二是占用了额外的空间。一个优秀的书写者会自觉地遵从一个法则:代码最小组织单位的长度以30个自然行以下为宜,如果超过50行就应该重新组织。一个变量的书写空间会给这一法则添加不必要的难度。
二 匈牙利命名法的收益
这里要证明匈牙利命名法的收益是含糊的,无法预期的。
范本1:strcpy(pstrFoo,pcstrFoo2) Vs strcpy(foo,foo2)
匈法在这里有什么收益呢?我看不到。没有一个程序员会承认自己不知道strcpy函数的参数类型吧。
范本2:unknown_function(nFoo) Vs unknown_function(foo)
匈法在这里有什么收益呢?我看不到。对于一个不知道确定类型的函数,程序员应该去查看该函数的文档,这是一种成本。使用匈法的唯一好处是看代码的人知道这个函数要求一个整型参数,这又有什么用处呢?函数是一种接口,参数的类型仅仅是接口中的一小部分。诸如函数的功能、出口信息、线程安全性、异常安全性、参数合法性等重要信息还是必须查阅文档。
范本3:nFoo=nBar Vs foo=bar
匈法在这里有什么收益呢?我看不到。使用匈法的唯一好处是看代码的人知道这里发生了一个整型变量的复制动作,听起来没什么问题,可以安心睡大觉了。如果他看到的是nFoo=szBar,可能会从美梦中惊醒。且慢,事情真的会是这样吗?我想首先被惊醒的应该是编译器。另一方面,nFoo=nBar只是在语法上合法而已,看代码的人真正关心的是语义的合法性,匈法对此毫无帮助。另一方面,一个优秀的书写者会自觉地遵从一个法则:代码最小组织单位中的临时变量以一两个为宜,如果超过三个就应该重新组织。结合前述第一个法则,可以得出这样的结论:易于理解的代码本身就应该是易于理解的,这是代码的内建高质量。好的命名规范对内建高质量的助益相当有限,而坏的命名规范对内建高质量的损害比人们想象的要大。
三 匈牙利命名法的实施
这里要证明匈牙利命名法在C语言是难以实施的,在C++语言中是无法实施的。从逻辑上讲,对匈法的收益做出否定的结论以后,再来论证匈法的可行性,是画蛇添足。不过有鉴于小马哥曾让已射杀之敌死灰复燃,我还是再踏上一支脚为妙。
前面讲过,匈法是类型系统的冗余,所以实施匈法的关键是我们是否能够精确地对类型系统进行复制。这取决于类型系统的复杂性。
先来看看C语言:
1.内置类型:int,char,float,double 复制为 n,ch,f,d?好像没有什么问题。不过谁来告诉我void应该怎么表示?
2.组合类型:array,union,enum,struct 复制为 a,u,e,s?好像比较别扭。
这里的难点不是为主类型取名,而是为副类型取名。an表示整型数组?sfoo,sbar表示结构foo,结构bar?ausfoo表示联合结构foo数组?累不累啊。
3.特殊类型:pointer。pointer在理论上应该是组合类型,但是在C语言中可以认为是内置类型,因为C语言并没有非常严格地区分不同的指针类型。下面开始表演:pausfoo表示联合结构foo数组指针?ppp表示指针的指针的指针?
噩梦还没有结束,再来看看类型系统更阿为丰富的C++语言:
1.class:如果说C语言中的struct还可以用stru搪塞过去的话,不要梦想用cls来搪塞C++中的class。严格地讲,class根本就并不是一个类型,而是创造类型的工具,在C++中,语言内置类型的数量和class创造的用户自定义类型的数量相比完全可以忽略不计。stdvectorFoo表示标准库向量类型变量Foo?疯狂的念头。
2.命名空间:boostfilesystemiteratorFoo,表示boost空间filesystem子空间遍历目录类型变量Foo?程序员要崩溃了。
3.模板:你记得std::mapstd::string,std::string类型的确切名字吗?我是记不得了,好像超过255个字符,还是饶了我吧。
4.模板参数:template class T, class BinaryPredicateconst T max(const T a, const T b, BinaryPredicate comp) 聪明的你,请用匈法为T命名。上帝在发笑。
5.类型修饰:static,extern,mutable,register,volatile,const,short,long,unsigned 噩梦加上修饰是什么?还是噩梦。百度地图
1、组织与风格
(1).关键词和操作符之间加适当的空格。
(2).相对独立的程序块与块之间加空行
(3).较长的语句、表达式等要分成多行书写。
(4).划分出的新行要进行适应的缩进,使排版整齐,语句可读。
(5).长表达式要在低优先级操作符处划分新行,操作符放在新行之首。
(6).循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分。
(7).若函数或过程中的参数较长,则要进行适当的划分。
(8).不允许把多个短语句写在一行中,即一行只写一条语句。
(9).函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格。
注:如果大家有兴趣可以到安安DIY创作室博客,有相关说明性的文章和解释。
2、注解
Java 的语法与 C++ 及为相似,那么,你知道 Java 的注释有几种吗?是两种?
// 注释一行
/* ...... */ 注释若干行
不完全对,除了以上两种之外,还有第三种,文档注释:
/** ...... */ 注释若干行,并写入 javadoc 文档
注释要简单明了。
String userName = null; //用户名
边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。
在必要的地方注释,注释量要适中。注释的内容要清楚、明了,含义准确,防止注释二义性。
保持注释与其描述的代码相邻,即注释的就近原则。
对代码的注释应放在其上方相邻位置,不可放在下面。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释应放在此域的右方;
同一结构中不同域的注释要对齐。
变量、常量的注释应放在其上方相邻位置或右方。
全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
在每个源文件的头部要有必要的注释信息,包括:文件名;版本号;作者;生成日期;模块功能描述(如功能、主要算法、内部各部分之间的关系、该文件与其它文件关系等);主要函数或过程清单及本文件历史修改记录等。
/**
* Copy Right Information : Neusoft IIT
* Project : eTrain
* JDK version used : jdk1.3.1
* Comments : config path
* Version : 1.01
* Modification history :2003.5.1
* Sr Date Modified By Why What is modified
* 1. 2003.5.2 Kevin Gao new
**/
在每个函数或过程的前面要有必要的注释信息,包括:函数或过程名称;功能描述;输入、输出及返回值说明;调用关系及被调用关系说明等
/**
* Description :checkout 提款
* @param Hashtable cart info
* @param OrderBean order info
* @return String
*/
public String checkout(Hashtable htCart,
OrderBean orderBean)
throws Exception{
}
javadoc注释标签语法
@author 对类的说明 标明开发该类模块的作者
@version 对类的说明 标明该类模块的版本
@see 对类、属性、方法的说明 参考转向,也就是相关主题
@param 对方法的说明 对方法中某参数的说明
@return 对方法的说明 对方法返回值的说明
@exception 对方法的说明 对方法可能抛出的异常进行说明
3、命名规范
定义这个规范的目的是让项目中所有的文档都看起来像一个人写的,增加可读性,减少项目组中因为换人而带来的损失。(这些规范并不是一定要绝对遵守,但是一定要让程序有良好的可读性)较短的单词可通过去掉元音形成缩写;要不然最后自己写的代码自己都看不懂了,那可不行。
较长的单词可取单词的头几发符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。
使用匈牙利表示法
Package 的命名
Package 的名字应该都是由一个小写单词组成。
package com.neu.util
Class 的命名
Class 的名字必须由大写字母开头而其他字母都小写的单词组成,对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母。
public class ThisAClassName{}
Class 变量的命名
变量的名字必须用一个小写字母开头。后面的单词用大写字母开头
userName , thisAClassMethod
Static Final 变量的命名
static Final 变量的名字应该都大写,并且指出完整含义。
/**
*DBConfig PATH
**/
public static final String
DB_CONFIG_FILE_PATH =com.neu.etrain.dbconfig;
参数的命名
参数的名字必须和变量的命名规范一致。
数组的命名
数组应该总是用下面的方式来命名:
byte[] buffer;
而不是:
byte buffer[];
方法的参数
使用有意义的参数命名,如果可能的话,使用和要赋值的字段一样的名字:
SetCounter(int size){
this.size = size;
}
4、文件样式
所有的 Java(*.java) 文件都必须遵守如下的样式规则:
版权信息
版权信息必须在 java 文件的开头,比如:
/*
* Copyright ? 2000 Shanghai XXX Co. Ltd.
* All right reserved.
*/
其他不需要出现在 javadoc 的信息也可以包含在这里。
Package/Imports
package 行要在 import 行之前,import 中标准的包名要在本地的包名之前,而且按照字母
顺序排列。如果 import 行中包含了同一个包中的不同子目录,则应该用 * 来处理。
package hotlava.net.stats;
import java io.*;
import java.util.Observable;
import hotlava.util.Application;
这里 java。io.* 使用来代替InputStream and OutputStream 的。
Class
接下来的是类的注释,一般是用来解释类的。
/**
* A class representing a set of packet and byte counters
* It is observable to allow it to be watched, but only
* reports changes when the current set is complete
*/
接下来是类定义,包含了在不同的行的 extends 和 implements
public class CounterSet
extends Observable
implements Cloneable
Class Fields
接下来是类的成员变量:
/**
* Packet counters
*/
protected int[] packets;
public 的成员变量必须生成文档(JavaDoc)。proceted、private和 package 定义的成
员变量如果名字含义明确的话,可以没有注释。
存取方法
接下来是类变量的存取的方法。它只是简单的用来将类的变量赋值获取值的话,可以简单的
写在一行上。
/**
* Get the counters
* @return an array containing the statistical data. This array has been
* freshly allocated and can be modified by the caller.
*/
public int[] getPackets() { return copyArray(packets, offset); }
public int[] getBytes() { return copyArray(bytes, offset); }
public int[] getPackets() { return packets; }
public void setPackets(int[] packets) { this.packets = packets; }
其它的方法不要写在一行上
构造函数
接下来是构造函数,它应该用递增的方式写(比如:参数多的写在后面)。
访问类型 (public, private 等.) 和 任何 static, final 或 synchronized 应该在一行
中,并且方法和参数另写一行,这样可以使方法和参数更易读。
public
CounterSet(int size){
this.size = size;
}
克隆方法
如果这个类是可以被克隆的,那么下一步就是 clone 方法:
public
Object clone() {
try {
CounterSet obj = (CounterSet)super.clone();
obj.packets = (int[])packets.clone();
obj.size = size;
return obj;
}catch(CloneNotSupportedException e) {
throw new InternalError(Unexpected CloneNotSUpportedException: +
e.getMessage());
}
}
类方法
下面开始写类的方法:
/**
* Set the packet counters
* (such as when restoring from a database)
*/
protected final
void setArray(int[] r1, int[] r2, int[] r3, int[] r4)
throws IllegalArgumentException
{
//
// Ensure the arrays are of equal size
//
if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)
throw new IllegalArgumentException(Arrays must be of the same size);
System.arraycopy(r1, 0, r3, 0, r1.length);
System.arraycopy(r2, 0, r4, 0, r1.length);
}
toString 方法
无论如何,每一个类都应该定义 toString 方法:
public
String toString() {
String retval = CounterSet: ;
for (int i = 0; i data.length(); i++) {
retval += data.bytes.toString();
retval += data.packets.toString();
}
return retval;
}
}
main 方法
如果main(String[]) 方法已经定义了, 那么它应该写在类的底部.
5、代码可读性
避免使用不易理解的数字,用有意义的标识来替代。
不要使用难懂的技巧性很高的语句。
源程序中关系较为紧密的代码应尽可能相邻。
6、代码性能
在写代码的时候,从头至尾都应该考虑性能问题。这不是说时间都应该浪费在优化代码上,而是我们时刻应该提醒自己要注意代码的效率。比如:如果没有时间来实现一个高效的算法,那么我们应该在文档中记录下来,以便在以后有空的时候再来实现她。
不是所有的人都同意在写代码的时候应该优化性能这个观点的,他们认为性能优化的问题应该在项目的后期再去考虑,也就是在程序的轮廓已经实现了以后。
不必要的对象构造
不要在循环中构造和释放对象
使用 StringBuffer 对象
在处理 String 的时候要尽量使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。
String 类将 StringBuffer 类封装了起来,(以花费更多时间为代价)为开发人员提供了一个安全的接口。当我们在构造字符串的时候,我们应该用 StringBuffer 来实现大部分的工作,当工作完成后将 StringBuffer 对象再转换为需要的 String 对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用StringBuffer 对象和她的 append() 方法。如果我们用 String 对象代替StringBuffer 对象的话,会花费许多不必要的创建和释放对象的 CPU 时间。大家可以来安安DIY创作室一起讨论。
避免太多的使用 synchronized 关键字避免不必要的使用关键字 synchronized,应该在必要的时候再使用她,这是一个避免死锁的好方法。
7、编程技巧
byte 数组转换到 characters
为了将 byte 数组转换到 characters,你可以这么做:
Hello world!.getBytes();
Utility 类
Utility 类(仅仅提供方法的类)应该被申明为抽象的来防止被继承或被初始化。
初始化
下面的代码是一种很好的初始化数组的方法:
objectArguments = new Object[] { arguments };
枚举类型
JAVA 对枚举的支持不好,但是下面的代码是一种很有用的模板:
class Colour {
public static final Colour BLACK = new Colour(0, 0, 0);
public static final Colour RED = new Colour(0xFF, 0, 0);
public static final Colour GREEN = new Colour(0, 0xFF, 0);
public static final Colour BLUE = new Colour(0, 0, 0xFF);
public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);
}
这种技术实现了RED, GREEN, BLUE 等可以象其他语言的枚举类型一样使用的常量。
他们可以用 '==' 操作符来比较。
但是这样使用有一个缺陷:如果一个用户用这样的方法来创建颜色 BLACK new Colour(0,0,0)
那么这就是另外一个对象,'=='操作符就会产生错误。她的 equal() 方法仍然有效。由于这个原因,这个技术的缺陷最好注明在文档中,或者只在自己的包中使用。
8、编写格式
代码样式
代码应该用 unix 的格式,而不是 windows 的(比如:回车变成回车+换行)
文档化
必须用 javadoc 来为类生成文档。不仅因为它是标准,这也是被各种 java 编译器都认可的方法。使用 @author 标记是不被推荐的,因为代码不应该是被个人拥有的。
缩进
缩进应该是每行2个空格. 不要在源文件中保存Tab字符. 在使用不同的源代码管理工具时Tab字符将因为用户设置的不同而扩展为不同的宽度.如果你使用 UltrEdit 作为你的 Java 源代码编辑器的话,你可以通过如下操作来禁止保存Tab字符, 方法是通过 UltrEdit中先设定 Tab 使用的长度室2个空格,然后用 Format|Tabs to Spaces 菜单将 Tab 转换为空格。
页宽
页宽应该设置为80字符. 源代码一般不会超过这个宽度, 并导致无法完整显示, 但这一设置也可以灵活调整. 在任何情况下, 超长的语句应该在一个逗号或者一个操作符后折行. 一条语句折行后, 应该比原来的语句再缩进2个字符.
{} 对
{} 中的语句应该单独作为一行. 例如, 下面的第1行是错误的, 第2行是正确的:
if (i0) { i ++ }; // 错误, { 和 } 在同一行
if (i0) {
i ++
}; // 正确, { 单独作为一行
} 语句永远单独作为一行.如果 } 语句应该缩进到与其相对应的 { 那一行相对齐的位置。
括号
左括号和后一个字符之间不应该出现空格, 同样, 右括号和前一个字符之间也不应该出现空格. 下面的例子说明括号和空格的错误及正确使用:
CallProc( AParameter ); // 错误
CallProc(AParameter); // 正确
不要在语句中使用无意义的括号. 括号只应该为达到某种目的而出现在源代码中。下面的例子说明错误和正确的用法:
if ((I) = 42) { // 错误 - 括号毫无意义
if (I == 42) or (J == 42) then // 正确 - 的确需要括号
9、代码编译
1.编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。
2.同一项目组内,最好使用相同的编辑器,并使用相同的设置选项。
3.合理地设计软件系统目录,方便开发人员使用。
4.打开编译器的所有告警开关对程序进行编译。
5.在同一项目组或产品组中,要统一编译开关选项。
6.使用工具软件(如Visual SourceSafe)对代码版本进行维护。如果大家有不明白的可以到安安DIY创作室留言。
10、可移植性
Borland Jbulider 不喜欢 synchronized 这个关键字,如果你的断点设在这些关键字的作用域内的话,调试的时候你会发现的断点会到处乱跳,让你不知所措。除非必须,尽量不要使用。
换行
如果需要换行的话,尽量用 println 来代替在字符串中使用\n。
你不要这样:
System.out.print(Hello,world!\n);
要这样:
System.out.println(Hello,world!);
或者你构造一个带换行符的字符串,至少要象这样:
String newline = System.getProperty(line.separator);
System.out.println(Hello world + newline);
PrintStream
PrintStream 已经被不赞成(deprecated)使用,用 PrintWrite 来代替它。