﻿<?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-Shooper.Java-文章分类-程序心得体会</title><link>http://www.blogjava.net/shooper/category/10934.html</link><description>Beginning Java</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:22:08 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:22:08 GMT</pubDate><ttl>60</ttl><item><title>java中的易混问题收集  </title><link>http://www.blogjava.net/shooper/articles/49130.html</link><dc:creator>Shooper.Java</dc:creator><author>Shooper.Java</author><pubDate>Tue, 30 May 2006 15:13:00 GMT</pubDate><guid>http://www.blogjava.net/shooper/articles/49130.html</guid><wfw:comment>http://www.blogjava.net/shooper/comments/49130.html</wfw:comment><comments>http://www.blogjava.net/shooper/articles/49130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shooper/comments/commentRss/49130.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shooper/services/trackbacks/49130.html</trackback:ping><description><![CDATA[第一，final, finally, finalize的区别。 <br /><br />final?修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的。将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载 <br /><br />finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。 <br />finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 <br /><br />第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否可以implements(实现)interface(接口)? <br /><br />匿名的内部类是没有名字的内部类。不能extends(继承) 其它类，但一个内部类可以作为一个接口，由另一个内部类实现。 <br /><br />第三，Static Nested Class 和 Inner Class的不同，说得越多越好 <br />Nested Class （一般是C++的说法），Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.frontfree.net/articles/services/view.asp?id=704&amp;page=1 <br />注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象 <br /><br />第四，&amp;和&amp;&amp;的区别。 <br />&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。 <br /><br />第五，HashMap和Hashtable的区别。 <br />都属于Map接口的类，实现了将惟一键映射到特定的值上。 <br />HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 <br />Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。 <br /><br />第六，Collection 和 Collections的区别。 <br />Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。 <br />Collection是个java.util下的接口，它是各种集合结构的父接口。 <br /><br /><br />第七，什么时候用assert。 <br />断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true。如果表达式计算为 false，那么系统会报告一个 Assertionerror。它用于调试目的： <br />assert(a &gt; 0); // throws an Assertionerror if a &lt;= 0 <br />断言可以有两种形式： <br />assert Expression1 ; <br />assert Expression1 : Expression2 ; <br />Expression1 应该总是产生一个布尔值。 <br />Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。 <br />断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记： <br />javac -source 1.4 Test.java <br />要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。 <br />要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。 <br />要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 <br />可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。 <br /><br /><br />第八，GC是什么? 为什么要有GC? (基础)。 <br />GC是垃圾收集器。Java 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一： <br />System.gc() <br />Runtime.getRuntime().gc() <br /><br />第九，String s = new String("xyz");创建了几个String Object? <br />两个对象，一个是“xyx”,一个是指向“xyx”的引用对象s。 <br /><br />第十，Math.round(11.5)等於多少? Math.round(-11.5)等於多少? <br />Math.round(11.5)返回（long）12，Math.round(-11.5)返回（long）-11; <br /><br />第十一，short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错? <br />short s1 = 1; s1 = s1 + 1;有错，s1是short型，s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。 <br /><br />第十二，sleep() 和 wait() 有什么区别? 搞线程的最爱 <br />sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后，线程不一定立即恢复执行。这是因为在那个时刻，其它线程可能正在运行而且没有被调度为放弃执行，除非(a)“醒来”的线程具有更高的优先级 <br />(b)正在运行的线程因为其它原因而阻塞。 <br />wait()是线程交互时，如果线程对一个同步对象x 发出一个wait()调用，该线程会暂停执行，被调对象进入等待状态，直到被唤醒或等待时间到。 <br /><br /><br /><br />第十三，Java有没有goto? <br />Goto?java中的保留字，现在没有在java中使用。 <br /><br />第十四，数组有没有length()这个方法? String有没有length()这个方法？ <br />数组没有length()这个方法，有length的属性。 <br />String有有length()这个方法。 <br /><br />第十五，Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? <br />方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写 (Overriding)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 <br /><br />第十六，Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? <br />Set里的元素是不能重复的，那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 <br />equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖，为的是当两个分离的对象的内容和类型相配的话，返回真值。 <br /><br />第十七，给我一个你最常见到的runtime exception。 <br />ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, <br />ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException <br /><br />第十八，error和exception有什么区别? <br />error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 <br />exception 表示一种设计或实现问题。也就是说，它表示如果程序运行正常，从不会发生的情况。 <br /><br /><br />第十九，List, Set, Map是否继承自Collection接口? <br />List，Set是 <br /><br />Map不是 <br /><br />第二十，abstract class和interface有什么区别? <br />声明方法的存在而不去实现它的类被叫做抽象类（abstract class），它用于要创建一个体现某些基本行为的类，并为该类声明方法，但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量，其类型是一个抽象类，并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现，否则它们也是抽象类为。取而代之，在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 <br />接口（interface）是抽象类的变体。在接口中，所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的，没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似，除了该实现类不能从接口定义中继承行为。当类实现特殊接口时，它定义（即将程序体给予）所有这种接口的方法。然后，它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类，它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换，instanceof 运算符可以用来决定某对象的类是否实现了接口。 <br /><br />第二十一，abstract的method是否可同时是static,是否可同时是native，是否可同时是synchronized? <br />都不能 <br /><br />第二十二，接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? <br />接口可以继承接口。抽象类可以实现(implements)接口，抽象类是否可继承实体类，但前提是实体类必须有明确的构造函数。 <br /><br />第二十三，启动一个线程是用run()还是start()? <br />启动一个线程是调用start()方法，使线程所代表的虚拟处理机处于可运行状态，这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 <br /><br /><br /><br />第二十四，构造器Constructor是否可被override? <br />构造器Constructor不能被继承，因此不能重写Overriding，但可以被重载Overloading。 <br /><br />第二十五，是否可以继承String类? <br />String类是final类故不可以继承。 <br /><br />第二十六，当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进入此对象的其它方法? <br />不能，一个对象的一个synchronized方法只能由一个线程访问。 <br /><br />第二十七，try {}里有一个return语句，那么紧跟在这个try后的finally {}里的code会不会被执行，什么时候被执行，在return前还是后? <br />会执行，在return前执行。 <br /><br /><br />第二十八，编程题: 用最有效率的方法算出2乘以8等於几? <br />有C背景的程序员特别喜欢问这种问题。 <br /><br />2 &lt;&lt; 3 <br /><br />第二十九，两个对象值相同(x.equals(y) == true)，但却可有不同的hash code，这句话对不对? <br />不对，有相同的hash code。 <br /><br />第三十，当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递? <br />是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时，参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变，但对象的引用是永远不会改变的。 <br /><br /><br />第三十一，swtich是否能作用在byte上，是否能作用在long上，是否能作用在String上? <br />switch（expr1）中，expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 <br /><br />第三十二，编程题: 写一个Singleton出来。 <br />Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。 <br />一般Singleton模式通常有几种种形式: <br />第一种形式: 定义一个类，它的构造函数为private的，它有一个static的private的该类变量，在类初始化时实例话，通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 <br />public class Singleton { <br />　　private Singleton(){} <br />　　//在自己内部定义自己一个实例，是不是很奇怪？ <br />　　//注意这是private 只供内部调用 <br />　　private static Singleton instance = new Singleton(); <br />　　//这里提供了一个供外部访问本class的静态方法，可以直接访问　　 <br />　　public static Singleton getInstance() { <br />　　　　return instance; 　　 <br />　　 } <br />} <br />第二种形式: <br />public class Singleton { <br />　　private static Singleton instance = null; <br />　　public static synchronized Singleton getInstance() { <br />　　//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次　　　 　 <br />　　//使用时生成实例，提高了效率！ <br />　　if (instance==null) <br />　　　　instance＝new Singleton(); <br />return instance; 　　} <br />} <br />其他形式: <br />定义一个类，它的构造函数为private的，所有方法为static的。 <br />一般认为第一种形式要更加安全些 <br /><br /><br /><br />Hashtable和HashMap <br />Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map interface的一个实现 <br /><br />HashMap允许将null作为一个entry的key或者value，而Hashtable不允许 <br /><br />还有就是，HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。因为contains方法容易让人引起误解。 <br /><br />最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在 <br />多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap <br />就必须为之提供外同步。 <br /><br />Hashtable和HashMap采用的hash/rehash算法都大概一样，所以性能不会有很大的差异。 <img src ="http://www.blogjava.net/shooper/aggbug/49130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shooper/" target="_blank">Shooper.Java</a> 2006-05-30 23:13 <a href="http://www.blogjava.net/shooper/articles/49130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符，字节和编码</title><link>http://www.blogjava.net/shooper/articles/46507.html</link><dc:creator>Shooper.Java</dc:creator><author>Shooper.Java</author><pubDate>Tue, 16 May 2006 15:46:00 GMT</pubDate><guid>http://www.blogjava.net/shooper/articles/46507.html</guid><wfw:comment>http://www.blogjava.net/shooper/comments/46507.html</wfw:comment><comments>http://www.blogjava.net/shooper/articles/46507.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shooper/comments/commentRss/46507.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shooper/services/trackbacks/46507.html</trackback:ping><description><![CDATA[
		<h2>
				<a name="main">
				</a>字符，字节和编码</h2>
		<p>
				<font size="1">[原创文章，转载请保留或注明出处：<a href="http://www.regexlab.com/zh/encoding.htm">http://www.regexlab.com/zh/encoding.htm</a>]</font>
		</p>
		<p>级别：中级</p>
		<blockquote>
				<p>摘要：本文介绍了字符与编码的发展过程，相关概念的正确理解。举例说明了一些实际应用中，编码的实现方法。然后，本文讲述了通常对字符与编码的几种误解，由于这些误解而导致乱码产生的原因，以及消除乱码的办法。本文的内容涵盖了“中文问题”，“乱码问题”。</p>
				<p>掌握编码问题的关键是正确地理解相关概念，编码所涉及的技术其实是很简单的。因此，阅读本文时需要慢读多想，多思考。</p>
		</blockquote>
		<h4>
				<a name="intro">
				</a>引言</h4>
		<p>“字符与编码”是一个被经常讨论的话题。即使这样，时常出现的乱码仍然困扰着大家。虽然我们有很多的办法可以用来消除乱码，但我们并不一定理解这些办法的内在原理。而有的乱码产生的原因，实际上由于底层代码本身有问题所导致的。因此，不仅是初学者会对字符编码感到模糊，有的底层开发人员同样对字符编码缺乏准确的理解。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h4>
				<a name="develop">
				</a>1. 编码问题的由来，相关概念的理解</h4>
		<h5>1.1 字符与编码的发展</h5>
		<p>从计算机对多国语言的支持角度看，大致可以分为三个阶段：</p>
		<table cellspacing="0" cellpadding="3" border="0">
				<tbody>
						<tr>
								<td class="top_1">　</td>
								<td class="top_2" nowrap="" align="middle">
										<b>系统内码</b>
								</td>
								<td class="top_2" align="middle">
										<b>说明</b>
								</td>
								<td class="top_2" align="middle">
										<b>系统</b>
								</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="">阶段一</td>
								<td class="con_2" nowrap="" align="middle">ASCII</td>
								<td class="con_2">计算机刚开始只支持英语，其它语言不能够在计算机上存储和显示。</td>
								<td class="con_2">英文 DOS</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="">阶段二</td>
								<td class="con_2" nowrap="" align="middle">ANSI编码<br />（本地化）</td>
								<td class="con_2">为使计算机支持更多语言，通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如：汉字 '中' 在中文操作系统中，使用 [0xD6,0xD0] 这两个字节存储。<br /><br />不同的国家和地区制定了不同的标准，由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式，称为<b> ANSI 编码</b>。在简体中文系统下，ANSI 编码代表 GB2312 编码，在日文操作系统下，ANSI 编码代表 JIS 编码。<br /><br />不同 ANSI 编码之间互不兼容，当信息在国际间交流时，无法将属于两种语言的文字，存储在同一段<b> ANSI 编码</b>的文本中。</td>
								<td class="con_2">中文 DOS，中文 Windows 95/98，日文 Windows 95/98</td>
						</tr>
						<tr>
								<td class="bot_1" nowrap="">阶段三</td>
								<td class="bot_2" nowrap="" align="middle">UNICODE<br />（国际化）</td>
								<td class="bot_2">为了使国际间信息交流更加方便，国际组织制定了 <b>UNICODE 字符集</b>，为各种语言中的每一个字符设定了统一并且唯一的数字编号，以满足跨语言、跨平台进行文本转换、处理的要求。</td>
								<td class="bot_2">Windows NT/2000/XP，Linux，Java</td>
						</tr>
				</tbody>
		</table>
		<p>字符串在内存中的存放方法：</p>
		<p>在 ASCII 阶段，<b>单字节字符串</b>使用一个字节存放一个字符（SBCS）。比如，"Bob123" 在内存中为：</p>
		<table style="FONT-SIZE: 80%; COLOR: #000080" cellspacing="5" cellpadding="0" border="0">
				<tbody>
						<tr>
								<td>42</td>
								<td>6F</td>
								<td>62</td>
								<td>31</td>
								<td>32</td>
								<td>33</td>
								<td>00</td>
						</tr>
						<tr>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
						</tr>
						<tr>
								<td align="middle">B</td>
								<td align="middle">o</td>
								<td align="middle">b</td>
								<td align="middle">1</td>
								<td align="middle">2</td>
								<td align="middle">3</td>
								<td align="middle">\0</td>
						</tr>
				</tbody>
		</table>
		<p>在使用 ANSI 编码支持多种语言阶段，每个字符使用一个字节或多个字节来表示（MBCS），因此，这种方式存放的字符也被称作<b>多字节字符</b>。比如，"中文123" 在中文 Windows 95 内存中为7个字节，每个汉字占2个字节，每个英文和数字字符占1个字节：</p>
		<table style="FONT-SIZE: 80%; COLOR: #000080" cellspacing="5" cellpadding="0" border="0">
				<tbody>
						<tr>
								<td>D6</td>
								<td>D0</td>
								<td>CE</td>
								<td>C4</td>
								<td>31</td>
								<td>32</td>
								<td>33</td>
								<td>00</td>
						</tr>
						<tr>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#000080">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
						</tr>
						<tr>
								<td align="middle" colspan="2">中</td>
								<td align="middle" colspan="2">文</td>
								<td align="middle">1</td>
								<td align="middle">2</td>
								<td align="middle">3</td>
								<td align="middle">\0</td>
						</tr>
				</tbody>
		</table>
		<p>在 UNICODE 被采用之后，计算机存放字符串时，改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节（16 位）来存放一个序号（DBCS），因此，这种方式存放的字符也被称作<b>宽字节字符</b>。比如，字符串 "中文123" 在 Windows 2000 下，内存中实际存放的是 5 个序号：</p>
		<table style="FONT-SIZE: 80%; COLOR: #000080" cellspacing="5" cellpadding="0" border="0">
				<tbody>
						<tr>
								<td valign="bottom">2D</td>
								<td valign="bottom">4E</td>
								<td valign="bottom">87</td>
								<td valign="bottom">65</td>
								<td valign="bottom">31</td>
								<td valign="bottom">00</td>
								<td valign="bottom">32</td>
								<td valign="bottom">00</td>
								<td valign="bottom">33</td>
								<td valign="bottom">00</td>
								<td valign="bottom">00</td>
								<td valign="bottom">00</td>
								<td>
										<font color="#808080">     ← 在 x86 CPU 中，低字节在前</font>
								</td>
						</tr>
						<tr>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td bgcolor="#ff0000" colspan="2">
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
								<td>
										<img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" />
								</td>
						</tr>
						<tr>
								<td align="middle" colspan="2">中</td>
								<td align="middle" colspan="2">文</td>
								<td align="middle" colspan="2">1</td>
								<td align="middle" colspan="2">2</td>
								<td align="middle" colspan="2">3</td>
								<td align="middle" colspan="2">\0</td>
								<td align="middle">　</td>
						</tr>
				</tbody>
		</table>
		<p>一共占 10 个字节。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>
				<a name="concept">
				</a>1.2 字符，字节，字符串</h5>
		<p>理解编码的关键，是要把字符的概念和字节的概念理解准确。这两个概念容易混淆，我们在此做一下区分：</p>
		<table cellspacing="0" cellpadding="3" border="0">
				<tbody>
						<tr>
								<td class="top_1">　</td>
								<td class="top_2" align="middle">
										<b>概念描述</b>
								</td>
								<td class="top_2" align="middle">
										<b>举例</b>
								</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">字符</td>
								<td class="con_2">人们使用的记号，抽象意义上的一个符号。</td>
								<td class="con_2">'1', '中', 'a', '$', '￥', ……</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">字节</td>
								<td class="con_2">计算机中存储数据的单元，一个8位的二进制数，是一个很具体的存储空间。</td>
								<td class="con_2">0x01, 0x45, 0xFA, ……</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">ANSI<br />字符串</td>
								<td class="con_2">在内存中，如果“字符”是以 <b>ANSI 编码</b>形式存在的，一个字符可能使用一个字节或多个字节来表示，那么我们称这种字符串为 <b>ANSI 字符串</b>或者<b>多字节字符串</b>。</td>
								<td class="con_2">"中文123"<br /><span class="rem"><font color="#339933">（占7字节）</font></span></td>
						</tr>
						<tr>
								<td class="bot_1" nowrap="" align="middle">UNICODE<br />字符串</td>
								<td class="bot_2">在内存中，如果“字符”是以在 UNICODE 中的序号存在的，那么我们称这种字符串为 <b>UNICODE 字符串</b>或者<b>宽字节字符串</b>。</td>
								<td class="bot_2">L"中文123"<br /><span class="rem"><font color="#339933">（占10字节）</font></span></td>
						</tr>
				</tbody>
		</table>
		<p>由于不同 ANSI 编码所规定的标准是不相同的，因此，对于一个给定的<b>多字节字符串</b>，我们必须知道它采用的是哪一种编码规则，才能够知道它包含了哪些“字符”。而对于 <b>UNICODE 字符串</b>来说，不管在什么环境下，它所代表的“字符”内容总是不变的。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>1.3 字符集与编码</h5>
		<p>各个国家和地区所制定的不同 ANSI 编码标准中，都只规定了各自语言所需的“字符”。比如：汉字标准（GB2312）中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义：</p>
		<ol>
				<li>使用哪些字符。也就是说哪些汉字，字母和符号会被收入标准中。所包含“字符”的集合就叫做“<b>字符集</b>”。 
</li>
				<li>规定每个“字符”分别用一个字节还是多个字节存储，用哪些字节来存储，这个规定就叫做“<b>编码</b>”。 </li>
		</ol>
		<p>各个国家和地区在制定编码标准的时候，“字符的集合”和“编码”一般都是同时制定的。因此，平常我们所说的“字符集”，比如：GB2312, GBK, JIS 等，除了有“字符的集合”这层含义外，同时也包含了“编码”的含义。</p>
		<p>“<b>UNICODE 字符集</b>”包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种，比如：UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>1.4 常用的编码简介</h5>
		<p>简单介绍一下常用的编码规则，为后边的章节做一个准备。在这里，我们根据编码规则的特点，把所有的编码分成三类：</p>
		<table cellspacing="0" cellpadding="3" border="0">
				<tbody>
						<tr>
								<td class="top_1" align="middle">
										<b>分类</b>
								</td>
								<td class="top_2" align="middle">
										<b>编码标准</b>
								</td>
								<td class="top_2" align="middle">
										<b>说明</b>
								</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">单字节字符编码</td>
								<td class="con_2">ISO-8859-1</td>
								<td class="con_2">最简单的编码规则，每一个字节直接作为一个 UNICODE 字符。比如，[0xD6, 0xD0] 这两个字节，通过 iso-8859-1 转化为字符串时，将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符，即 "?D"。<br /><br />反之，将 UNICODE 字符串通过 iso-8859-1 转化为字节串时，只能正常转化 0~255 范围的字符。</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">ANSI 编码</td>
								<td class="con_2">GB2312,<br />BIG5,<br />Shift_JIS,<br />ISO-8859-2 ……</td>
								<td class="con_2">把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时，根据各自编码的规定，一个 UNICODE 字符可能转化成一个字节或多个字节。<br /><br />反之，将字节串转化成字符串时，也可能多个字节转化成一个字符。比如，[0xD6, 0xD0] 这两个字节，通过 GB2312 转化为字符串时，将得到 [0x4E2D] 一个字符，即 '中' 字。<br /><br />“ANSI 编码”的特点：<br />1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。<br />2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。</td>
						</tr>
						<tr>
								<td class="bot_1" nowrap="" align="middle">UNICODE 编码</td>
								<td class="bot_2">UTF-8,<br />UTF-16, UnicodeBig ……</td>
								<td class="bot_2">与“ANSI 编码”类似的，把字符串通过 UNICODE 编码转化成“字节串”时，一个 UNICODE 字符可能转化成一个字节或多个字节。<br /><br />与“ANSI 编码”不同的是：<br />1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。<br />2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。</td>
						</tr>
				</tbody>
		</table>
		<p>我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节，我们只需要知道“编码”的概念就是把“字符”转化成“字节”就可以了。对于“UNICODE 编码”，由于它们是可以通过计算得到的，因此，在特殊的场合，我们可以去了解某一种“UNICODE 编码”是怎样的规则。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h4>
				<a name="implement">
				</a>2. 字符与编码在程序中的实现</h4>
		<h5>2.1 程序中的字符与字节</h5>
		<p>在 C++ 和 Java 中，用来代表“字符”和“字节”的数据类型，以及进行编码的方法：</p>
		<table cellspacing="0" cellpadding="3" border="0">
				<tbody>
						<tr>
								<td class="top_1" align="middle">
										<b>类型或操作</b>
								</td>
								<td class="top_2" align="middle">
										<b>C++</b>
								</td>
								<td class="top_2" align="middle">
										<b>Java</b>
								</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">字符</td>
								<td class="con_2">wchar_t</td>
								<td class="con_2">char</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">字节</td>
								<td class="con_2">char</td>
								<td class="con_2">byte</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">ANSI 字符串</td>
								<td class="con_2">char[]</td>
								<td class="con_2">byte[]</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">UNICODE 字符串</td>
								<td class="con_2">wchar_t[]</td>
								<td class="con_2">String</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">字节串→字符串</td>
								<td class="con_2">mbstowcs(), MultiByteToWideChar()</td>
								<td class="con_2">string = new String(bytes, "encoding")</td>
						</tr>
						<tr>
								<td class="bot_1" nowrap="" align="middle">字符串→字节串</td>
								<td class="bot_2">wcstombs(), WideCharToMultiByte()</td>
								<td class="bot_2">bytes = string.getBytes("encoding")</td>
						</tr>
				</tbody>
		</table>
		<p>以上需要注意几点：</p>
		<ol>
				<li>Java 中的 char 代表一个“UNICODE 字符（宽字节字符）”，而 C++ 中的 char 代表一个字节。 
</li>
				<li>MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。 </li>
		</ol>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>2.2 C++ 中相关实现方法</h5>
		<p>声明一段字符串常量：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<span class="rem">
												<font color="#339933">// ANSI 字符串，内容长度 7 字节</font>
										</span>
										<span class="key">
												<br />
												<font color="#0000ff">char</font>
										</span>     sz[<span class="number"><font color="#6d66a5">20</font></span>] = <span class="string"><font color="#ff00ff">"中文123"</font></span>;<br /><br /><font color="#339933"><span class="rem">// UNICODE 字符串，内容长度 5 个 wchar_t（10 字节）</span><br /></font>wchar_t wsz[<span class="number"><font color="#6d66a5">20</font></span>] = L<span class="string"><font color="#ff00ff">"\x4E2D\x6587\x0031\x0032\x0033"</font></span>;</td>
						</tr>
				</tbody>
		</table>
		<p>UNICODE 字符串的 I/O 操作，字符与字节的转换操作：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<span class="rem">
												<font color="#339933">// 运行时设定当前 ANSI 编码，VC 格式<br /></font>
										</span>setlocale(LC_ALL, <span class="string"><font color="#ff00ff">".936"</font></span>);<br /><br /><font color="#339933"><span class="rem">// GCC 中格式</span><br /></font>setlocale(LC_ALL, <span class="string"><font color="#ff00ff">"zh_CN.GBK"</font></span>);<br /><br /><font color="#339933"><span class="rem">// Visual C++ 中使用小写 %s，按照 setlocale 指定编码输出到文件<br />// GCC 中使用大写 %S</span><br /></font>fwprintf(fp, L<span class="string"><font color="#ff00ff">"%s\n"</font></span>, wsz);<br /><br /><font color="#339933"><span class="rem">// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节</span><br /></font>wcstombs(sz, wsz, <span class="number"><font color="#6d66a5">20</font></span>);<span class="rem"><br /><font color="#339933">// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串<br /></font></span>mbstowcs(wsz, sz, <span class="number"><font color="#6d66a5">20</font></span>);</td>
						</tr>
				</tbody>
		</table>
		<p>在 Visual C++ 中，UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符，则需要使用 #pragma setlocale，告诉编译器源程序使用的编码：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<span class="rem">
												<font color="#339933">// 如果源程序的编码与当前默认 ANSI 编码不一致，<br />// 则需要此行，编译时用来指明当前源程序使用的编码</font>
										</span>
										<span class="key">
												<br />
												<font color="#0000ff">#pragma setlocale</font>
										</span>(<span class="string"><font color="#ff00ff">".936"</font></span>)<br /><br /><font color="#339933"><span class="rem">// UNICODE 字符串常量，内容长度 10 字节</span><br /></font>wchar_t wsz[<span class="number"><font color="#6d66a5">20</font></span>] = L<span class="string"><font color="#ff00ff">"中文123"</font></span>;</td>
						</tr>
				</tbody>
		</table>
		<p>以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的，#pragma setlocale 在编译时起作用，setlocale() 在运行时起作用。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>2.3 Java 中相关实现方法</h5>
		<p>字符串类 String 中的内容是 UNICODE 字符串：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<span class="rem">
												<font color="#339933">// Java 代码，直接写中文</font>
										</span>
										<span class="pw">
												<br />
												<font color="#ff0000">String</font>
										</span>string = <span class="string"><font color="#ff00ff">"中文123"</font></span>;<br /><br /><font color="#339933"><span class="rem">// 得到长度为 5，因为是 5 个字符</span><br /></font><span class="pw"><font color="#ff0000">System</font></span>.out.println(string.length());</td>
						</tr>
				</tbody>
		</table>
		<p>字符串 I/O 操作，字符与字节转换操作。在 Java 包 java.io.* 中，以“Stream”结尾的类一般是用来操作“字节串”的类，以“Reader”，“Writer”结尾的类一般是用来操作“字符串”的类。</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<span class="rem">
												<font color="#339933">// 字符串与字节串间相互转化<br /><br />// 按照 GB2312 得到字节（得到多字节字符串）</font>
										</span>
										<span class="key">
												<br />
												<font color="#0000ff">byte</font>
										</span>[] bytes = string.getBytes(<span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br /><br /><font color="#339933"><span class="rem">// 从字节按照 GB2312 得到 UNICODE 字符串</span><br /></font>string = <span class="key"><font color="#0000ff">new</font></span><span class="pw"><font color="#ff0000">String</font></span>(bytes, <span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br /><br /><font color="#339933"><span class="rem">// 要将 String 按照某种编码写入文本文件，有两种方法：<br /><br />// 第一种办法：用 Stream 类写入已经按照指定编码转化好的字节串</span><br /></font>OutputStream os = <span class="key"><font color="#0000ff">new</font></span> FileOutputStream(<span class="string"><font color="#ff00ff">"1.txt"</font></span>);<br />os.write(bytes);<br />os.close();<br /><br /><font color="#339933"><span class="rem">// 第二种办法：构造指定编码的 Writer 来写入字符串</span><br /></font>Writer ow = <span class="key"><font color="#0000ff">new</font></span> OutputStreamWriter(<span class="key"><font color="#0000ff">new</font></span> FileOutputStream(<span class="string"><font color="#ff00ff">"2.txt"</font></span>), <span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br />ow.write(string);<br />ow.close();<br /><br /><span class="rem"><font color="#339933">/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */</font></span></td>
						</tr>
				</tbody>
		</table>
		<p>如果 java 的源程序编码与当前默认 ANSI 编码不符，则在编译的时候，需要指明一下源程序的编码。比如：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">E:\&gt;javac <font color="#ff0000">-encoding BIG5</font> Hello.java</td>
						</tr>
				</tbody>
		</table>
		<p>以上需要注意区分源程序的编码与 I/O 操作的编码，前者是在编译时起作用，后者是在运行时起作用。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h4>
				<a name="misunderstand">
				</a>3. 几种误解，以及乱码产生的原因和解决办法</h4>
		<h5>3.1 容易产生的误解</h5>
		<table cellspacing="0" cellpadding="3" border="0">
				<tbody>
						<tr>
								<td class="top_1">　</td>
								<td class="top_2" align="middle">
										<b>对编码的误解</b>
								</td>
						</tr>
						<tr>
								<td class="con_1" nowrap="" align="middle">误解一</td>
								<td class="con_2">在将“字节串”转化成“UNICODE 字符串”时，比如在读取文本文件时，或者通过网络传输文本时，容易将“字节串”简单地作为<b>单字节字符串</b>，采用每“一个字节”就是“一个字符”的方法进行转化。<br /><br />而实际上，在非英文的环境中，应该将“字节串”作为 ANSI 字符串，采用适当的编码来得到 UNICODE 字符串，有可能“多个字节”才能得到“一个字符”。<br /><br />通常，一直在英文环境下做开发的程序员们，容易有这种误解。</td>
						</tr>
						<tr>
								<td class="bot_1" nowrap="" align="middle">误解二</td>
								<td class="bot_2">在 DOS，Windows 98 等非 UNICODE 环境下，字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串，必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维：“字符串的编码”。<br /><br />当 UNICODE 被支持后，Java 中的 String 是以字符的“序号”来存储的，不是以“某种编码的字节”来存储的，因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时，或者，将一个“字节串”当成一个 ANSI 字符串时，才有编码的概念。<br /><br />不少的人都有这个误解。</td>
						</tr>
				</tbody>
		</table>
		<p>第一种误解，往往是导致乱码产生的原因。第二种误解，往往导致本来容易纠正的乱码问题变得更复杂。</p>
		<p>在这里，我们可以看到，其中所讲的“误解一”，即采用每“一个字节”就是“一个字符”的转化方法，实际上也就等同于采用 iso-8859-1 进行转化。因此，我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作，得到原始的“字节串”。然后再使用正确的 ANSI 编码，比如 string = new String(bytes, "GB2312")，来得到正确的“UNICODE 字符串”。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>
				<a name="instances">
				</a>3.2 非 UNICODE 程序在不同语言环境间移植时的乱码</h5>
		<p>非 UNICODE 程序中的字符串，都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同，将会导致 ANSI 字符串的显示失败。</p>
		<p>比如，在日文环境下开发的非 UNICODE 的日文程序界面，拿到中文环境下运行时，界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串，那么当在中文环境下运行时，界面上将可以显示正常的日文。</p>
		<p>由于客观原因，有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件，这时我们可以采用一些工具，比如，南极星，AppLocale 等，暂时的模拟不同的语言环境。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>3.3 网页提交字符串</h5>
		<p>当页面中的表单提交字符串时，首先把字符串按照当前页面的编码，转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如，一个编码为 GB2312 的页面，提交 "中" 这个字符串时，提交给服务器的内容为 "%D6%D0"。</p>
		<p>在服务器端，Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节，然后再根据 GB2312 编码规则得到 "中" 字。</p>
		<p>在 Tomcat 服务器中，request.getParameter() 得到乱码时，常常是因为前面提到的“误解一”造成的。默认情况下，当提交 "%D6%D0" 给 Tomcat 服务器时，request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符，而不是返回一个 "中" 字符。因此，我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串，再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>3.4 从数据库读取字符串</h5>
		<p>通过数据库客户端（比如 ODBC 或 JDBC）从数据库服务器中读取字符串时，客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时，客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。</p>
		<p>如果从数据库读取字符串时得到乱码，而数据库中存放的数据又是正确的，那么往往还是因为前面提到的“误解一”造成的。解决的办法还是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法，重新得到原始的字节串，再重新使用正确的编码转化成字符串。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h5>3.5 电子邮件中的字符串</h5>
		<p>当一段 Text 或者 HTML 通过电子邮件传送时，发送的内容首先通过一种指定的<b>字符编码</b>转化成“字节串”，然后再把“字节串”通过一种指定的<b>传输编码</b>（Content-Transfer-Encoding）进行转化得到另一串“字节串”。比如，打开一封电子邮件源代码，可以看到类似的内容：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">Content-Type: text/plain;<br />        <font color="#ff0000">charset="gb2312"</font><br /><font color="#ff0000">Content-Transfer-Encoding: base64</font><br /><br />sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==</td>
						</tr>
				</tbody>
		</table>
		<p>最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时，Base64 得到的“字节串”比 Quoted-Printable 更短。在对英文文本进行转化时，Quoted-Printable 得到的“字节串”比 Base64 更短。</p>
		<p>邮件的标题，用了一种更简短的格式来标注“字符编码”和“传输编码”。比如，标题内容为 "中"，则在邮件源代码中表示为：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<font color="#339933">
												<span class="rem">// 正确的标题格式</span>
												<br />
										</font>Subject: <span style="BACKGROUND-COLOR: #ffff00">=?</span>GB2312<span style="BACKGROUND-COLOR: #ffff00">?B?</span>1tA=<span style="BACKGROUND-COLOR: #ffff00">?=</span></td>
						</tr>
				</tbody>
		</table>
		<p>其中，</p>
		<ul>
				<li>第一个“=?”与“?”中间的部分指定了字符编码，在这个例子中指定的是 GB2312。 
</li>
				<li>“?”与“?”中间的“B”代表 Base64。如果是“Q”则代表 Quoted-Printable。 
</li>
				<li>最后“?”与“?=”之间的部分，就是经过 GB2312 转化成字节串，再经过 Base64 转化后的标题内容。 </li>
		</ul>
		<p>如果“传输编码”改为 Quoted-Printable，同样，如果标题内容为 "中"：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<font color="#339933">
												<span class="rem">// 正确的标题格式</span>
												<br />
										</font>Subject: <span style="BACKGROUND-COLOR: #ffff00">=?</span>GB2312<span style="BACKGROUND-COLOR: #ffff00">?Q?</span>=D6=D0<span style="BACKGROUND-COLOR: #ffff00">?=</span></td>
						</tr>
				</tbody>
		</table>
		<p>如果阅读邮件时出现乱码，一般是因为“字符编码”或“传输编码”指定有误，或者是没有指定。比如，有的发邮件组件在发送邮件时，标题 "中"：</p>
		<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td class="code">
										<font color="#339933">
												<span class="rem">// 错误的标题格式</span>
												<br />
										</font>Subject: <span style="BACKGROUND-COLOR: #ffff00">=?</span><font color="#ff0000">ISO-8859-1</font><span style="BACKGROUND-COLOR: #ffff00">?Q?</span>=D6=D0<span style="BACKGROUND-COLOR: #ffff00">?=</span></td>
						</tr>
				</tbody>
		</table>
		<p>这样的表示，实际上是明确指明了标题为 [0x00D6, 0x00D0]，即 "?D"，而不是 "中"。</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td align="right">
										<img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" border="0" />
								</td>
						</tr>
						<tr valign="top">
								<td align="right">
										<table cellspacing="0" cellpadding="0">
												<tbody>
														<tr align="right">
																<td>
																		<table cellspacing="0" cellpadding="0" border="0">
																				<tbody>
																						<tr>
																								<td valign="center">
																										<img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" />
																								</td>
																								<td valign="top" align="right">
																										<p>
																												<a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
																										</p>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<h4>
				<a name="correct">
				</a>4. 几种错误理解的纠正</h4>
		<h5>误解：“ISO-8859-1 是国际编码？”</h5>
		<p>非也。iso-8859-1 只是单字节字符集中最简单的一种，也就是“字节编号”与“UNICODE 字符编号”一致的那种编码规则。当我们要把一个“字节串”转化成“字符串”，而又不知道它是哪一种 ANSI 编码时，先暂时地把“每一个字节”作为“一个字符”进行转化，不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。</p>
		<h5>误解：“Java 中，怎样知道某个字符串的内码？”</h5>
		<p>Java 中，字符串类 java.lang.String 处理的是 UNICODE 字符串，不是 ANSI 字符串。我们只需要把字符串作为“抽象的符号的串”来看待。因此不存在字符串的内码的问题。</p>
<img src ="http://www.blogjava.net/shooper/aggbug/46507.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shooper/" target="_blank">Shooper.Java</a> 2006-05-16 23:46 <a href="http://www.blogjava.net/shooper/articles/46507.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给JAVA设计开发新手的一些建议和意见</title><link>http://www.blogjava.net/shooper/articles/45313.html</link><dc:creator>Shooper.Java</dc:creator><author>Shooper.Java</author><pubDate>Tue, 09 May 2006 15:57:00 GMT</pubDate><guid>http://www.blogjava.net/shooper/articles/45313.html</guid><wfw:comment>http://www.blogjava.net/shooper/comments/45313.html</wfw:comment><comments>http://www.blogjava.net/shooper/articles/45313.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shooper/comments/commentRss/45313.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shooper/services/trackbacks/45313.html</trackback:ping><description><![CDATA[
		<div class="clear">
		</div>
		<div class="item-body">
				<div class="item-content">
						<p align="center">作者：飞云小侠    来自：CSDN</p>
						<p>　　为了给朋友同事一些设计问题上的指导，特撰写此文，很多观点都是从别人的文章中获取，有些观点肯定也有偏颇，有些观点也仅仅是提出并没有做详细论述，请多拍砖，以便改正。 </p>
						<p>　　【<strong>概述</strong>】</p>
						<p>　　在工作中，作为一个程序员或者一个设计师，总是要设计一些函数库或者一个框架，当然最经常的还是做项目，即使是一个项目，也会被经常改动，甚至交给别人改动。<br />　　当你做这些工作的时候，你的这些成果都是要给别人了解使用的，或者说给以后的你使用的，为了别人的方便或者为了自己的方便，我们要尽可能做好设计。<br />　　</p>
						<p>　　【<strong>放正心态，任何东西都是不断发展的</strong>】</p>
						<p>　　技术是日新月异的，每一天都有新的技术出来，正所谓"山外有山，人外有人"，每一个新的轮子出来，都可能比你要设计的轮子好，所以在设计的时候，应该了解一下是否已经有了类似的轮子，是否要设计一个新的轮子。</p>
						<p>　　即使你的轮子已经设计好了，也不好认为自己的轮子一定比别人的轮子好，虽然你的轮子可能更适合你的实际使用。</p>
						<p>　　技术在不断的发展中，你以及你的朋友/同事都在不断进步，"士别三日，当刮目相看"，所以不要认为你的水平一定比别人高，"尺有所短，寸有所长"，所以别人对你的函数库/框架提出意见，提出疑问的时候，请不要惊奇，不要反感，不要认为别人在"挑刺"，也许你的函数库/框架早就不适合当前的发展了。<br />　　<br />　　态度决定一切。你的领导或许更重视这一点。<br />　　<br />　　【<strong>必要的组成部分:单元测试，文档，实例，手册etc</strong>】</p>
						<p>　　单元测试，文档，API Doc，手册，演示程序，Change Log，Readme，build。xml等等</p>
						<p>　　有一天别人使用了你设计的函数库/框架，当你升级后，原来的项目却不能工作了，经过一天的调试，你终于找到了原因，原来是不小心写错了一个东西。</p>
						<p>　　你肯定不希望上述的事情发生，那么请你写单元测试吧，这样既不浪费自己的时间，也不耽误别人的工作，何乐而不为。你花在写单元测试的时间/带来的乐趣和你升级后改正莫名其妙的错误的时间和苦恼相比，肯定更有价值。你看到单元测试的绿条，难道不感到高兴吗?!</p>
						<p>　　如果你不能保证你的程序修改没有错误，不要指望你的同事认为你的错误是可以容忍的，他们在心里早就开始骂你了，呵呵。写单元测试吧<br />　　<br />　　看看任何一个知名的框架，都包含完善的文档，单元测试，示例程序，用户手册，那么请你也包含这些吧。哦，对了，请详细地写好JavaDoc，它很重要。<br />　　<br />　　使用你的框架/函数库的人如果到处去找使用方法，去找某个类(但是他不知道是否有这个类)，那么说明你的文档没有到位。如果你希望别人使用你的这个类或者功能，那么请写好文档，不要指望别人去读你的源码然后就能理解它是干什么用的。<br />　　<br />　　如果你做到这些，那么你的函数库/框架也有了"知名"的前提，难道不是吗?如果没有，我想是没法让别人更好地使用的。<br />　　<br />　　对了，有了这些东西，还要有一个良好的目录组织，这个也可以参考别的框架的组织方式。</p>
						<p>　【<strong>借鉴成熟的设计，参考已有的项目</strong>】</p>
						<p>　　1. 要做一个新的东西，没有想法。不要惊讶，我肯定先找一个现有的东西来借鉴。<br />　　<br />　　当然前提是不要重新发明轮子，或者是你有充分条件要重新发明一个轮子。<br />　　<a href="http://www.javafan.net/special/struts/index.jsp" target="_blank">Struts</a>，WebWork，<a href="http://www.javafan.net/special/spring/index.jsp" target="_blank">Spring</a>等等都是成熟的框架，不管你使用起来是否符合你的习惯。<br />　　在你成为大师之前，你的设计思想估计前人都已经提出并实践过了，所以要勇敢地去借鉴。"站在巨人的肩膀上"我们能更近一步。<br />　　<br />　　例如我们厌倦了在访问数据库时使用如下的代码:</p>
						<p>　　try<br />　　{ <br />　　//your code here<br />　　}<br />　　catch(Exception e)<br />　　{<br />　　//catch Exception <br />　　}<br />　　finally<br />　　{<br />　　//must do something<br />　　} </p>
						<p>　　我们就可以借鉴Spring框架的JdbcTemplate类，看看它是如何利用回调函数来处理的。 <br />　　<br />　　我们使用<a href="http://www.javafan.net/special/hibernate/index.jsp" target="_blank">hibernate</a>时是不是也会使用类似上面的代码，那么可以参考Spring框架的HibernateTemplate。<br />　　<br />　　借鉴也是一种捷径。<br />　　<br />　　警告:借鉴但不要抄袭，借鉴代码要注明来源，尊重他人也是尊重自己。<br />　　<br />　　2. 在实际的项目中，往往可以参考已经有的项目来做自己的设计。<br />　　<br />　　例如做一个网站，我不知道如何访问数据库，如何布局，如何分层，那么我们可以参考已经有的网站程序，看看别人是如何利用SiteMesh或者tiles布局，如何使用Hibernate来访问数据库或者使用已经封装好的JDBC类来访问数据库，如何利用Struts，WebWork或者其他访问来分层。</p>
						<p>　　【<strong>遵守约定俗成的一些做法</strong>】 </p>
						<p>　　为了使别人更方便地使用你的东西，那么在设计一些通用的函数或者类的时候，请遵守通用的做法，不要与众不同，除非你的内部实现确实与众不同。</p>
						<p>　　例如实现一个类似ArrayList的类，那么请不要这样写:</p>
						<p>　　public int count()<br />　　{<br />　　return list.size();<br />　　}<br />　　public Item getItem(int i)<br />　　{<br />　　return list.get(i);<br />　　}<br />　　<br />　　而应该这样:</p>
						<p>　　public int size()<br />　　{<br />　　return list.size();<br />　　}<br />　　public Item get(int i)<br />　　{<br />　　return list.get(i);<br />　　}<br />　　<br />　　当然每个人都有自己的想法，如果你非常认为你原来的方式比普通的好，那么请提供2套方式供别人选择。它不会给你带来麻烦，只是一个一看就懂的做法，不用怀疑，这样做有好处。<br />　　<br />　　很多类的设计都有一些约定俗成的做法，那么在你设计一个新类的时候，先借鉴一下吧，多看看JDK的源码/文档，看看别人是怎么实现的。这更有助于推广你的成果。<br />　　　　<br />　　【<strong>不要迷信权威</strong>】 </p>
						<p>　　在使用已有的框架或者函数库时，不要认为所有的东西都是正确的或者是最好的最好，肯定不是。没有完美的东西，已经存在的东西在设计的时候因为种种局限或者因为作者的水平，对现在来说肯定存在不合理的设计，或者过于理想化的设计，而不能满足实际情况。<br />　　<br />　　不迷信权威，才能到达新的境界。</p>
						<p>　　【<strong>不要轻易排斥，不了解就不要草率发表意见，要严谨</strong>】</p>
						<p>　　在网上经常看到。Net和Java的比较/火拼，或者是Struts VS Webwork或者是其他等等，非常之多。经常看到的是一方对对方的东西不甚了解，就开始批评，结果说不到点子上，反而被嘲笑一番。<br />　　几种技术的比较有时候是必要的，例如技术选型的时候。但是如果一些对这些技术根本不了解的人来选型，来评判，你能对结果信服吗?<br />　　存在就是合理，任何技术都有其存在的理由，虽然有些东西早就过时了，但是在当时它也是应运而生的。<br />　　几种技术，都是来解决同样的问题，但是问题也有很多方面，解决方式也有很多种，每个人的想法也都不一样，思路也不一样，所以没有绝对符合要求的技术，但是应该有符合你的技术，不符合你的技术不等于也不满足别人的要求。所以不要轻易排斥别的东西。<br />　　<br />　　在做技术比较的时候，如果你不了解，那么请不要轻易发表意见，至少你可以亲自去了解，去实践之后在发表你的意见岂不是更好。<br />　　<br />　　在发表意见的时候，也要严谨，不要轻易下结论，要经过求证，否则一旦错误只会让对手笑话，让你的同事看不起你。例如你说Hibernate3不支持jdk1。3，那么最好去好好找到你的证据，否则就会成为错误。(Hibernate3支持jdk1。3) <br />　　<br />　　作为一个技术人员，严谨应该是我们的习惯之一，无论做开发还是做设计。</p>
						<p>　　【<strong>处理好你的异常</strong>】</p>
						<p>　　异常处理是Java编程中非常重要的一个部分。建议在使用异常之前阅读或者。<br />　　<br />　　下面从书中摘出几条建议:<br />　　* 绝对不要忽略异常<br />　　* 千万不要隐藏异常<br />　　* 仅在不正常的情况下使用异常<br />　　* 对可恢复的情况使用可检查异常，对程序错误使用运行时异常(RunTimeException)<br />　　* 给方法引发的异常做文档<br />　　* 在详细信息里面包括失败捕获信息<br />　　* 使用finally避免资源泄漏<br />　　* ....<br />　　<br />　　在这里特别提出的是，在开发中要特别处理NULL的情况，否则经常引发NullPointException异常，在Java里这是一个最令人头疼的异常了。<br />　　如果你的程序因为一个NULL值，而报了几十个NullPointException的话，不但得让人烦死，而且还非常难以找到错误所在。所以在Java中一定要注意这个问题。<br />　　如果你的函数不允许Null值，那么可以截获它，抛出一个异常，或者给客户更友好的提示，难道不好吗?<br />　　<br />　　让我们来看一个例子:</p>
						<p>　　public String getName(User aUser)<br />　　{<br />　　//如果aUser为Null，会发生什么情况<br />　　return aUser.getName();<br />　　}<br />　　<br />　　很明显，如果参数为Null，就会抛出异常。应该改为:<br />　　public String getName(User aUser)<br />　　{<br />　　if(null=aUser)<br />　　{<br />　　return "";<br />　　} <br />　　else <br />　　{<br />　　return aUser.getName();<br />　　}<br />　　}<br />　　<br />　　或者你要求参数不能为空，还可以抛出一个异常，强制使用者不能传入空值。<br />　　<br />　　还有经常被忽略的是RunTimeException和普通异常的区别，在Java中，这是一个特殊的异常类，程序中如果遇到这个异常，用户可以不截获它，而如果是其他的普通异常，就不许要截获它。我们的代码经常这么写:<br />　　try<br />　　{<br />　　//your code here<br />　　}<br />　　catch(Exception e)<br />　　{<br />　　//do warn<br />　　}</p>
						<p>　　这样写的话，就截获了所有异常，当然也包括了RunTimeException。 在很多情况下，这是不合适的处理方式，我们只应截获必要的异常，而应该忽略RuntimeException。<br />　　<br />　　关于RunTimeException，在Spring中还有更好的利用方式，建议阅读Spring框架中在事务中对异常的处理代码，例如对Jdbc抛出的SqlException的转换。<br />　　<br />　　关于异常处理，我提出几点建议:<br />　　* 捕获异常而且再次抛出时要包含原来的异常信息<br />　　* 不要忘了RunTimeException，除非必要，否则不要用catch(Exception e)的方式捕获所有异常。<br />　　* 不要用异常做流程控制，异常的性能代价比较高昂。(对此，可能有人不同意。此处不详细讨论)<br />　　* 不要把异常处理都抛给别人，本函数有能力处理的就不要抛出。<br />　　<br />　　在此建议读者详细阅读或者。<br />　　<br />　　【<strong>过度依赖</strong>】</p>
						<p>　　在定位错误的时候，经常遇到浏览了七 八个文件还是没有找到什么地方执行了真正需要的函数，这个时候就非常郁闷。A调用了B，B调用了C，C调用了D。。。。。。让人找不到北<br />　　<br />　　面对这样的程序，存在的问题不仅仅是定位错误麻烦，而且如果需要维护这样的函数库/框架，恐怕你的有非常高的统御能力才行，否则打死我也不去维护。<br />　　<br />　　那么我们自己最好不要写这样的程序出来给人用。</p>
						<p>　　【<strong>滥用接口</strong>】</p>
						<p>　　现在流行"面对接口编程"，这本身本来是不错，但是滥用接口的现象却经常发生。<br />　　"面向接口"，于是所有的类都有一个对应的接口，接口的函数声明和类一模一样，而且一个接口只有一个类来实现它。这样的面向接口有什么意义哪? (为了用Spring的事务的情况除外)<br />　　<br />　　根据"迪比特法则(Law of Demter)"，一个对象应当对其他对象有尽可能少的了解。一个接口内应该只定义对方所需要的方法，而不要把一些没用的方法声明放在接口里面。<br />　　<br />　　例如如下一个类:<br />　　<br />　　public class MyCounter<br />　　{<br />　　private int n1;<br />　　private int n2;<br />　　public MyCounter(int n1，int n2)<br />　　{<br />　　this。n1=n1;<br />　　this。n2=n2; <br />　　}<br />　　<br />　　public void setN1(int n1)<br />　　{<br />　　return this.n1 = n1;<br />　　}<br />　　public void setN2(int n2)<br />　　{<br />　　return this.n2 = n2;<br />　　}<br />　　public int getN1()<br />　　{<br />　　return n1;<br />　　}<br />　　public int getN2()<br />　　{<br />　　return n2;<br />　　}<br />　　<br />　　public int getResult()<br />　　{<br />　　return n1 + n2;<br />　　} <br />　　}</p>
						<p>　　我们可以看到，这个类的主要目的是得到计算结果，所以正确的接口应该类似:<br />　　　　<br />　　public interface Counter<br />　　{<br />　　int getResult();<br />　　} <br />　　<br />　　但是很多情况下，经常是这样的接口:<br />　　　　<br />　　public interface Counter<br />　　{<br />　　int getResult();<br />　　int getN1();<br />　　int getN2();<br />　　void setN1(int n1);<br />　　void setN2(int n2);<br />　　} <br />　　　　<br />　　我们想一想，这样做有2个后果:<br />　　1. 除了getResult之外，其他的函数我们根本用不到，所以是多余的。<br />　　2. 如果我们要自己实现一个Counter，如果接口中仅仅定义了getResult，我们仅仅需要实现它就可以了。我们自己的类可能是多个数运算，有乘除加减等等各种运算，参数也有可能是一些数组。但是如果按照第二种方法声明接口的话，我们就必须实现后面的四个方法，如果这样的话，实现这样东西不仅没用，而且浪费时间。我们恐怕要大声骂娘了吧。<br />　　<br />　　所以，接口有好的作用，但是不要滥用。<br />　　■ 如果你的接口永远只有一个类实现，那么可能就没有必要用接口。<br />　　■ 你的接口只需要声明别人用到的函数即可。</p>
						<p>　　<strong>【空接口的使用】</strong></p>
						<p>　　在接口使用的时候，空接口有2种情况:<br />　　1. 类似Cloneable，Serializable，他们往往是做一个标记，表示需要某个功能。当然你也可以这么用，来表示你的类具有某个功能，实现了你的某个接口。<br />　　2. 你的接口继承了别的接口(非空)，你的接口本身没有声明函数。这种情况一般是你不希望用户使用父接口来作为参数类型，因为他们的用途可能不同，此时就可以用空接口来实现。<br />　　<br />　　第一种情况我们不再多说，搜索一下关于Cloneable，Serializable的文章就会了解很多。<br />　　我们来看下面的代码:</p>
						<p>　　public interface Text<br />　　{<br />　　String getText();<br />　　}<br />　　<br />　　public interface SqlText extends Text<br />　　{<br />　　} </p>
						<p>　　可以看到，Text接口是用于返回一个字符串。而SqlText是一个空接口，它继承了Text接口。也就是说SqlText也是一种Text。但是我们可以知道，任何一个字符串不一定是Sql字符串，所以此时声明了一个SqlText接口来用于表名当前的字符串是一个Sql字符串。你的函数可以这样声明: </p>
						<p>　　public void doQuery(SqlText aSqlText)</p>
						<p>　　而不是这样<br />　　<br />　　public void doQuery(Text aText)</p>
						<p>　　避免用户产生歧义的想法，一眼看去，就明白应该传入一个Sql字符串。<br />　　<br />　　<strong>【继承层次过多】</strong><br />　　一般来说，继承的层次不要过多，否则使用者可能会讨厌，找一个函数会很麻烦。很多Java语言检查工具都建议你的继承层次不要超过3层。<br />　　<br />　　<strong>【Has A ，Is A，不要滥用继承】</strong></p>
						<p>　　"我是一个Mp3"，"我有一个Mp3"，其实很容易分辨。但是在实际应用中，往往存在把"我有一个Mp3"的情况当作"我是一个Mp3"，或者是为了偷懒方便而放松了对自己的要求，甚至还沾沾自喜，感觉找到一个捷径。(scud以前也干过这种事情)。<br />　　<br />　　以前我曾经这样干过:我的逻辑类直接继承了我的数据库访问类，这样我可以直接在逻辑类里面访问:<br />　　<br />　　public MyLogic extends MyDBA<br />　　<br />　　aLogic.getInt("click");<br />　　aLogic.getString("name");<br />　　<br />　　看起来是非常方便，但是你的逻辑类就牢牢绑在了DBA上，是一种非常不好的做法。现在我这样声明:</p>
						<p>　　public MyLogic<br />　　<br />　　MyDBA adba;<br />　　<br />　　adba.getInt("click");<br />　　adba.getString("name");</p>
						<p>　　其实代码改动不大，但是你的逻辑类不在牢牢绑在DBA身上了，何乐而不为。<br />　　<br />　　其实这种现象在开发人员中间可能经常见到，我们要尽量避免。下面再来看一个例子:<br />　　<br />　　//一个保存分页信息的类<br />　　<br />　　public class PageInfo<br />　　{<br />　　private int page;<br />　　private int pageCount;<br />　　private int recPerPage;<br />　　private int recCount; <br />　　<br />　　//get，set method list...<br />　　}</p>
						<p>　　一般的情况是，在Dao中进行分页查询，计算总记录，总页数等等，所以需要把PageInfo传给Dao。而在逻辑类中，把传回来的分页信息数据推到FormBean或者是Action中。<br />　　也许你会这么想，如果我的Action或者FormBean继承了PageInfo，岂不是要省很多事。<br />　　<br />　　千万别这么干。并不是所有的动作都需要分页信息，你的FormBean和PageInfo没有继承的关系。也就是说FormBean Has A PageInfo，但是不是Is A PageInfo。<br />　　<br />　　<strong>【保持外观/行为一致】</strong></p>
						<p>　　外观一致其实很容易理解，例如你用size()表示得到一个List的大小，那么在所有的List类中你都用size()得到它的大小，这就是外观一致。<br />　　外观一致让用户更方便使用你的函数库，不用记住几个不同的表示同一个功能的函数名字。或者几个名字相同功能却不同的函数。那就很糟糕了。<br />　　<br />　　行为一致相对外观一致就相对比较难做到，但是优秀的设计师肯定会让他的成果行为一致，而不是出人意料的行为，也不是一套强行规定的行为。<br />　　<br />　　我们来看下面的代码:<br />　　<br />　　import java.util.HashMap;<br />　　import java.util.Map;</p>
						<p>　　class UserInfo<br />　　{<br />　　private String realname;<br />　　<br />　　public UserInfo(String sName)<br />　　{<br />　　this.realname = sName;<br />　　}<br />　　<br />　　public void setName(String sName)<br />　　{<br />　　this.realname = sName;<br />　　}<br />　　public String getName()<br />　　{<br />　　return this.realname;<br />　　} <br />　　}<br />　　<br />　　public class MyTest<br />　　{<br />　　<br />　　Map userInfoMap = new HashMap(); <br />　　<br />　　public void setUserInfo(String sName,UserInfo aInfo)<br />　　{<br />　　userInfoMap.put(sName,aInfo);<br />　　<br />　　userInfoMap.put(aInfo.getName(),aInfo);<br />　　}<br />　　<br />　　public UserInfo getUserInfo(String sName)<br />　　{<br />　　return (UserInfo)userInfoMap.get(sName);<br />　　} <br />　　<br />　　public static void main(String args[])<br />　　{<br />　　MyTest aTest = new MyTest();<br />　　<br />　　UserInfo aUserInfo = new UserInfo("王小二");<br />　　<br />　　aTest.setUserInfo("儿童团团长",aUserInfo);<br />　　aTest.setUserInfo("三班班长",aUserInfo);<br />　　<br />　　UserInfo 儿童团团长 = aTest.getUserInfo("儿童团团长");<br />　　<br />　　if(null!=儿童团团长)<br />　　{ <br />　　System.out.println(儿童团团长.getName());<br />　　}<br />　　else<br />　　{<br />　　System.out.println("儿童团团长 Not Found");<br />　　}<br />　　<br />　　UserInfo 王小二 = aTest.getUserInfo("王小二");<br />　　<br />　　if(null!=王小二)<br />　　{ <br />　　System.out.println(王小二.getName());<br />　　}<br />　　else<br />　　{<br />　　System.out.println("王小二 Not Found");<br />　　}<br />　　<br />　　}<br />　　}<br />　　可以看到，上面的代码运行结果是"王小二"，也就是说儿童团团长是王小二，王小二本身也是王小二，这一切正常。<br />　　<br />　　现在我们把setUserInfo里面的第一句注释掉:<br />　　<br />　　public void setUserInfo(String sName,UserInfo aInfo)<br />　　{<br />　　//userInfoMap.put(sName,aInfo);<br />　　<br />　　userInfoMap.put(aInfo.getName(),aInfo);<br />　　}</p>
						<p>　　再次运行上面的代码，我们发现儿童团团长不存在了，但是王小二还在。还可以看出，如果找"三班班长"的话，肯定也找不到，也就是说只有依据王小二的真名才能找到王小二，其他方法就不行了。<br />　　<br />　　从上面的setUserInfo和getUserInfo分析，如果采用修改后的代码，我们的程序就出现了行为表现不一致，而这是令人迷惑不解的，我们set了半天，却找不到，岂不是令人恼火!<br />　　<br />　　当然上面的代码比较简单，通过简单的修改就能做到行为一致，但在实际编程中，往往因为复杂的行为操作，经常会造成行为不一致，从而给开发人员带来困惑。</p>
						<p>　　<strong>【MVC，MVC2，WEB设计编程的分层】</strong></p>
						<p>　　请阅读文章 <a href="http://forum.javaeye.com/viewtopic.php?t=11712&amp;postdays=0&amp;postorder=asc&amp;start=0" target="_blank">http://forum.javaeye.com/viewtopic.php?t=11712&amp;postdays=0&amp;postorder=asc&amp;start=0</a></p>
						<p>　　<strong>【可扩展不等于功能强大，不要夸大其辞】</strong></p>
						<p>　　现在的系统，因为接口或者其他方法的使用，都具有很大的扩展性。但是扩展性不等于功能强大。<br />　　存在一个接口，用户可以实现自己的接口，确实非常方便。但是如果你的系统本身只实现了一个接口或者根本没有实现，那么对用户来说就谈不上方便。<br />　　<br />　　例如WebWork的validators，本身是一个接口，但是实际上本身实现的具体类很少，而且功能很差，这个时候如果你说WebWork的校验器很厉害，那么就可能不太恰当了。当然扩展Webwork的Validator还是非常方便的。<br />　　<br />　　当然，可扩展性还是需要的，但是不要吹嘘，在这个浮躁的年代，让我们多干点实事。 :)</p>
						<p>　　<strong>【20/80原则】</strong><br />　　<br />　　在工作中，我经常想到20/80原则，也就是"巴雷多原则"。例如我们可以看到:</p>
						<p>　　时间：我们20%的时间会产生成果的80%<br />　　<br />　　产品：产品的20%带来利润的80%<br />　　<br />　　阅读：20%的书篇幅包括了内容的80%<br />　　<br />　　工作：20%的工作给我们80%的满意<br />　　<br />　　演讲：20%的演讲产生影响的80%<br />　　<br />　　领导：20%的人作出80%的决定</p>
						<p>　　从上面可以看出，很多时候它都很有说服力。<br />　　在这里我想提到几点，但是和上面的可能出发点有所不同:<br />　　<br />　　1、程序的80%都是在处理特殊情况，所以我们一定要对特殊情况重视，不要因为是特殊情况，就不很重视。80%的客户对特殊情况都很重视。<br />　　文档对特殊情况也要详细描述，因为开发人员80%的时候在查找这些东西，而对那些经常用到的用法却很少查阅文档。<br />　　<br />　　2、优化问题:80%的瓶颈都出在20%的代码上，所以在优化代码的时候不需要优化所有代码，只需要优化20%的关键代码就够了。当然追求完美的人我们就不多说了。 <br />　　记得有一条优化的原则是"不要优化!不要优化"，是非常有道理的。<br />　　<br />　　3、如果你20%的事情做砸了，往往会导致80%的事情都砸了，或者是导致别人认为你把事情几乎都做砸了。<br />　　如果你对一些事情发表了一些很不严谨的看法，那么别人会认为你在别的事情上也很不严谨。<br />　　依此类推，代码质量，文档完整性等等，都会让人产生类似的推理。<br />　　<br />　　(当然一个代码写的很乱的人，往往文档也很乱。)<br />　　<br />　　<strong>【强制绑定是不受欢迎的】</strong></p>
						<p>　　不要在程序中强制绑定一些额外的功能。<br />　　<br />　　有的框架往往功能很多，是"大型计算机"，有很多功能，但是在我需要打字的时候，给我打字的功能即可，不要强制我使用网络功能，打印功能，负载均衡功能等等。<br />　　<br />　　一般来说，如果一个东西有很多功能，那么做好做成可配置，可插拔的，这样用户使用你的东西，没必要在不使用高级功能的时候，浪费用户的内存，磁盘。开发人员还得多copy好多lib文件，占用调试时间，岂不是很麻烦。<br />　　<br />　　不要买一送一，我不想要就别给我。 :)</p>
						<p>　　<strong>【有时候也得考虑兼容性】</strong></p>
						<p>　　一般来说，一个公司的客户会有很多，用户的运行环境是各种各样的。jdk1.3，jdk1.4甚至还有jdk1.2。这样我们在编程的时候就必须做一些妥协，有些函数库就不能使用。<br />　　如果这些用户的jdk不能升级(一般来说都需要购买新的产品才能升级)，或者我们必须对这些情况妥协，那么我们就要在开发中考虑这些问题。<br />　　<br />　　例如以前，在Servlet 2.2的时候，因为没有setCharacterEncoding，我们必须手动对各种字符进行转换。当Servlet2.3的时候，可以使用这个函数了。但是为了客户考虑，我们只好没有升级还是使用原来的方法。(当然后来大多数用户都使用了新的App Server，我们就可以使用filter来处理编码问题了)。<br />　　<br />　　向下兼容性确实让人头疼，JDK1.5也发布好久了，不过我们现在也不能使用，只能自己没事测试测试。<br />　　<br />　　在编程的时候，一定要设置好IDE的兼容性设置，防止我们使用了不能使用的特性。Jbuilder，Eclipse都有类似的设置。<br />　　<br />　　<strong>【成本与现实，给用户以选择余地】</strong></p>
						<p>　　全文检索，lucene，like是三种对大文本字段检索的方法。那么你采用哪一种呢?<br />　　<br />　　也许你会毫不犹豫的说"全文检索" (我看你像TRS公司的托 :P)。<br />　　<br />　　正如"强制绑定是不受欢迎的"里面所说的一样，我还是觉得应该给用户以选择的余地。<br />　　<br />　　全文检索是要花钱的或者需要配置，而且一般来说数据库专用的全文检索都是不通用的，lucene是需要开发人员开发的，只有like最简单了，但是太简单了，而且性能也差。<br />　　<br />　　这个时候，也许我们就应该提供几种方式供用户选择了，用户如何选择那就看他们了。。。<br />　　<br />　　<strong>【结束语】</strong></p>
						<p>　　实际开发设计中肯定还存在很多其他的问题，本文不可能一一论述。到此为止。 :)<br />　　<br />　　希望各位在开发设计中成为高水平的设计师。 :)</p>
				</div>
		</div>
<img src ="http://www.blogjava.net/shooper/aggbug/45313.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shooper/" target="_blank">Shooper.Java</a> 2006-05-09 23:57 <a href="http://www.blogjava.net/shooper/articles/45313.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从Coding Fan到真正的技术专家</title><link>http://www.blogjava.net/shooper/articles/45303.html</link><dc:creator>Shooper.Java</dc:creator><author>Shooper.Java</author><pubDate>Tue, 09 May 2006 15:23:00 GMT</pubDate><guid>http://www.blogjava.net/shooper/articles/45303.html</guid><wfw:comment>http://www.blogjava.net/shooper/comments/45303.html</wfw:comment><comments>http://www.blogjava.net/shooper/articles/45303.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shooper/comments/commentRss/45303.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shooper/services/trackbacks/45303.html</trackback:ping><description><![CDATA[
		<div class="clear">
		</div>
		<div class="item-body">
				<div class="item-content">
						<p align="center">
								<font color="#0000ff">作者：happy_forever     来自：</font>
								<a href="http://www.javafan.net/">
										<font color="#0000ff">http://www.javafan.net</font>
								</a>
						</p>
						<p>
								<font color="#0000ff">
								</font>
						</p>
						<font color="#0000ff">以下文章都是经典，看不看随你的便，我只希望知识掌握在更多中国人的手里！</font>
						<p>
						</p>
						<p>
								<font color="#0000ff">中国有很多小朋友，他们18,9岁或21,2岁，通过自学也写了不少代码，他们有的代码写的很漂亮，一些技术细节相当出众，也很有钻研精神，但是他们被一些错误的认识和观点左右，缺乏对系统，对程序的整体理解能力，这些人，一个网上的朋友说得很好，他们实际上只是一些Coding fans，压根没有资格称为程序员，但是据我所知，不少小网络公司的CTO就是这样的coding fans,拿着吓人的工资，做着吓人的项目，项目的结局通常也很吓人。 </font>
						</p>
						<p>
								<font color="#0000ff">程序员基本素质： </font>
						</p>
						<p>
								<font color="#0000ff">作一个真正合格的程序员，或者说就是可以真正合格完成一些代码工作的程序员，应该具有的素质。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>1：团队精神和协作能力</strong><br />把它作为基本素质，并不是不重要，恰恰相反，这是程序员应该具备的最基本的，也是最重要的安身立命之本。把高水平程序员说成独行侠的都是在呓语，任何个人的力量都是有限的，即便如linus这样的天才，也需要通过组成强大的团队来创造奇迹，那些遍布全球的为linux写核心的高手们，没有协作精神是不可想象的。独行侠可以作一些赚钱的小软件发点小财，但是一旦进入一些大系统的研发团队，进入商业化和产品化的开发任务，缺乏这种素质的人就完全不合格了。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>2：文档习惯 <br /></strong>说高水平程序员从来不写文档的肯定是乳臭未干的毛孩子，良好的文档是正规研发流程中非常重要的环节，作为代码程序员，30％的工作时间写技术文档是很正常的，而作为高级程序员和系统分析员，这个比例还要高很多。缺乏文档，一个软件系统就缺乏生命力，在未来的查错，升级以及模块的复用时就都会遇到极大的麻烦。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>3：规范化，标准化的代码编写习惯</strong><br />作为一些外国知名软件公司的规矩，代码的变量命名，代码内注释格式，甚至嵌套中行缩进的长度和函数间的空行数字都有明确规定，良好的编写习惯，不但有助于代码的移植和纠错，也有助于不同技术人员之间的协作。 <br />有些coding fans叫嚣高水平程序员写的代码旁人从来看不懂，这种叫嚣只能证明他们自己压根不配自称程序员。代码具有良好的可读性，是程序员基本的素质需求。 <br />再看看整个linux的搭建，没有规范化和标准化的代码习惯，全球的研发协作是绝对不可想象的。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>4：需求理解能力</strong><br />程序员需要理解一个模块的需求，很多小朋友写程序往往只关注一个功能需求，他们把性能指标全部归结到硬件，操作系统和开发环境上，而忽视了本身代码的性能考虑，有人曾经放言说写一个广告交换程序很简单，这种人从来不知道在百万甚至千万数量级的访问情况下的性能指标是如何实现的，对于这样的程序员，你给他深蓝那套系统，他也做不出太极链的并访能力。性能需求指标中，稳定性，并访支撑能力以及安全性都很重要，作为程序员需要评估该模块在系统运营中所处的环境，将要受到的负荷压力以及各种潜在的危险和恶意攻击的可能性。就这一点，一个成熟的程序员至少需要2到3年的项目研发和跟踪经验才有可能有心得。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>5：复用性，模块化思维能力</strong><br />经常可以听到一些程序员有这样的抱怨，写了几年程序，变成了熟练工，每天都是重复写一些没有任何新意的代码，这其实是中国软件人才最大浪费的地方，一些重复性工作变成了熟练程序员的主要工作，而这些，其实是完全可以避免的。 <br />复用性设计，模块化思维就是要程序员在完成任何一个功能模块或函数的时候，要多想一些，不要局限在完成当前任务的简单思路上，想想看该模块是否可以脱离这个系统存在，是否可以通过简单的修改参数的方式在其他系统和应用环境下直接引用，这样就能极大避免重复性的开发工作，如果一个软件研发单位和工作组能够在每一次研发过程中都考虑到这些问题，那么程序员就不会在重复性的工作中耽误太多时间，就会有更多时间和精力投入到创新的代码工作中去。 <br />一些好的程序模块代码，即便是70年代写成的，拿到现在放到一些系统里面作为功能模块都能适合的很好，而现在我看到的是，很多小公司软件一升级或改进就动辄全部代码重写，大部分重复性工作无谓的浪费了时间和精力。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>6：测试习惯 </strong><br />作为一些商业化正规化的开发而言，专职的测试工程师是不可少的，但是并不是说有了专职的测试工程师程序员就可以不进行自测；软件研发作为一项工程而言，一个很重要的特点就是问题发现的越早，解决的代价就越低，程序员在每段代码，每个子模块完成后进行认真的测试，就可以尽量将一些潜在的问题最早的发现和解决，这样对整体系统建设的效率和可靠性就有了最大的保证。 <br />测试工作实际上需要考虑两方面，一方面是正常调用的测试，也就是看程序是否能在正常调用下完成基本功能，这是最基本的测试职责，可惜在很多公司这成了唯一的测试任务，实际上还差的远那；第二方面就是异常调用的测试，比如高压力负荷下的稳定性测试，用户潜在的异常输入情况下的测试，整体系统局部故障情况下该模块受影响状况的测试，频发的异常请求阻塞资源时的模块稳定测试等等。当然并不是程序员要对自己的每段代码都需要进行这种完整测试，但是程序员必须清醒认识自己的代码任务在整体项目中的地位和各种性能需求，有针对性的进行相关测试并尽早发现和解决问题，当然这需要上面提到的需求理解能力。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>7：学习和总结的能力</strong><br />程序员是人才很容易被淘汰，很容易落伍的职业，因为一种技术可能仅仅在三两年内具有领先性，程序员如果想安身立命，就必须不断跟进新的技术，学习新的技能。 <br />善于学习，对于任何职业而言，都是前进所必需的动力，对于程序员，这种要求就更加高了。但是学习也要找对目标，一些小coding fans们，他们也津津乐道于他们的学习能力，一会学会了asp，一会儿学会了php，一会儿学会了jsp，他们把这个作为炫耀的资本，盲目的追逐一些肤浅的，表面的东西和名词，做网络程序不懂通讯传输协议，做应用程序不懂中断向量处理，这样的技术人员，不管掌握了多少所谓的新语言，永远不会有质的提高。 <br />善于总结，也是学习能力的一种体现，每次完成一个研发任务，完成一段代码，都应当有目的的跟踪该程序的应用状况和用户反馈，随时总结，找到自己的不足，这样逐步提高，一个程序员才可能成长起来。 <br />一个不具备成长性的程序员，即便眼前看是个高手，建议也不要选用，因为他落伍的时候马上就到了。 <br />具备以上全部素质的人，应当说是够格的程序员了，请注意以上的各种素质都不是由IQ决定的，也不是大学某些课本里可以学习到的，需要的仅仅是程序员对自己工作的认识，是一种意识上的问题。 </font>
						</p>
						<p>
								<font color="#0000ff">那么作为高级程序员，以至于系统分析员，也就是对于一个程序项目的设计者而言，除了应该具备上述全部素质之外，还需要具备以下素质： </font>
						</p>
						<p>
								<font color="#0000ff"><strong>第一，需求分析能力</strong><br />对于程序员而言，理解需求就可以完成合格的代码，但是对于研发项目的组织和管理者，他们不但要理解客户需求，更多时候还要自行制定一些需求，为什么这么说呢？ <br />一般而言，进行研发任务，也许是客户提出需求，也许是市场和营销部门提出的需求，这时候对于研发部门，他们看到的不是一个完整的需求，通常而言，该需求仅仅是一些功能上的要求，或者更正规些，可能获得一个完整的用户视图；但是这都不够，因为客户由于非技术因素多一些，他们可能很难提出完整和清晰，或者说专业性的性能需求，但是对于项目组织者和规划者，他必须能够清醒认识到这些需求的存在并在完成需求分析报告的时候适当的提出，同时要完整和清晰的体现在设计说明书里面，以便于程序员编码时不会失去这些准则。 <br />程序设计者必须正确理解用户需求所处的环境，并针对性做出需求的分析，举例而言，同样一个软件通过ASP租用方式发布和通过License方式发布，性能需求可能就是有区别的，前者强调的是更好的支撑能力和稳定性，而后者则可能更强调在各种平台下的普适性和安装使用的简捷性。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>第二，项目设计方法和流程处理能力</strong><br />程序设计者必须能够掌握不少于两到三种的项目设计方法（比如自顶至下的设计方法，比如快速原型法等等），并能够根据项目需求和资源搭配来选择合适的设计方法进行项目的整体设计。设计方法上选择不当，就会耽误研发周期，浪费研发资源，甚至影响研发效果。 <br />一个程序设计者还需要把很多功夫用在流程图的设计和处理上，他需要做数据流图以确立数据词典；他需要加工逻辑流图以形成整体的系统处理流程。一个流程有问题的系统，就算代码多漂亮，每个模块多精致，也不会成为一个好的系统。当然，做好流程分析并选择好项目设计方法，都需要在需求分析能力上具有足够的把握。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>第三，复用设计和模块化分解能力</strong><br />这个似乎又是老调重谈，前面基本素质上不是已经说明了这个问题吗？ <br />作为一个从事模块任务的程序员，他需要对他所面对的特定功能模块的复用性进行考虑，而作为一个系统分析人员，他要面对的问题复杂的多，需要对整体系统按照一种模块化的分析能力分解为很多可复用的功能模块和函数，并针对每一模块形成一个独立的设计需求。举个例子，好比是汽车生产，最早每辆汽车都是独立安装的，每个部件都是量身定做的，但是后来不一样了，机器化大生产了，一个汽车厂开始通过流水线来生产汽车，独立部件开始具有一定的复用性，在后来标准化成为大趋势，不同型号，品牌甚至不同厂商的汽车部件也可以进行方便的换装和升级，这时候，汽车生产的效率达到最大化。软件工程也是同样的道理，一个成熟的软件行业，在一些相关项目和系统中，不同的部件是可以随意换装的，比如微软的许多桌面软件，在很多操作模块（如打开文件，保存文件等等）都是复用的同一套功能模块，而这些接口又通过一些类库提供给了桌面应用程序开发者方便挂接，这就是复用化的模块设计明显的一个佐证。 <br />将一个大型的，错综复杂的应用系统分解成一些相对独立的，具有高度复用性的，并能仅仅依靠几个参数完成数据联系的模块组合，是作为高级程序员和系统分析员一项最重要的工作，合适的项目设计方法，清晰的流程图，是实现这一目标的重要保证。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>第四，整体项目评估能力</strong><br />作为系统设计人员，必须能够从全局出发，对项目又整体的清醒认识，比如公司的资源配置是否合理和到位，比如工程进度安排是否能最大化体现效率又不至于无法按期完成。评估项目整体和各个模块的工作量，评估项目所需的资源，评估项目可能遇到的困难，都需要大量的经验积累，换言之，这是一种不断总结的累计才能达到的境界。在西方一些软件系统设计的带头人都是很年长的，比如4，50岁，甚至更老，他们在编码方面已经远远不如年轻人那样活络，但是就项目评估而言，他们几十年的经验积累就是最重要和宝贵的财富。中国缺这么一代程序员，主要还不是缺那种年纪的程序员，而是那种年纪的程序员基本上都是研究单位作出来的，都不是从专业的产品化软件研发作出来的，他们没有能积累那种产品化研发的经验，这也是没有办法的事情。 </font>
						</p>
						<p>
								<font color="#0000ff"><strong>第五，团队组织管理能力</strong><br />完成一个项目工程，需要团队的齐心协力，作为项目设计者或研发的主管人，就应当有能力最大化发挥团队的整体力量，技术管理由于其专业性质，不大同于一般的人事管理，因为这里面设计了一些技术性的指标和因素。 <br />首先是工作的量化，没有量化就很难做到合适的绩效考核，而程序量化又不是简单的代码行数可以计算的，因此要求技术管理人员需要能真正评估一个模块的复杂性和工作量。 <br />其次是对团队协作模式的调整，一般而言，程序开发的协作通常分为小组进行，小组有主程序员方式的，也有民主方式的，根据程序员之间的能力水平差距，以及根据项目研发的需求，选择合适的组队方式，并能将责权和成员的工作任务紧密结合，这样才能最大发挥组队的效率。 <br />一个代码水平高的人，未必能成为一个合格的项目研发主管，这方面的能力欠缺往往是容易被忽视的。 </font>
						</p>
						<p>
								<font color="#0000ff">综上可以看到，作为一个主管研发的负责人，一个项目设计者，所需要具备的素质和能力并不是程序代码编写的能力，当然一般情况下，一个程序员通过不断的总结提高达到了这种素质的时候，他所具有的代码编写能力也已经相当不简单了，但是请注意这里面的因果关系，一个高水平的项目设计者通常已经是代码编写相当优秀的人了，但是并不是一个代码相当优秀的程序员就可以胜任项目设计的工作，这里面存在的也不是智商和课本的问题，还是在于一个程序员在积累经验，逐步提升的时候没有意识到应当思考哪方面的东西，没有有意识的就项目的组织和复用设计进行揣摩，没有经常性的文档习惯和总结习惯，不改变这些，我们的合格的项目设计者还是非常欠缺。 </font>
						</p>
						<p>
								<font color="#0000ff">另外，为防止有无聊的人和我较真，补充一点，本文针对目标是作商业化的软件项目和工程，那些科研机构的编程高手，比如算法高手，比如图象处理高手，他们的工作是研究课题而非直接完成商业软件（当然最终间接成为商业产品，比如微软研究院在作的研究课题），因此他们强调的素质可能是另外的东西，这些人（专家），并不能说是程序员，不能用程序员的标准去衡量。 </font>
						</p>
						<p>
								<font color="#0000ff">最后补充一点东西，一个软件项目研发的设计流程是怎样的呢？以通常标准的设计方法为例，（不过笔者喜欢快速原型法）。 </font>
						</p>
						<p>
								<font color="#0000ff">第一个步骤是<strong>市场调研</strong>，技术和市场要结合才能体现最大价值。 </font>
						</p>
						<p>
								<font color="#0000ff">第二个步骤是<strong>需求分析</strong>，这个阶段需要出三样东西，用户视图，数据词典和用户操作手册。用户视图是该软件用户（包括终端用户和管理用户）所能看到的页面样式，这里面包含了很多操作方面的流程和条件。数据词典是指明数据逻辑关系并加以整理的东东，完成了数据词典，数据库的设计就完成了一半多。用户操作手册是指明了操作流程的说明书。请注意，用户操作流程和用户视图是由需求决定的，因此应该在软件设计之前完成，完成这些，就为程序研发提供了约束和准绳，很遗憾太多公司都不是这样做的，因果颠倒，顺序不分，开发工作和实际需求往往因此产生隔阂脱节的现象。 <br />需求分析，除了以上工作，笔者以为作为项目设计者应当完整的做出项目的性能需求说明书，因为往往性能需求只有懂技术的人才可能理解，这就需要技术专家和需求方（客户或公司市场部门）能够有真正的沟通和了解。 </font>
						</p>
						<p>
								<font color="#0000ff">第三个步骤是<strong>概要设计</strong>，将系统功能模块初步划分，并给出合理的研发流程和资源要求。作为快速原型设计方法，完成概要设计就可以进入编码阶段了，通常采用这种方法是因为涉及的研发任务属于新领域，技术主管人员一上来无法给出明确的详细设计说明书，但是并不是说详细设计说明书不重要，事实上快速原型法在完成原型代码后，根据评测结果和经验教训的总结，还要重新进行详细设计的步骤。 </font>
						</p>
						<p>
								<font color="#0000ff">第四个步骤是<strong>详细设计</strong>，这是考验技术专家设计思维的重要关卡，详细设计说明书应当把具体的模块以最‘干净’的方式(黑箱结构）提供给编码者，使得系统整体模块化达到最大；一份好的详细设计说明书，可以使编码的复杂性减低到最低，实际上，严格的讲详细设计说明书应当把每个函数的每个参数的定义都精精细细的提供出来，从需求分析到概要设计到完成详细设计说明书，一个软件项目就应当说完成了一半了。换言之，一个大型软件系统在完成了一半的时候，其实还没有开始一行代码工作。那些把作软件的程序员简单理解为写代码的，就从根子上犯了错误了。 </font>
						</p>
						<p>
								<font color="#0000ff">第五个步骤是<strong>编码</strong>，在规范化的研发流程中，编码工作在整个项目流程里最多不会超过1/2，通常在1/3的时间，所谓磨刀不误砍柴功，设计过程完成的好，编码效率就会极大提高，编码时不同模块之间的进度协调和协作是最需要小心的，也许一个小模块的问题就可能影响了整体进度，让很多程序员因此被迫停下工作等待，这种问题在很多研发过程中都出现过。编码时的相互沟通和应急的解决手段都是相当重要的，对于程序员而言，bug永远存在，你必须永远面对这个问题，大名鼎鼎的微软，可曾有连续三个月不发补丁的时候吗？从来没有！ </font>
						</p>
						<p>
								<font color="#0000ff">第六个步骤是<strong>测试</strong>，测试有很多种：按照测试执行方，可以分为内部测试和外部测试；按照测试范围，可以分为模块测试和整体联调；按照测试条件，可以分为正常操作情况测试和异常情况测试；按照测试的输入范围，可以分为全覆盖测试和抽样测试。以上都很好理解，不再解释。 <br />总之，测试同样是项目研发中一个相当重要的步骤，对于一个大型软件，3个月到1年的外部测试都是正常的，因为永远都会又不可预料的问题存在。 <br />完成测试后，完成验收并完成最后的一些帮助文档，整体项目才算告一段落，当然日后少不了升级，修补等等工作，只要不是想通过一锤子买卖骗钱，就要不停的跟踪软件的运营状况并持续修补升级，知道这个软件被彻底淘汰为止。 </font>
						</p>
						<p>
								<font color="#0000ff">写这些步骤算不上卖弄什么，因为实话讲我手边是一本《软件工程》，在大学里这是计算机专业的必修课程，但是我知道很多程序员似乎从来都只是热衷于什么《30天精通VC》之类的，他们有些和我一样游击队出身，没有正规学过这个专业，还有一些则早就在混够学分后就把这些真正有用的东西还给了老师。 </font>
						</p>
						<p>
								<font color="#0000ff">网上现在也很浮躁，一些coding fans乱嚷嚷，混淆视听，实际上真正的技术专家很少在网上乱发帖子的，如笔者这样不知天高地厚的，其实实在是算不上什么高手，只不过看不惯这种对技术，对程序员的误解和胡说，只好挺身而出，做拨乱反正之言，也希望那些还沉迷于一些错误人士的coding fans们能认真想想，走到正途上，毕竟那些聪明的头脑还远远没有发挥应有的价值。</font>
						</p>
						<div class="clear">
						</div>
				</div>
		</div>
<img src ="http://www.blogjava.net/shooper/aggbug/45303.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shooper/" target="_blank">Shooper.Java</a> 2006-05-09 23:23 <a href="http://www.blogjava.net/shooper/articles/45303.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>