﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-够了,让我们实践吧..-随笔分类-Java</title><link>http://www.blogjava.net/zhuzi1987/category/36118.html</link><description>JAVA+YOU!中国的开发者们加油!!</description><language>zh-cn</language><lastBuildDate>Sat, 06 Dec 2008 15:38:31 GMT</lastBuildDate><pubDate>Sat, 06 Dec 2008 15:38:31 GMT</pubDate><ttl>60</ttl><item><title>【转】垃圾回收与强引用，软引用，弱引用，幻引用的关系(二)</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244803.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sat, 06 Dec 2008 13:14:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244803.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/244803.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244803.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/244803.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/244803.html</trackback:ping><description><![CDATA[運行看結果，慢慢了解<br />
import&nbsp;java.lang.ref.ReferenceQueue;<br />
import&nbsp;java.lang.ref.SoftReference;<br />
import&nbsp;java.lang.ref.WeakReference;<br />
<br />
<br />
public&nbsp;class&nbsp;Testone&nbsp;{<br />
public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[]){<br />
A&nbsp;a=new&nbsp;A();<br />
//a.test();<br />
//SoftReference&nbsp;sr&nbsp;=&nbsp;new&nbsp;SoftReference(a);<br />
ReferenceQueue&lt;A&gt;&nbsp;rq&nbsp;=&nbsp;new&nbsp;ReferenceQueue&lt;A&gt;();<br />
WeakReference&lt;A&gt;&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference&lt;A&gt;(a,&nbsp;rq);<br />
a&nbsp;=&nbsp;null;<br />
System.out.println(wr.get());<br />
System.out.println(rq.poll());<br />
System.gc();<br />
System.runFinalization();<br />
System.out.println(wr.get());<br />
System.out.println(rq.poll());<br />
if&nbsp;(wr&nbsp;!=&nbsp;null)&nbsp;{<br />
a&nbsp;=&nbsp;(A)wr.get();<br />
System.out.println("asdasdas");<br />
a.test();<br />
}<br />
else{<br />
a&nbsp;=&nbsp;new&nbsp;A();<br />
System.out.println("123123");<br />
a.test();<br />
a&nbsp;=&nbsp;null;<br />
wr&nbsp;=&nbsp;new&nbsp;WeakReference&lt;A&gt;(a);<br />
}<br />
<br />
}<br />
}<br />
class&nbsp;A{<br />
void&nbsp;test(){<br />
System.out.println("A.test()");<br />
}<br />
} <br />
<img src ="http://www.blogjava.net/zhuzi1987/aggbug/244803.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-12-06 21:14 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244803.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】垃圾回收与强引用，软引用，弱引用，幻引用的关系(一)</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244802.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sat, 06 Dec 2008 13:13:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244802.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/244802.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244802.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/244802.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/244802.html</trackback:ping><description><![CDATA[<h1 style="font-size: 18px; margin-bottom: 8px">垃圾回收与强引用，软引用，弱引用，幻引用的关系（一）</h1>
Java&nbsp;2&nbsp;平台引入了&nbsp;java.lang.ref&nbsp;包，其中包括的类可以让您引用对象，而不将它们留在内存中。这些类还提供了与垃圾收集器（garbage&nbsp;collector）之间有限的交互。Peter&nbsp;Haggar&nbsp;在本文中分析了&nbsp;SoftReference、WeakReference&nbsp;和&nbsp;PhantomReference&nbsp;类的功能和行为，并就这些类的使用给出了一些编程风格上的建议。<br />
当在&nbsp;Java&nbsp;2&nbsp;平台中首次引入&nbsp;java.lang.ref&nbsp;包（其中包含&nbsp;SoftReference、WeakReference&nbsp;和&nbsp;PhantomReference&nbsp;类）时，它的实用性显然被过分夸大了。它包含的类可能是有用的，但这些类具有的某些局限性会使它们显得不是很有吸引力，而且其应用程序也将特别局限于解决一类特定的问题。<br />
<br />
垃圾收集概述<br />
引用类的主要功能就是能够引用仍可以被垃圾收集器回收的对象。在引入引用类之前，我们只能使用强引用（strong&nbsp;reference）。举例来说，下面一行代码显示的就是强引用&nbsp;obj：<br />
<br />
<br />
Object&nbsp;obj&nbsp;=&nbsp;new&nbsp;Object();<br />
<br />
<br />
<br />
<br />
obj&nbsp;这个引用将引用堆中存储的一个对象。只要&nbsp;obj&nbsp;引用还存在，垃圾收集器就永远不会释放用来容纳该对象的存储空间。<br />
<br />
当&nbsp;obj&nbsp;超出范围或被显式地指定为&nbsp;null&nbsp;时，垃圾收集器就认为没有对这个对象的其它引用，也就可以收集它了。然而您还需要注意一个重要的细节：仅凭对象可以被收集并不意味着垃圾收集器的一次指定运行就能够回收它。由于各种垃圾收集算法有所不同，某些算法会更频繁地分析生存期较短的对象，而不是较老、生存期较长的对象。因此，一个可供收集的对象可能永远也不会被回收。如果程序在垃圾收集器释放对象之前结束，这种情况就可能会出现。因此，概括地说，您永远无法保证可供收集的对象总是会被垃圾收集器收集。<br />
<br />
这些信息对于您分析引用类是很重要的。由于垃圾收集有着特定的性质，所以引用类实际上可能没有您原来想像的那么有用，尽管如此，它们对于特定问题来说还是很有用的类。软引用（soft&nbsp;reference）、弱引用（weak&nbsp;reference）和虚引用（phantom&nbsp;reference）对象提供了三种不同的方式来在不妨碍收集的情况下引用堆对象。每种引用对象都有不同的行为，而且它们与垃圾收集器之间的交互也有所不同。此外，这几个新的引用类都表现出比典型的强引用&#8220;更弱&#8221;的引用形式。而且，内存中的一个对象可以被多个引用（可以是强引用、软引用、弱引用或虚引用）引用。在进一步往下讨论之前，让我们来看看一些术语：<br />
<br />
强可及对象（strongly&nbsp;reachable）：可以通过强引用访问的对象。<br />
<br />
<br />
软可及对象（softly&nbsp;reachable）：不是强可及对象，并且能够通过软引用访问的对象。<br />
<br />
<br />
弱可及对象（weakly&nbsp;reachable）：不是强可及对象也不是软可及对象，并且能够通过弱引用访问的对象。<br />
<br />
<br />
虚可及对象（phantomly&nbsp;reachable）：不是强可及对象、软可及对象，也不是弱可及对象，已经结束的，可以通过虚引用访问的对象。<br />
<br />
<br />
清除：将引用对象的&nbsp;referent&nbsp;域设置为&nbsp;null，并将引用类在堆中引用的对象声明为可结束的。<br />
SoftReference&nbsp;类<br />
SoftReference&nbsp;类的一个典型用途就是用于内存敏感的高速缓存。SoftReference&nbsp;的原理是：在保持对对象的引用时保证在&nbsp;JVM&nbsp;报告内存不足情况之前将清除所有的软引用。关键之处在于，垃圾收集器在运行时可能会（也可能不会）释放软可及对象。对象是否被释放取决于垃圾收集器的算法以及垃圾收集器运行时可用的内存数量。&nbsp;<br />
<br />
WeakReference&nbsp;类<br />
WeakReference&nbsp;类的一个典型用途就是规范化映射（canonicalized&nbsp;mapping）。另外，对于那些生存期相对较长而且重新创建的开销也不高的对象来说，弱引用也比较有用。关键之处在于，垃圾收集器运行时如果碰到了弱可及对象，将释放&nbsp;WeakReference&nbsp;引用的对象。然而，请注意，垃圾收集器可能要运行多次才能找到并释放弱可及对象。<br />
<br />
PhantomReference&nbsp;类<br />
PhantomReference&nbsp;类只能用于跟踪对被引用对象即将进行的收集。同样，它还能用于执行&nbsp;pre-mortem&nbsp;清除操作。PhantomReference&nbsp;必须与&nbsp;ReferenceQueue&nbsp;类一起使用。需要&nbsp;ReferenceQueue&nbsp;是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时，PhantomReference&nbsp;对象就被放在它的&nbsp;ReferenceQueue&nbsp;上。将&nbsp;PhantomReference&nbsp;对象放在&nbsp;ReferenceQueue&nbsp;上也就是一个通知，表明&nbsp;PhantomReference&nbsp;对象引用的对象已经结束，可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。&nbsp;&nbsp;<br />
<br />
垃圾收集器和引用交互<br />
垃圾收集器每次运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了软可及对象，就会出现下列情况：<br />
<br />
SoftReference&nbsp;对象的&nbsp;referent&nbsp;域被设置为&nbsp;null，从而使该对象不再引用&nbsp;heap&nbsp;对象。<br />
<br />
<br />
SoftReference&nbsp;引用过的&nbsp;heap&nbsp;对象被声明为&nbsp;finalizable。<br />
<br />
<br />
当&nbsp;heap&nbsp;对象的&nbsp;finalize()&nbsp;方法被运行而且该对象占用的内存被释放，SoftReference&nbsp;对象就被添加到它的&nbsp;ReferenceQueue（如果后者存在的话）。<br />
如果垃圾收集器发现了弱可及对象，就会出现下列情况：<br />
<br />
WeakReference&nbsp;对象的&nbsp;referent&nbsp;域被设置为&nbsp;null，从而使该对象不再引用&nbsp;heap&nbsp;对象。<br />
<br />
<br />
WeakReference&nbsp;引用过的&nbsp;heap&nbsp;对象被声明为&nbsp;finalizable。<br />
<br />
<br />
当&nbsp;heap&nbsp;对象的&nbsp;finalize()&nbsp;方法被运行而且该对象占用的内存被释放时，WeakReference&nbsp;对象就被添加到它的&nbsp;ReferenceQueue（如果后者存在的话）。<br />
如果垃圾收集器发现了虚可及对象，就会出现下列情况：<br />
<br />
PhantomReference&nbsp;引用过的&nbsp;heap&nbsp;对象被声明为&nbsp;finalizable。<br />
<br />
<br />
与软引用和弱引用有所不同，PhantomReference&nbsp;在堆对象被释放之前就被添加到它的&nbsp;ReferenceQueue。（请记住，所有的&nbsp;PhantomReference&nbsp;对象都必须用经过关联的&nbsp;ReferenceQueue&nbsp;来创建。）这使您能够在堆对象被回收之前采取行动。<br />
请考虑清单&nbsp;1&nbsp;中的代码。<br />
<br />
清单&nbsp;1.&nbsp;使用&nbsp;WeakReference&nbsp;及&nbsp;ReferenceQueue&nbsp;的示例代码<br />
//Create&nbsp;a&nbsp;strong&nbsp;reference&nbsp;to&nbsp;an&nbsp;object<br />
MyObject&nbsp;obj&nbsp;=&nbsp;new&nbsp;MyObject();&nbsp;//1<br />
<br />
//Create&nbsp;a&nbsp;reference&nbsp;queue<br />
ReferenceQueue&nbsp;rq&nbsp;=&nbsp;new&nbsp;ReferenceQueue();&nbsp;//2<br />
<br />
//Create&nbsp;a&nbsp;weakReference&nbsp;to&nbsp;obj&nbsp;and&nbsp;associate&nbsp;our&nbsp;reference&nbsp;queue<br />
WeakReference&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj,&nbsp;rq);&nbsp;//3<br />
<br />
<br />
<br />
行&nbsp;//1&nbsp;创建&nbsp;MyObject&nbsp;对象，而行&nbsp;//2&nbsp;则创建&nbsp;ReferenceQueue&nbsp;对象。行&nbsp;//3&nbsp;创建引用其引用对象&nbsp;MyObject&nbsp;的&nbsp;WeakReference&nbsp;对象，还创建它的&nbsp;ReferenceQueue。请注意，每个对象引用（obj、rq&nbsp;及&nbsp;wr）都是强引用。要利用这些引用类，您必须取消对&nbsp;MyObject&nbsp;对象的强引用，方法是将&nbsp;obj&nbsp;设置为&nbsp;null。前面说过，如果不这样做，对象&nbsp;MyObject&nbsp;永远都不会被回收，引用类的任何优点都会被削弱。<br />
<br />
每个引用类都有一个&nbsp;get()&nbsp;方法，而&nbsp;ReferenceQueue&nbsp;类有一个&nbsp;poll()&nbsp;方法。get()&nbsp;方法返回对被引用对象的引用。在&nbsp;PhantomReference&nbsp;上调用&nbsp;get()&nbsp;总是会返回&nbsp;null。这是因为&nbsp;PhantomReference&nbsp;只用于跟踪收集。poll()&nbsp;方法返回已被添加到队列中的引用对象，如果队列中没有任何对象，它就返回&nbsp;null。因此，执行清单&nbsp;1&nbsp;之后再调用&nbsp;get()&nbsp;和&nbsp;poll()&nbsp;的结果可能是：<br />
<br />
<br />
wr.get();&nbsp;//returns&nbsp;reference&nbsp;to&nbsp;MyObject<br />
rq.poll();&nbsp;//returns&nbsp;null<br />
<br />
<br />
<br />
<br />
现在我们假定垃圾收集器开始运行。由于&nbsp;MyObject&nbsp;对象没有被释放，所以&nbsp;get()&nbsp;和&nbsp;poll()&nbsp;方法将返回同样的值；obj&nbsp;仍然保持对该对象进行强引用。实际上，对象布局还是没有改变，和图&nbsp;1&nbsp;所示的差不多。然而，请考虑下面的代码：<br />
<br />
<br />
obj&nbsp;=&nbsp;null;<br />
System.gc();&nbsp;//run&nbsp;the&nbsp;collector<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
现在，调用&nbsp;get()&nbsp;和&nbsp;poll()&nbsp;将产生与前面不同的结果：<br />
<br />
<br />
wr.get();&nbsp;//returns&nbsp;null<br />
rq.poll();&nbsp;//returns&nbsp;a&nbsp;reference&nbsp;to&nbsp;the&nbsp;WeakReference&nbsp;object<br />
<br />
<br />
<br />
<br />
这种情况表明，MyObject&nbsp;对象（对它的引用原来是由&nbsp;WeakReference&nbsp;对象进行的）不再可用。这意味着垃圾收集器释放了&nbsp;MyObject&nbsp;占用的内存，从而使&nbsp;WeakReference&nbsp;对象可以被放在它的&nbsp;ReferenceQueue&nbsp;上。这样，您就可以知道当&nbsp;WeakReference&nbsp;或&nbsp;SoftReference&nbsp;类的&nbsp;get()&nbsp;方法返回&nbsp;null&nbsp;时，就有一个对象被声明为&nbsp;finalizable，而且可能（不过不一定）被收集。只有当&nbsp;heap&nbsp;对象完全结束而且其内存被回收后，WeakReference&nbsp;或&nbsp;SoftReference&nbsp;才会被放到与其关联的&nbsp;ReferenceQueue&nbsp;上。清单&nbsp;2&nbsp;显示了一个完整的可运行程序，它展示了这些原理中的一部分。这段代码本身就颇具说明性，它含有很多注释和打印语句，可以帮助您理解。<br />
<br />
清单&nbsp;2.&nbsp;展示引用类原理的完整程序<br />
import&nbsp;java.lang.ref.*;<br />
class&nbsp;MyObject<br />
{<br />
protected&nbsp;void&nbsp;finalize()&nbsp;throws&nbsp;Throwable<br />
{<br />
System.out.println("In&nbsp;finalize&nbsp;method&nbsp;for&nbsp;this&nbsp;object:&nbsp;"&nbsp;+<br />
this);<br />
}<br />
}<br />
<br />
class&nbsp;ReferenceUsage<br />
{<br />
public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])<br />
{<br />
hold();<br />
release();<br />
}<br />
<br />
public&nbsp;static&nbsp;void&nbsp;hold()<br />
{<br />
System.out.println("Example&nbsp;of&nbsp;incorrectly&nbsp;holding&nbsp;a&nbsp;strong&nbsp;"&nbsp;+<br />
"reference");<br />
//Create&nbsp;an&nbsp;object<br />
MyObject&nbsp;obj&nbsp;=&nbsp;new&nbsp;MyObject();<br />
System.out.println("object&nbsp;is&nbsp;"&nbsp;+&nbsp;obj);<br />
<br />
//Create&nbsp;a&nbsp;reference&nbsp;queue<br />
ReferenceQueue&nbsp;rq&nbsp;=&nbsp;new&nbsp;ReferenceQueue();<br />
<br />
//Create&nbsp;a&nbsp;weakReference&nbsp;to&nbsp;obj&nbsp;and&nbsp;associate&nbsp;our&nbsp;reference&nbsp;queue<br />
WeakReference&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj,&nbsp;rq);<br />
<br />
System.out.println("The&nbsp;weak&nbsp;reference&nbsp;is&nbsp;"&nbsp;+&nbsp;wr);<br />
<br />
//Check&nbsp;to&nbsp;see&nbsp;if&nbsp;it&#180;s&nbsp;on&nbsp;the&nbsp;ref&nbsp;queue&nbsp;yet<br />
System.out.println("Polling&nbsp;the&nbsp;reference&nbsp;queue&nbsp;returns&nbsp;"&nbsp;+<br />
rq.poll());<br />
System.out.println("Getting&nbsp;the&nbsp;referent&nbsp;from&nbsp;the&nbsp;"&nbsp;+<br />
"weak&nbsp;reference&nbsp;returns&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
System.out.println("Calling&nbsp;GC");<br />
System.gc();<br />
System.out.println("Polling&nbsp;the&nbsp;reference&nbsp;queue&nbsp;returns&nbsp;"&nbsp;+<br />
rq.poll());<br />
System.out.println("Getting&nbsp;the&nbsp;referent&nbsp;from&nbsp;the&nbsp;"&nbsp;+<br />
"weak&nbsp;reference&nbsp;returns&nbsp;"&nbsp;+&nbsp;wr.get());<br />
}<br />
<br />
public&nbsp;static&nbsp;void&nbsp;release()<br />
{<br />
System.out.println("");<br />
System.out.println("Example&nbsp;of&nbsp;correctly&nbsp;releasing&nbsp;a&nbsp;strong&nbsp;"&nbsp;+<br />
"reference");<br />
//Create&nbsp;an&nbsp;object<br />
MyObject&nbsp;obj&nbsp;=&nbsp;new&nbsp;MyObject();<br />
System.out.println("object&nbsp;is&nbsp;"&nbsp;+&nbsp;obj);<br />
<br />
//Create&nbsp;a&nbsp;reference&nbsp;queue<br />
ReferenceQueue&nbsp;rq&nbsp;=&nbsp;new&nbsp;ReferenceQueue();<br />
<br />
//Create&nbsp;a&nbsp;weakReference&nbsp;to&nbsp;obj&nbsp;and&nbsp;associate&nbsp;our&nbsp;reference&nbsp;queue<br />
WeakReference&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj,&nbsp;rq);<br />
<br />
System.out.println("The&nbsp;weak&nbsp;reference&nbsp;is&nbsp;"&nbsp;+&nbsp;wr);<br />
<br />
//Check&nbsp;to&nbsp;see&nbsp;if&nbsp;it&#180;s&nbsp;on&nbsp;the&nbsp;ref&nbsp;queue&nbsp;yet<br />
System.out.println("Polling&nbsp;the&nbsp;reference&nbsp;queue&nbsp;returns&nbsp;"&nbsp;+<br />
rq.poll());<br />
System.out.println("Getting&nbsp;the&nbsp;referent&nbsp;from&nbsp;the&nbsp;"&nbsp;+<br />
"weak&nbsp;reference&nbsp;returns&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
System.out.println("Set&nbsp;the&nbsp;obj&nbsp;reference&nbsp;to&nbsp;null&nbsp;and&nbsp;call&nbsp;GC");<br />
obj&nbsp;=&nbsp;null;<br />
System.gc();<br />
System.out.println("Polling&nbsp;the&nbsp;reference&nbsp;queue&nbsp;returns&nbsp;"&nbsp;+<br />
rq.poll());<br />
System.out.println("Getting&nbsp;the&nbsp;referent&nbsp;from&nbsp;the&nbsp;"&nbsp;+<br />
"weak&nbsp;reference&nbsp;returns&nbsp;"&nbsp;+&nbsp;wr.get());<br />
}<br />
}<br />
<br />
<br />
<br />
<br />
用途和风格<br />
这些类背后的原理就是避免在应用程序执行期间将对象留在内存中。相反，您以软引用、弱引用或虚引用的方式引用对象，这样垃圾收集器就能够随意地释放对象。当您希望尽可能减小应用程序在其生命周期中使用的堆内存大小时，这种用途就很有好处。您必须记住，要使用这些类，您就不能保留对对象的强引用。如果您这么做了，那就会浪费这些类所提供的任何好处。<br />
<br />
另外，您必须使用正确的编程风格以检查收集器在使用对象之前是否已经回收了它，如果已经回收了，您首先必须重新创建该对象。这个过程可以用不同的编程风格来完成。选择错误的风格会导致出问题。请考虑清单&nbsp;3&nbsp;中从&nbsp;WeakReference&nbsp;检索被引用对象的代码风格：<br />
<br />
清单&nbsp;3.&nbsp;检索被引用对象的风格<br />
obj&nbsp;=&nbsp;wr.get();<br />
if&nbsp;(obj&nbsp;==&nbsp;null)<br />
{<br />
wr&nbsp;=&nbsp;new&nbsp;WeakReference(recreateIt());&nbsp;//1<br />
obj&nbsp;=&nbsp;wr.get();&nbsp;//2<br />
}<br />
//code&nbsp;that&nbsp;works&nbsp;with&nbsp;obj<br />
<br />
<br />
<br />
<br />
研究了这段代码之后，请看看清单&nbsp;4&nbsp;中从&nbsp;WeakReference&nbsp;检索被引用对象的另一种代码风格：<br />
<br />
清单&nbsp;4.&nbsp;检索被引用对象的另一种风格<br />
obj&nbsp;=&nbsp;wr.get();<br />
if&nbsp;(obj&nbsp;==&nbsp;null)<br />
{<br />
obj&nbsp;=&nbsp;recreateIt();&nbsp;//1<br />
wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj);&nbsp;//2<br />
}<br />
//code&nbsp;that&nbsp;works&nbsp;with&nbsp;obj<br />
<br />
<br />
<br />
<br />
请比较这两种风格，看看您能否确定哪种风格一定可行，哪一种不一定可行。清单&nbsp;3&nbsp;中体现出的风格不一定在所有情况下都可行，但清单&nbsp;4&nbsp;的风格就可以。清单&nbsp;3&nbsp;中的风格不够好的原因在于，if&nbsp;块的主体结束之后&nbsp;obj&nbsp;不一定是非空值。请考虑一下，如果垃圾收集器在清单&nbsp;3&nbsp;的行&nbsp;//1&nbsp;之后但在行&nbsp;//2&nbsp;执行之前运行会怎样。recreateIt()&nbsp;方法将重新创建该对象，但它会被&nbsp;WeakReference&nbsp;引用，而不是强引用。因此，如果收集器在行&nbsp;//2&nbsp;在重新创建的对象上施加一个强引用之前运行，对象就会丢失，wr.get()&nbsp;则返回&nbsp;null。<br />
<br />
清单&nbsp;4&nbsp;不会出现这种问题，因为行&nbsp;//1&nbsp;重新创建了对象并为其指定了一个强引用。因此，如果垃圾收集器在该行之后（但在行&nbsp;//2&nbsp;之前）运行，该对象就不会被回收。然后，行&nbsp;//2&nbsp;将创建对&nbsp;obj&nbsp;的&nbsp;WeakReference。在使用这个&nbsp;if&nbsp;块之后的&nbsp;obj&nbsp;之后，您应该将&nbsp;obj&nbsp;设置为&nbsp;null，从而让垃圾收集器能够回收这个对象以充分利用弱引用。清单&nbsp;5&nbsp;显示了一个完整的程序，它将展示刚才我们描述的风格之间的差异。（要运行该程序，其运行目录中必须有一个&#8220;temp.fil&#8221;文件。<br />
<br />
清单&nbsp;5.&nbsp;展示正确的和不正确的编程风格的完整程序。<br />
import&nbsp;java.io.*;<br />
import&nbsp;java.lang.ref.*;<br />
<br />
class&nbsp;ReferenceIdiom<br />
{<br />
public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[])&nbsp;throws&nbsp;FileNotFoundException<br />
{<br />
broken();<br />
correct();<br />
}<br />
<br />
public&nbsp;static&nbsp;FileReader&nbsp;recreateIt()&nbsp;throws&nbsp;FileNotFoundException<br />
{<br />
return&nbsp;new&nbsp;FileReader("temp.fil");<br />
}<br />
<br />
public&nbsp;static&nbsp;void&nbsp;broken()&nbsp;throws&nbsp;FileNotFoundException<br />
{<br />
System.out.println("Executing&nbsp;method&nbsp;broken");<br />
FileReader&nbsp;obj&nbsp;=&nbsp;recreateIt();<br />
WeakReference&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj);<br />
<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
System.out.println("Now,&nbsp;clear&nbsp;the&nbsp;reference&nbsp;and&nbsp;run&nbsp;GC");<br />
//Clear&nbsp;the&nbsp;strong&nbsp;reference,&nbsp;then&nbsp;run&nbsp;GC&nbsp;to&nbsp;collect&nbsp;obj.<br />
obj&nbsp;=&nbsp;null;<br />
System.gc();<br />
<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
//Now&nbsp;see&nbsp;if&nbsp;obj&nbsp;was&nbsp;collected&nbsp;and&nbsp;recreate&nbsp;it&nbsp;if&nbsp;it&nbsp;was.<br />
obj&nbsp;=&nbsp;(FileReader)wr.get();<br />
if&nbsp;(obj&nbsp;==&nbsp;null)<br />
{<br />
System.out.println("Now,&nbsp;recreate&nbsp;the&nbsp;object&nbsp;and&nbsp;wrap&nbsp;it<br />
in&nbsp;a&nbsp;WeakReference");<br />
wr&nbsp;=&nbsp;new&nbsp;WeakReference(recreateIt());<br />
System.gc();&nbsp;//FileReader&nbsp;object&nbsp;is&nbsp;NOT&nbsp;pinned...there&nbsp;is&nbsp;no<br />
//strong&nbsp;reference&nbsp;to&nbsp;it.&nbsp;Therefore,&nbsp;the&nbsp;next<br />
//line&nbsp;can&nbsp;return&nbsp;null.<br />
obj&nbsp;=&nbsp;(FileReader)wr.get();<br />
}<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
}<br />
<br />
public&nbsp;static&nbsp;void&nbsp;correct()&nbsp;throws&nbsp;FileNotFoundException<br />
{<br />
System.out.println("");<br />
System.out.println("Executing&nbsp;method&nbsp;correct");<br />
FileReader&nbsp;obj&nbsp;=&nbsp;recreateIt();<br />
WeakReference&nbsp;wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj);<br />
<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
System.out.println("Now,&nbsp;clear&nbsp;the&nbsp;reference&nbsp;and&nbsp;run&nbsp;GC");<br />
//Clear&nbsp;the&nbsp;strong&nbsp;reference,&nbsp;then&nbsp;run&nbsp;GC&nbsp;to&nbsp;collect&nbsp;obj<br />
obj&nbsp;=&nbsp;null;<br />
System.gc();<br />
<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
<br />
//Now&nbsp;see&nbsp;if&nbsp;obj&nbsp;was&nbsp;collected&nbsp;and&nbsp;recreate&nbsp;it&nbsp;if&nbsp;it&nbsp;was.<br />
obj&nbsp;=&nbsp;(FileReader)wr.get();<br />
if&nbsp;(obj&nbsp;==&nbsp;null)<br />
{<br />
System.out.println("Now,&nbsp;recreate&nbsp;the&nbsp;object&nbsp;and&nbsp;wrap&nbsp;it<br />
in&nbsp;a&nbsp;WeakReference");<br />
obj&nbsp;=&nbsp;recreateIt();<br />
System.gc();&nbsp;//FileReader&nbsp;is&nbsp;pinned,&nbsp;this&nbsp;will&nbsp;not&nbsp;affect<br />
//anything.<br />
wr&nbsp;=&nbsp;new&nbsp;WeakReference(obj);<br />
}<br />
System.out.println("wr&nbsp;refers&nbsp;to&nbsp;object&nbsp;"&nbsp;+&nbsp;wr.get());<br />
}<br />
}<br />
<br />
<br />
<br />
<br />
总结<br />
如果使用得当，引用类还是很有用的。然而，由于它们所依赖的垃圾收集器行为有时候无法预知，所以其实用性就会受到影响。能否有效地使用它们还取决于是否应用了正确的编程风格；关键在于您要理解这些类是如何实现的以及如何对它们进行编程。<br />
=================================================================================<br />
<br />
Java&nbsp;对象的状态有:<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;已创建(created)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;强可达(strong&nbsp;reachable)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;不可见(invisible)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;不可达(unreachable)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;已收集(collected)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;终化(finalized)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;已回收(deallocated)&nbsp;<br />
<br />
Java对象生命周期的状态转换:&nbsp;{image:img=objectstatus.jpg|width=400}&nbsp;引用对象<br />
三种新的引用类型:<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;软引用(soft&nbsp;reference)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;弱引用(weak&nbsp;reference)<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;幻引用(phantom&nbsp;reference)&nbsp;<br />
<br />
强可达(Strong&nbsp;Reachable)<br />
定义:&nbsp;~An&nbsp;object&nbsp;is&nbsp;strong&nbsp;reachable&nbsp;if&nbsp;it&nbsp;can&nbsp;be&nbsp;reached&nbsp;by&nbsp;some&nbsp;thread&nbsp;without&nbsp;traversing&nbsp;any&nbsp;reference&nbsp;objects.&nbsp;A&nbsp;newly-created&nbsp;object&nbsp;is&nbsp;strong&nbsp;reachable&nbsp;by&nbsp;the&nbsp;thread&nbsp;that&nbsp;created&nbsp;it.~<br />
处于强可达状态的对象,&nbsp;在任何情况下都不会被回收掉.&nbsp;软可达(Softly&nbsp;Reachable)<br />
定义:~An&nbsp;object&nbsp;is&nbsp;softly&nbsp;reachable&nbsp;if&nbsp;it&nbsp;is&nbsp;not&nbsp;strongly&nbsp;reachable&nbsp;but&nbsp;can&nbsp;be&nbsp;reached&nbsp;by&nbsp;traversing&nbsp;a&nbsp;soft&nbsp;reference.~<br />
含义是:当对象不处于强可达状态,&nbsp;并且可以通过软引用进行访问时,&nbsp;即处于软可达状态.<br />
当程序申请内存的时候,&nbsp;垃圾收集器会判断是否开始回收处于软可达状态的对象,&nbsp;如果决定回收某个对象,&nbsp;那么垃圾收集器会清除所有指向该对象的软引用,&nbsp;如果任何处于其它软可达状态的对象可以通过强引用访问该对象,&nbsp;那么指向这些对象的软引用也会被清除掉.&nbsp;垃圾收集器在决定哪些软可达状态的对象被收集时,&nbsp;采用"最久未被使用"原则,&nbsp;或称"最不常使用"原则.&nbsp;垃圾收集器也保证在OutOfMemeryError产生以前,&nbsp;所有的软引用都被清除.<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;产生和使用一个软引用&nbsp;<br />
<br />
//&nbsp;createSoftReference&nbsp;sr&nbsp;=&nbsp;new&nbsp;SoftReference(new&nbsp;SomeObject());//&nbsp;getSomeObject&nbsp;o&nbsp;=&nbsp;(SomeObject)&nbsp;sf.get();//&nbsp;create&nbsp;in&nbsp;a&nbsp;reference&nbsp;queue;ReferenceQueue&nbsp;queue&nbsp;=&nbsp;new&nbsp;ReferenceQueue();SoftReference&nbsp;sr&nbsp;=&nbsp;new&nbsp;SoftReference(new&nbsp;SomeObject(),&nbsp;queue);<br />
<br />
弱可达(Weakly&nbsp;Reachable)<br />
定义:~An&nbsp;Object&nbsp;is&nbsp;weakly&nbsp;reachable&nbsp;if&nbsp;it&nbsp;is&nbsp;neither&nbsp;strongly&nbsp;nor&nbsp;softly&nbsp;reachable&nbsp;but&nbsp;can&nbsp;be&nbsp;reached&nbsp;by&nbsp;traversing&nbsp;a&nbsp;weak&nbsp;reference.~<br />
垃圾收集器会一次清除所有弱引用.&nbsp;幻可达(Phantomly&nbsp;Reachable)<br />
定义:~An&nbsp;object&nbsp;is&nbsp;phantomly&nbsp;reachable&nbsp;if&nbsp;it&nbsp;is&nbsp;neither&nbsp;strongly,&nbsp;softly,&nbsp;nor&nbsp;weakly&nbsp;reachable,&nbsp;it&nbsp;has&nbsp;been&nbsp;finalized,&nbsp;and&nbsp;some&nbsp;phantom&nbsp;reference&nbsp;refers&nbsp;to&nbsp;it.~<br />
幻引用不能直接创建.&nbsp;必须通过向引用队列等级的途径来创建:<br />
<br />
ReferenceQueue&nbsp;queue&nbsp;=&nbsp;new&nbsp;ReferenceQueue();PhantomReference&nbsp;pr&nbsp;=&nbsp;new&nbsp;PhantomReference&nbsp;(new&nbsp;SomeObject(),&nbsp;queue);<br />
<br />
你不可能从幻引用再次得到对象,&nbsp;pr.get()永远返回null.&nbsp;另外,&nbsp;必须调用Reference.clear()手工清除幻引用.&nbsp;All&nbsp;About&nbsp;ReferenceObjects&nbsp;No&nbsp;InterWiki&nbsp;reference&nbsp;defined&nbsp;in&nbsp;properties&nbsp;for&nbsp;Wiki&nbsp;called&nbsp;'[http'!)]<br />
Reference&nbsp;Objects&nbsp;No&nbsp;InterWiki&nbsp;reference&nbsp;defined&nbsp;in&nbsp;properties&nbsp;for&nbsp;Wiki&nbsp;called&nbsp;'[http'!)]<br />
Reference&nbsp;Objects&nbsp;and&nbsp;Garbage&nbsp;Collection&nbsp;No&nbsp;InterWiki&nbsp;reference&nbsp;defined&nbsp;in&nbsp;properties&nbsp;for&nbsp;Wiki&nbsp;called&nbsp;'[http'!)]<br />
\[Jike&nbsp;Thread\?Soft,&nbsp;Weak,&nbsp;and&nbsp;Phantom&nbsp;References|http://www-124.ibm.com/pipermail/jikesrvm-core/2003-May/000365.html]<br />
<br />
<br />
Trackback:&nbsp;http://tb.blog.csdn.net/TrackBack.aspx?PostId=1492810<br />
 <img src ="http://www.blogjava.net/zhuzi1987/aggbug/244802.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-12-06 21:13 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/12/06/244802.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机类装载：原理、实现与应用</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/08/21/223585.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Thu, 21 Aug 2008 12:45:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/08/21/223585.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/223585.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/08/21/223585.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/223585.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/223585.html</trackback:ping><description><![CDATA[<p><span style="color: #800000">一、引言 </span></p>
<p><span style="color: #800000">　　Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部分的过程。JVM的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块, 而不影响系统其他功能模块的正常运行。本文将分析JVM中的类装载系统，探讨JVM中类装载的原理、实现以及应用。 </span></p>
<p><span style="color: #800000">　　二、Java虚拟机的类装载实现与应用 </span></p>
<p><span style="color: #800000">　　2.1 装载过程简介 </span></p>
<p><span style="color: #800000">　　所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的class对象的过程，其中类或接口的名称是给定了的。当然名称也可以通过计算得到，但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。 </span></p>
<p><span style="color: #800000">　　在Java中，类装载器把一个类装入Java虚拟机中，要经过三个步骤来完成：装载、链接和初始化，其中链接又可以分成校验、准备和解析三步，除了解析外，其它步骤是严格按照顺序完成的，各个步骤的主要工作如下： </span></p>
<p><span style="color: #800000">　　装载：查找和导入类或接口的二进制数据； <br />
　　链接：执行下面的校验、准备和解析步骤，其中解析步骤是可以选择的； <br />
　　校验：检查导入类或接口的二进制数据的正确性； <br />
　　准备：给类的静态变量分配并初始化存储空间； <br />
　　解析：将符号引用转成直接引用； <br />
　　初始化：激活类的静态变量的初始化Java代码和静态Java代码块。 </span></p>
<p><span style="color: #800000">　　至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误，请参看《Java虚拟机规范》以及《深入Java虚拟机》，它们在网络上面的资源地址是： <br />
　　</span><a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html"><span style="color: #800000">http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html</span></a><span style="color: #800000"> <br />
　　</span><a href="http://www.artima.com/insidejvm/ed2/index.html"><span style="color: #800000">http://www.artima.com/insidejvm/ed2/index.html</span></a><span style="color: #800000"> <br />
　　由于本文的讨论重点不在此就不再多叙述。 </span></p>
<p><span style="color: #800000">　　2.2 装载的实现 </span></p>
<p><span style="color: #800000">　　JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 </span></p>
<p><span style="color: #800000">　　在Java中，ClassLoader是一个抽象类，它在包java.lang中,可以这样说，只要了解了在ClassLoader中的一些重要的方法，再结合上面所介绍的JVM中类装载的具体的过程，对动态装载类这项技术就有了一个比较大概的掌握，这些重要的方法包括以下几个: </span></p>
<p><span style="color: #800000">　　①loadCass方法 loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的类的名称,该名称以包表示法表示,如Java.lang.Object；resolve参数告诉方法是否需要解析类，在初始化类之前,应考虑类解析，并不是所有的类都需要解析，如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是ClassLoader 的入口点。 </span></p>
<p><span style="color: #800000">　　②defineClass方法 这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。 </span></p>
<p><span style="color: #800000">　　③findSystemClass方法 findSystemClass方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将字节数组转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是JVM 正常装入类的缺省机制。 </span></p>
<p><span style="color: #800000">　　④resolveClass方法 resolveClass(Class c)方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。 </span></p>
<p><span style="color: #800000">　　⑤findLoadedClass方法 当调用loadClass方法装入类时,调用findLoadedClass 方法来查看ClassLoader是否已装入这个类,如果已装入,那么返回Class对象,否则返回NULL。如果强行装载已存在的类,将会抛出链接错误。 </span></p>
<p><span style="color: #800000">　　2.3 装载的应用 </span></p>
<p><span style="color: #800000">　　一般来说，我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()，对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。 </span></p>
<p><span style="color: #800000">　　所有的Java 虚拟机都包括一个内置的类装载器，这个内置的类库装载器被称为根装载器(bootstrap ClassLoader)。根装载器的特殊之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装载的类都是安全的、可信任的,可以不经过安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时,必须使用用户自定义的装载器(user-defined ClassLoader)。下面我们举例说明它的应用。 </span></p>
<p><span style="color: #800000">　　public abstract class MultiClassLoader extends ClassLoader{ <br />
　　... <br />
　　public synchronized Class loadClass(String s, boolean flag) <br />
　　throws ClassNotFoundException <br />
　　{ <br />
　　/* 检查类s是否已经在本地内存*/ <br />
　　Class class1 = (Class)classes.get(s); </span></p>
<p><span style="color: #800000">　　/* 类s已经在本地内存*/ <br />
　　if(class1 != null) return class1; <br />
　　try/*用默认的ClassLoader 装入类*/ { <br />
　　class1 = super.findSystemClass(s); <br />
　　return class1; <br />
　　} <br />
　　catch(ClassNotFoundException _ex) { <br />
　　System.out.println("&gt;&gt; Not a system class."); <br />
　　} </span></p>
<p><span style="color: #800000">　　/* 取得类s的字节数组*/ <br />
　　byte abyte0[] = loadClassBytes(s); <br />
　　if(abyte0 == null) throw new ClassNotFoundException(); </span></p>
<p><span style="color: #800000">　　/* 将类字节数组转换为类*/ <br />
　　class1 = defineClass(null, abyte0, 0, abyte0.length); <br />
　　if(class1 == null) throw new ClassFormatError(); <br />
　　if(flag) resolveClass(class1); /*解析类*/ </span></p>
<p><span style="color: #800000">　　/* 将新加载的类放入本地内存*/ <br />
　　classes.put(s, class1); <br />
　　System.out.println("&gt;&gt; Returning newly loaded class."); </span></p>
<p><span style="color: #800000">　　/* 返回已装载、解析的类*/ <br />
　　return class1; <br />
　　} <br />
　　... <br />
　　} <br />
三、Java虚拟机的类装载原理 </span></p>
<p><span style="color: #800000">　　前面我们已经知道，一个Java应用程序使用两种类型的类装载器：根装载器(bootstrap)和用户定义的装载器(user-defined)。根装载器是Java虚拟机实现的一部分，举个例子来说，如果一个Java虚拟机是在现在已经存在并且正在被使用的操作系统的顶部用C程序来实现的，那么根装载器将是那些C程序的一部分。根装载器以某种默认的方式将类装入，包括那些Java API的类。在运行期间一个Java程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分，而用户定义的类装载器则不是，它是用Java语言写的，被编译成class文件之后然后再被装入到虚拟机，并像其它的任何对象一样可以被实例化。 Java类装载器的体系结构如下所示： <br />
　　 <br />
　　Java的类装载模型是一种代理(delegation)模型。当JVM 要求类装载器CL(ClassLoader)装载一个类时,CL首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时,CL才获得装载这个类的机会。这样, 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时, 如果没有显式地给出父装载器, 那么JVM将默认系统装载器为其父装载器。Java的基本类装载器代理结构如图2所示： <br />
下面针对各种类装载器分别进行详细的说明。 <br />
根(Bootstrap) 装载器:该装载器没有父装载器，它是JVM实现的一部分，从sun.boot.class.path装载运行时库的核心代码。 <br />
扩展(Extension) 装载器:继承的父装载器为根装载器，不像根装载器可能与运行时的操作系统有关，这个类装载器是用纯Java代码实现的，它从java.ext.dirs (扩展目录)中装载代码。 <br />
　　系统(System or Application) 装载器:装载器为扩展装载器，我们都知道在安装JDK的时候要设置环境变量(CLASSPATH )，这个类装载器就是从java.class.path(CLASSPATH 环境变量)中装载代码的，它也是用纯Java代码实现的，同时还是用户自定义类装载器的缺省父装载器。 </span></p>
<p><span style="color: #800000">　　小应用程序(Applet) 装载器: 装载器为系统装载器，它从用户指定的网络上的特定目录装载小应用程序代码。 </span></p>
<p><span style="color: #800000">　　在设计一个类装载器的时候，应该满足以下两个条件： </span></p>
<p><span style="color: #800000">　　对于相同的类名，类装载器所返回的对象应该是同一个类对象 </span></p>
<p><span style="color: #800000">　　如果类装载器CL1将装载类C的请求转给类装载器CL2，那么对于以下的类或接口,CL1和CL2应该返回同一个类对象:a)S为C的直接超类;b)S为C的直接超接口;c)S为C的成员变量的类型;d)S为C的成员方法或构建器的参数类型;e)S为C的成员方法的返回类型。 <br />
　　每个已经装载到JVM中的类都隐式含有装载它的类装载器的信息。类方法getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类，可见类装载器认识的类是它自己装载的类的超集。注意我们可以得到类装载器的有关的信息，但是已经装载到JVM中的类是不能更改它的类装载器的。 </span></p>
<p><span style="color: #800000">　　Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。 </span></p>
<p><span style="color: #800000">　　四、结论 </span></p>
<p><span style="color: #800000">　　类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用，尤其分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装载并执行Java类，希望能使读者对JVM中的类装载有一个比较深入的理解 <br />
</span></p>
 <img src ="http://www.blogjava.net/zhuzi1987/aggbug/223585.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-08-21 20:45 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/08/21/223585.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载"TOMCAT中文解决"</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/08/16/222466.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sat, 16 Aug 2008 10:29:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/08/16/222466.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/222466.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/08/16/222466.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/222466.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/222466.html</trackback:ping><description><![CDATA[<div><span style="color: #999999">-------------------<br />
Tomcat 5中文问题<br />
author:kiss__sky@163.com<br />
-------------------<br />
<br />
问题描述：<br />
<br />
1 表单提交的数据，用request.getParameter(&#8220;xxx&#8221;)返回的字符串为乱码或者？？<br />
2 直接通过url如http://localhost/a.jsp?name=中国，这样的get请求在服务端用request. getParameter(&#8220;name&#8221;)时返回的是乱码；按tomcat4的做法设置Filter也没有用或者用request.setCharacterEncoding("GBK");也不管用<br />
<br />
原因：<br />
1 tomcat的j2ee实现对表单提交即post方式提示时处理参数采用缺省的iso-8859-1来处理<br />
2 tomcat对get方式提交的请求对query-string 处理时采用了和post方法不一样的处理方式。(与tomcat4不一样,所以设置setCharacterEncoding(&#8220;gbk&#8221;))不起作用。<br />
<br />
<br />
解决办法：<br />
<br />
首先所有的jsp文件都加上:<br />
<!--element not supported - Type: 8 Name: #comment--><br />
<br />
1 实现一个Filter.设置处理字符集为GBK。(在tomcat的webapps/servlet-examples目录有一个完整的例子。请参考web.xml和SetCharacterEncodingFilter的配置。) <br />
<br />
1)只要把%TOMCAT安装目录%/ webapps\servlets-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class文件拷到你的webapp目录/filters下，如果没有filters目录，就创建一个。<br />
2)在你的web.xml里加入如下几行：<br />
<br />
</span>
<pre><br />
<span style="color: #999999"> &lt;filter&gt;<br />
&lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;<br />
&lt;filter-class&gt;filters.SetCharacterEncodingFilter&lt;/filter-class&gt;<br />
&lt;init-param&gt;<br />
&lt;param-name&gt;encoding&lt;/param-name&gt;<br />
&lt;param-value&gt;GBK&lt;/param-value&gt;<br />
&lt;/init-param&gt;<br />
&lt;/filter&gt;<br />
<br />
&lt;filter-mapping&gt;<br />
&lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;<br />
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />
&lt;/filter-mapping&gt;<br />
<br />
</span></pre>
<br />
<span style="color: #999999">3)完成.<br />
<br />
2 get方式的解决办法<br />
1) 打开tomcat的server.xml文件，找到<connector>区块，加入如下一行：<br />
URIEncoding=&#8221;GBK&#8221;<br />
完整的应如下：<br />
</span>
<pre><br />
<span style="color: #999999">&lt;Connector <br />
port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<br />
enableLookups="false" redirectPort="8443" acceptCount="100"<br />
debug="0" connectionTimeout="20000" <br />
disableUploadTimeout="true" <br />
URIEncoding="GBK"<br />
/&gt;<br />
<br />
</span></pre>
<br />
<br />
<span style="color: #999999">2)重启tomcat,一切OK。<br />
<br />
执行如下jsp页页测试是否成功<br />
<br />
</span>
<pre><br />
<span style="color: #999999">&lt;%@ page contentType="text/html;charset=gb2312"%&gt;<br />
&lt;%@ page import="java.util.*"%&gt;<br />
<br />
&lt;% <br />
<br />
String q=request.getParameter("q"); <br />
q = q == null? "没有值" : q;<br />
<br />
%&gt;<br />
<br />
<br />
&lt;HTML&gt;<br />
&lt;HEAD&gt;&lt;TITLE&gt;新闻列表显示&lt;/TITLE&gt;<br />
&lt;META http-equiv=Content-Type content="text/html; charset=gb2312"&gt;<br />
&lt;META http-equiv=pragma content=no-cache&gt;<br />
&lt;body&gt;<br />
你提交了：<br />
&lt;%=q%&gt;<br />
<br />
&lt;br&gt;<br />
&lt;form action="tcnchar.jsp" method="post"&gt;<br />
输入中文:&lt;input type="text" name="q"&gt;&lt;input type="submit" value="确定"&gt;<br />
&lt;br&gt;<br />
&lt;a href="tcnchar.jsp?q=中国"&gt;通过get方式提交&lt;/a&gt;<br />
<br />
&lt;/form&gt;<br />
&lt;/BODY&gt;&lt;/HTML&gt;<br />
</span></pre>
<br />
<br />
<span style="color: #999999">测试结果如果你输入文本框或者点超链都会显示:你提交了&#8221;中国&#8221;,说明成功!!!!!<br />
<br />
<br />
<br />
特别感谢下面这篇帖子，帮我解决了中文问题.最后祝大家好运!!!<br />
参考网址：<br />
<br />
http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0<br />
</span></div>
<br />
<img src ="http://www.blogjava.net/zhuzi1987/aggbug/222466.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-08-16 18:29 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/08/16/222466.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java实现MD5加密</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221579.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Tue, 12 Aug 2008 14:55:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221579.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/221579.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221579.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/221579.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/221579.html</trackback:ping><description><![CDATA[<p><font color="#667087">import java.security.MessageDigest;</font></p>
<p><font color="#667087">/**<br />
&nbsp;* MD5加密类<br />
&nbsp;* @author zhang<br />
&nbsp;*/<br />
public class MD5Encoding<br />
{<br />
&nbsp;/**<br />
&nbsp; * <br />
&nbsp; *<br />
&nbsp; */<br />
&nbsp;private MD5Encoding()<br />
&nbsp;{<br />
&nbsp;}</font></p>
<p><font color="#667087">&nbsp;/**<br />
&nbsp; * 加密算法MD5<br />
&nbsp; * <br />
&nbsp; * @param text 明文<br />
&nbsp; * @return String 密文<br />
&nbsp; */<br />
&nbsp;public final static String encoding(String text)<br />
&nbsp;{<br />
&nbsp;&nbsp;char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};<br />
&nbsp;&nbsp;String encodingStr = null;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;try<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;byte[] strTemp = text.getBytes();<br />
&nbsp;&nbsp;&nbsp;MessageDigest mdTemp = MessageDigest.getInstance("MD5");<br />
&nbsp;&nbsp;&nbsp;mdTemp.update(strTemp);<br />
&nbsp;&nbsp;&nbsp;byte[] md = mdTemp.digest();<br />
&nbsp;&nbsp;&nbsp;int j = md.length;<br />
&nbsp;&nbsp;&nbsp;char str[] = new char[j * 2];<br />
&nbsp;&nbsp;&nbsp;int k = 0;<br />
&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; j; i++)<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;byte byte0 = md[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;str[k++] = hexDigits[byte0 &gt;&gt;&gt; 4 &amp; 0xf];<br />
&nbsp;&nbsp;&nbsp;&nbsp;str[k++] = hexDigits[byte0 &amp; 0xf];<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;encodingStr = new String(str);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;catch (Exception e)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;return encodingStr;<br />
&nbsp;}<br />
&nbsp;public static void main(String[] areg)<br />
&nbsp;{<br />
&nbsp;&nbsp;MD5Encoding md5 = new MD5Encoding();<br />
&nbsp;&nbsp;md5.encoding("admin");<br />
&nbsp;}<br />
}</font></p>
 <img src ="http://www.blogjava.net/zhuzi1987/aggbug/221579.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-08-12 22:55 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221579.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tomcat内存优化与连接数</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221571.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Tue, 12 Aug 2008 14:46:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221571.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/221571.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221571.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/221571.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/221571.html</trackback:ping><description><![CDATA[<p><font color="#667087">1, Tomcat内存参数配置</font></p>
<p><font color="#667087">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果是Win与Linux系统启动Tomcat服务器,需要在tomcat/bin/catalina.sh与/tomcat/bin/catalina.bat两个文件:在两个文件里面加如:</font></p>
<p><strong><font color="#667baf">SET&nbsp; CATALINA_OPTS= -Xms64m -Xmx128m</font></strong></p>
<p><font color="#667087">2, Tomcat连接数配置</font></p>
<p><font color="#667087">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 配置Tomcat连接数.需要在Server.xml文件里面加如:</font></p>
<p><font color="#667087">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxThreads="150" &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表示最多同时处理150个连接 &nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minSpareThreads="25" &nbsp; &nbsp;&nbsp; 表示即使没有人使用也开这么多空线程等待 &nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxSpareThreads="75" &nbsp; &nbsp; 表示如果最多可以空75个线程，例如某时刻有80人访问，之后没有人访问了，则tomcat不会保留80个空线程，而是关闭5个空的。 &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; acceptCount="100" &nbsp; 当同时连接的人数达到maxThreads时，还可以接收排队的连接，超过这个连接的则直接返回拒绝连接。</font></p>
<p><strong><font color="#667087">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <connector strong="" protocol="HTTP/1.1" port="8080"></connector></font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connectionTimeout="20000" maxThreads="150" </font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minSpareThreads="25"&nbsp;</font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxSpareThreads="75" </font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enableLookups="false" </font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; acceptCount="100" debug="0" </font></strong></p>
<p><strong><font color="#667baf">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disableUploadTimeout="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirectPort="8443" URIEncoding="UTF-8"/&gt;</font></strong></p>
<p><font color="#667baf">URIEncoding="UTF-8"&nbsp; 是设定JSP编码格式.</font></p>
 <img src ="http://www.blogjava.net/zhuzi1987/aggbug/221571.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-08-12 22:46 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/08/12/221571.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】浅谈DAO模式</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216146.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sun, 20 Jul 2008 01:41:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216146.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/216146.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/216146.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/216146.html</trackback:ping><description><![CDATA[<p><span style="color: #800000">DAO模式在现在的开发中应用非常的广泛，它可以帮助我们实现持久化逻辑和业务逻辑的分离，同时实现对多种持久化实现的支持。当然现在你可以通过 hibernate来实现对多种持久化的支持，由于新的技术新的方式的出现，DAO也相应的做出了一些调整，比如泛型DAO，在SpringSide中有很还得例子可以参考这方面的实现。</span></p>
<p><span style="color: #800000">这里聊下传统意义上的DAO模式（在阎宏的JAVA与模式书中有详细的介绍），需要注意的几个方面：</span></p>
<p><span style="color: #3a0000"><span style="color: #800000">1、不要DAO中出现业务逻辑</span></span></p>
<p><span style="color: #800000">DAO只需关注持久化部分，可以通过Facade来控制事务的边界，从而提高DAO的复用性，在不同的事务策略中应用</span></p>
<p><span style="color: #3a0000"><span style="color: #800000">2、不要过多的在 DAO层捕捉异常</span></span></p>
<p><span style="color: #800000">在很多的开发中，会喜欢使用Checked Exception，抛到servcie层、再到action层，其实在DAO中发生的异常常常是不可恢复的（比如DB的连接问题），所以应该选择 RuntimeException，我们所需要的只是log的记录并通知管理员，并通过全局的异常处理画面告之。</span></p>
<p><span style="color: #800000">暂时这些，由于ORM的懒加载技术，在DAO中可能会有些调整，为了增加DAO的复用性，这方面的技术也应该剥离出来，不过暂时还没这么处理过，所以无法总结，希望有这方面经验的人提供一些</span></p>
<img src ="http://www.blogjava.net/zhuzi1987/aggbug/216146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-07-20 09:41 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]单元测试(提升篇)</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216143.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sun, 20 Jul 2008 01:32:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216143.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/216143.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216143.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/216143.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/216143.html</trackback:ping><description><![CDATA[<p><span style="font-family: 宋体;">本节是单元测试的第三篇。我以为这是重中之重的一章。单元测试的关键在于代码要可测。可测才能测。要做好单元测试，就必须在代码的可测性方面努力，在设计、重构方面用心。本篇主要分享我在如何写出可测性代码方面的理解，与大家共勉！</span></p>
<h2 style="text-align: center; line-height: 20%;" align="center"><span style="font-family: 黑体;">单元测试</span>(<span style="font-family: 黑体;">提升篇</span>)</h2>
<p style="margin-left: 73.5pt; text-align: center; line-height: 20%;" align="center"><strong>------</strong><strong><span style="font-family: 宋体;">编写可测试性代码</span></strong></p>
<h6><span style="font-family: 黑体;"><span style="font-size: 18pt;"><span style="font-size: 14pt;">一、可测试性设计</span></span></span></h6>
<p style="margin-left: 18pt; text-indent: -18pt;"><span><span>1.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">接口依赖</span></p>
<p><span style="font-family: 宋体;">这是最重要的一点，因为这可以使得我们很容易的针对一个接口实现</span>Mock<span style="font-family: 宋体;">对象，模拟</span>/<span style="font-family: 宋体;">替换实际对象会变的很容易。达到使一个被测对象处于一个孤立环境的测试要求。</span></p>
<p><span style="font-family: 宋体;">
<div align="center"><img alt="" src="../../images/blogjava_net/wukaichun600/21.png" height="77" width="538" /></div>
<br />
</span></p>
<p style="text-align: center;" align="center"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">这里，</span>ClassA<span style="font-family: 宋体;">依赖于</span>ClassB<span style="font-family: 宋体;">的具体实现，</span>TestCase<span style="font-family: 宋体;">根本无法独立于</span>ClassB<span style="font-family: 宋体;">对</span>ClassA<span style="font-family: 宋体;">进行独立测试。</span></p>
<p><span style="font-family: 宋体;"><img alt="" src="../../images/blogjava_net/wukaichun600/22.png" height="230" width="527" /><br />
</span></p>
<p style="text-align: center;" align="center"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">因此，我们将</span>ClassA<span style="font-family: 宋体;">改为依赖于接口</span>B_Inf<span style="font-family: 宋体;">。这样，可以很容易的实现一个</span>Mock_B<span style="font-family: 宋体;">替换</span>ClassB<span style="font-family: 宋体;">去</span>ClassA<span style="font-family: 宋体;">进行孤立测试。</span></p>
<p style="margin-left: 18pt; text-indent: -18pt;"><span><span>2.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">依赖注入</span></p>
<p><span style="font-family: 宋体;">一个方法对外部类的依赖，应该是可注入的。即可以通过构造方法、</span>get/set<span style="font-family: 宋体;">方法的方式在外部将依赖关系注入。事实上，这也为我们在测试用例中替换待测类的依赖对象提供了机会。不应该出现在方法内部新建对象使用的情况。</span></p>
<p style="margin-left: 18pt; text-indent: -18pt;"><span><span>3.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">降低耦合度</span></p>
<p><span style="font-family: 宋体;">待测类应与最少的类耦合，即最小交互原则。特别是要减少与那些离了具体环境就不能运行的类的耦合。可以通过门面模式等对外部调用进行隔离。</span></p>
<p>5<span style="font-family: 宋体;">．</span>AOP</p>
<p><span style="font-family: 宋体;">面向切面编程。给我们提供的启示是，将真正需要测的逻辑分离出来。摆脱那些无意义且简单重复的代码对测试的干扰。</span></p>
<p>6.&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">明确的契约</span></p>
<p><span style="font-family: 宋体;">方法一定要有明确清晰的输入</span>/<span style="font-family: 宋体;">输出。建议在方法的注释描述中，分三段&#8220;描述&#8221;&#8220;前置条件&#8221;&#8220;后置条件&#8221;。</span></p>
<h6><span style="font-family: 黑体;"><span style="font-size: 14pt;">二、可测试性重构</span></span></h6>
<p style="margin-left: 21pt; text-indent: -21pt;"><span><span>1.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">可恶的静态方法</span></p>
<p><span style="font-family: 宋体;">在我们的代码中有大量的调用静态方法的地方。用起来我们很爽，但这对测试来说却是灾难。因为我们除了通过改变代码创建</span>stub<span style="font-family: 宋体;">来改变这些方法的行为外，我们没有任何途径。更要命的是，写这些</span>stub<span style="font-family: 宋体;">的代价非常的巨大，常常令人望而却步。</span></p>
<p><span style="font-family: 宋体;">解决方案：</span></p>
<p><span style="font-family: 宋体;">将方法内部的这些调用提取成</span>protected<span style="font-family: 宋体;">方法。在外部创建待测类的子类，重写该</span>protected<span style="font-family: 宋体;">方法。</span></p>
<p><span style="font-family: 宋体;">最佳实践：</span></p>
<p><span style="font-family: 宋体;"><img alt="" src="../../images/blogjava_net/wukaichun600/23.png" height="639" width="1068" /><br />
</span></p>
<p><span style="font-family: 宋体;">这些静态方法由单态类提供，单态类由工厂方法获取，具体类使用这些单态类的接口。</span></p>
<p><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">我们在方法中通过接口使用对外部模块的调用。一方面，隔离了外部模块改变对我们产生的冲击，另一方面，也使我们使用</span>Mock<span style="font-family: 宋体;">替换实际的外部组件，创建孤立测试环境成为可能。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt;"><span><span>2.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">待测类方法中，</span>new<span style="font-family: 宋体;">出另一个对象并使用其方法</span></p>
<p><span style="font-family: 宋体;">两种方案：</span>1<span style="font-family: 宋体;">）将</span>new <span style="font-family: 宋体;">出对象的过程封装成</span>protected<span style="font-family: 宋体;">方法。同重构</span>1<span style="font-family: 宋体;">。</span>2)<span style="font-family: 宋体;">将该对象提取成类属性，即由使用关系变成关联关系。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt;"><span><span>3.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">分离不可测</span>/<span style="font-family: 宋体;">不必测代码</span></p>
<p><span style="font-family: 宋体;">在不影响的情况下，将不可测部分分离到一些不需要测的简单方法中去。或者将可测的部分提取到一个私有方法中去。然后针对这个私有方法进行测试。</span></p>
<p><span style="font-family: 宋体;">通常这种做法使用范围有限，但有些时候还是值的一试。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt;"><span><span>4.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">单一职责</span></p>
<p><span style="font-family: 宋体;">职责太多，肯定不好测。针对于这一点&#8220;不好测的方法必然不好用&#8221;。当方法过大，承担责任过多时，拆分是应该的。</span></p>
<p><s>&nbsp;</s></p>
<p style="margin-left: 21pt; text-indent: -21pt;"><span><span>5.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">为类提供一个空构造方法。</span></p>
<p><span style="font-family: 宋体;">我们的各个测试方法都依赖于对象处于一个特定的状态，满足一定的前置条件。而要将对象置为我们希望的状态，我们必须首先拥有一个对象。然而，很多时候，在一个隔离的单元测试环境下，构造函数由于各种原因不能正常初始化。</span></p>
<p><span style="font-family: 宋体;">此时，可以为类提供一个的空的构造方法。在外部构造一个</span>&#8220;<span style="font-family: 宋体;">裸</span>&#8221;<span style="font-family: 宋体;">对象，而后根据前置条件将各个属性设置成需要的</span>Mock<span style="font-family: 宋体;">对象。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 8pt;">-----------------------------------------------------------------------------------------------------------------------------------------------------------</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 8pt; font-family: 宋体;">事实上，要想写出具有可测性的代码，最佳的办法就是测试驱动开发。先写测试代码把功能体现出来，再写功能代码让测试通过。这样写出的代码显而易见会更具有测试性。</span></p>
<p style="text-align: left;" align="left"><br />
</p>
<p style="text-align: left;" align="left"><span style="font-size: 8pt; font-family: 宋体;">原地址:http://www.blogjava.net/wukaichun600/archive/2008/07/18/214125.html<br />
</span></p>
   <img src ="http://www.blogjava.net/zhuzi1987/aggbug/216143.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-07-20 09:32 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216143.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]单元测试(技能篇)</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216142.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sun, 20 Jul 2008 01:31:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216142.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/216142.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/216142.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/216142.html</trackback:ping><description><![CDATA[<p style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;">本节是单元测试系列的第二篇。重点讲解如何使用Mock/Stub和依赖注入技术进行单元测试。关于工具JUnit等则不做累赘介绍。 希望通过本章能够帮助大家开始单元测试的有益实践，与大家共勉！</span></p>
<h1 style="text-align: center;" align="center"><span style="font-family: 宋体;">单元测试（技能篇）</span></h1>
<h2><span style="font-family: 黑体;">一、</span>Stub<span style="font-family: 黑体;">技术</span></h2>
<p><span style="font-family: 宋体;">这是最为古老的一种测试技能。通过类层次上的替换实现了对待测环境的模拟。</span></p>
<p><span style="font-family: 宋体;">实现的时候有两种途径：</span></p>
<p>1<span style="font-family: 宋体;">、重写实际类，在测试时，先于实际类加载，即覆盖。如：我们在</span>unittest/stub<span style="font-family: 宋体;">文件夹下针对于每一个重写类都有相同的包结构和类名：</span></p>
<p><span style="font-family: 宋体;"><img alt="" src="../../images/blogjava_net/wukaichun600/11.png" height="198" width="244" /><br />
</span></p>
<p><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">在类路径中优先加载：</span></p>
<p><span style="font-family: 宋体;"><img alt="" src="../../images/blogjava_net/wukaichun600/12.png" height="275" width="338" /><br />
</span></p>
<p><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p>2<span style="font-family: 宋体;">、在实际代码中添加判断。比如，如果当前是测试环境</span>if(isUT)<span style="font-family: 宋体;">执行</span>XX<span style="font-family: 宋体;">操作，截断真正需要做的事。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><strong><span style="background: silver none repeat scroll 0% 50%; font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">void</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> sendCommand(</span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">int</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> cmdCode)</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">if</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">(isUT())</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">//...</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">else</span></strong></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //... </span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; <span style="background: silver none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">}</span></span></p>
<p>Stub<span style="font-family: 宋体;">技术的问题就在于我们在重写这些类的时候，不仅仅要关注接口，还要关注其内部逻辑。如果你只是简单的返回一个固定的响应，会很简单。但是对于每一次运行需要根据不同的输入返回不同的输出时方法内部的处理就会复杂的多。</span></p>
<p><span style="font-family: 宋体;">由于实现的难度，所以，使用时就要注意：有高价值、重用度高、数量少。这就是说，<span style="color: blue;">重写一个类，就可以有一大批类可以用。</span></span></p>
<h2><span style="font-family: 黑体;">二、</span>Mock<span style="font-family: 黑体;">技术</span></h2>
<p>Mock<span style="font-family: 宋体;">是目前单元测试中最常用的。用来在对象层次上实现细类度替换十分方便。</span></p>
<p><span style="font-family: 宋体;">当我们在测试中，需要其它类</span>/<span style="font-family: 宋体;">接口的一个方法时，我们可以<span style="color: blue;">通过继承</span></span><span style="color: blue;">/</span><span style="font-family: 宋体; color: blue;">实现其一个子类对象来替换实际对象</span><span style="font-family: 宋体;">。在</span>Mock<span style="font-family: 宋体;">子类中将需要的方法直接返回需要的结果就行了。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">private</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">class</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> Mock_QueryCtrl </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">extends</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> QueryCtrl</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> List queryNEList()</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List neList = </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">new</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> ArrayList();</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">//</span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">直接填充并返回你需要的数据</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">...</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">return</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> neList;</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-family: 宋体;">同样，我们也可以<span style="color: blue;">通过测试待测类的子类来测试待测类</span>。这对于被测方法使用了自身类的方法时很适用。</span></p>
<h2><span style="font-family: 黑体;">三、依赖注入</span></h2>
<p><span style="font-family: 宋体;">单元测试的一个关键就是替换。类层次上的替换，通过在类路径中提前加载就可以实现。而在对象层次上，</span>java<span style="font-family: 宋体;">的反射机制提供了很好的帮助。</span></p>
<p>1<span style="font-family: 宋体;">）</span>.<span style="font-family: 宋体;">获取</span>/<span style="font-family: 宋体;">注入私有属性</span></p>
<p>2<span style="font-family: 宋体;">）</span>.<span style="font-family: 宋体;">执行私有方法</span></p>
<p><span style="font-family: 宋体;">附：注入私有属性的实现：</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">void</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> setFieldObject(Object instance, String fieldName, Object
value)</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">throws</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> IllegalArgumentException, IllegalAccessException,</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NoSuchFieldException {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field field = </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">null</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">;</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class c = instance.getClass();</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">do</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">try</span></strong></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field = c.getDeclaredField(fieldName);</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">catch</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> (SecurityException e) </span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">catch</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> (NoSuchFieldException e)</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c = c.getSuperclass();</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">while</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> (c.getName() != </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #2a00ff;">"java.lang.Object"</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> &amp;&amp; field
== </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">null</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">);</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">if</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> (field != </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">null</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">) </span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field.setAccessible(</span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">true</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">);</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field.set(instance, value);</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">else</span></strong></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">throw</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">new</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> NoSuchFieldException(fieldName);</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 10pt; font-family: 宋体; color: black;">注：这是一个简单实现，实际中需要优化。</span></p>
<h2><span style="font-family: 黑体;">四、实例：</span></h2>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-family: 宋体;">下例演示了如何测试类</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">NEListTable</span><span style="font-family: 宋体;">的</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">ShowNETable()</span><span style="font-family: 宋体;">方法。其中注意的是，方法中调用了类</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">QueryCtrl</span><span style="font-family: 宋体;">的</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">queryNEList()</span><span style="font-family: 宋体;">方法。</span></p>
<p style="text-align: left;" align="left"><strong><span style="font-size: 10pt; font-family: 宋体; color: black;">待测类：</span></strong></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">class</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> NEListTable </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">{</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; QueryCtrl </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #0000c0;">ctrl</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> = </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">null</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">;</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">void</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> ShowNETable()</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List neList =
</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #0000c0;">ctrl</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">.queryNEList();</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">for</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">(</span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">int</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> i =
0;i&lt;neList.size();i++)</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">//</span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">将</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">neList</span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">转换为表格行</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">//</span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">显示表格</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">...</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">}</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">class</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> QueryCtrl {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;"> List queryNEList()
</span></p>
<p style="margin-left: 21pt; text-align: left; text-indent: 19.6pt;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">{</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">return</span></strong><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">null</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">;</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: black;">}</span></p>
<p style="text-align: left;" align="left"><strong><span style="font-size: 10pt; font-family: 宋体;">测试类：</span></strong></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #7f0055;">public class</span></strong><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;"> TestNEListTable<strong><span style="color: #7f0055;"> extends</span></strong> TestCase </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">{</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">private</span></strong>
NEListTable table = null;</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">private</span></strong>
TestHelper helper = null;</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">public</span></strong>
void testShowNETable() </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mock_QueryCtrl ctrl = new
Mock_QueryCtrl();</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; helper.setObjectField(table,"ctrl",ctrl);<span style="color: #3f7f5f;">//</span></span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">将</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">Mock</span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">对象注入</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">table</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table.ShowNETable();</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertTrue(table.getRowCount()&gt;0);</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">private
class</span></strong> Mock_QueryCtrl <strong><span style="color: #7f0055;">extends</span></strong>
QueryCtrl</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">public</span></strong>
List queryNEList()</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List neList = new
ArrayList();</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #3f7f5f;">//</span></span><span style="font-size: 10pt; font-family: 宋体; color: #3f7f5f;">返回你需要的数据</span><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #3f7f5f;">...</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong><span style="color: #7f0055;">return</span></strong> neList;</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;;">}</span></p>
<p style="margin-left: 21pt; text-align: left;" align="left"><br />
<span style="font-size: 10pt; font-family: &quot;Courier New&quot;;"><br />
原地址:http://www.blogjava.net/wukaichun600/archive/2008/07/10/213790.html<br />
</span></p>
  <img src ="http://www.blogjava.net/zhuzi1987/aggbug/216142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-07-20 09:31 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]单元测试(基础篇)</title><link>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216141.html</link><dc:creator>竹子</dc:creator><author>竹子</author><pubDate>Sun, 20 Jul 2008 01:29:00 GMT</pubDate><guid>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216141.html</guid><wfw:comment>http://www.blogjava.net/zhuzi1987/comments/216141.html</wfw:comment><comments>http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216141.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuzi1987/comments/commentRss/216141.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuzi1987/services/trackbacks/216141.html</trackback:ping><description><![CDATA[<p><span style="font-family: 宋体;">这篇文章的主旨是在正式进行单元测试之前帮助大家厘清一些概念。了解什么是单元测
试，可以做什么，有哪些指导原则。做了又有什么好处，它又存在什么样的局限性。最后重点讲了现在做单元测试的难点。事实上这是任何单元测试都会面临的一个
问题。在这里分享我的观点，与大家共勉！</span></p>
<h1 style="margin-left: 0cm; text-align: center; text-indent: 0cm;" align="center"><span style="font-family: &quot;HG Mincho Light J&quot;;">单元测试</span>(<span style="font-family: &quot;HG Mincho Light J&quot;;">基础</span><span style="font-family: 宋体;">篇</span>)</h1>
<h2 style="margin-left: 0cm; text-indent: 0cm;"><span style="font-family: &quot;HG Mincho Light J&quot;;">一、</span><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">什么是</span><span style="font-style: normal;">UT</span></h2>
<p style="margin-left: 4.3pt;">UT<span style="font-family: 宋体;">测的是方法，检验的是一个类对外界的承诺。因此，大多数情况下，我们测的应该是公共方法，除非不得已才对私有方法进行测试。</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">方法是程序设计的最小单位。</span>UT<span style="font-family: 宋体;">的局限也体现在这里，它并没有针对类之间的交互做检验。所以，不能指望单元测试做完了，就没有问题了。在这个方面的欠缺，我们可以通过自动化的功能</span>/<span style="font-family: 宋体;">组件测试来完成。这也是开发者测试的一部分。</span></p>
<h2 style="margin-left: 0cm; text-indent: 0cm;"><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">二、</span><span style="font-style: normal;">UT</span><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">的任务</span></h2>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>1.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">尽早的发现问题</span></p>
<p><span style="font-family: 宋体;">说白了，就是不要让问题流</span><span style="font-family: 宋体;">出</span><span style="font-family: 宋体;">去。让我们的缺陷率降低</span><span style="font-family: 宋体;">，把我们的产品做的漂亮</span><span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">另一方面，一些细类度的问题在这里也确实更容易发现，同时也为进行更大粒度的测试做好集成准备。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>2.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">编织一层保护网</span></p>
<p><span style="font-family: 宋体;">给新的代码建立有效的保护。</span> <span style="font-family: 宋体;">保证对代码每一份改动，都不会对现有系统造成伤害。避免了引入问题。</span> </p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span style="font-family: 宋体;">3.写出优雅的代码</span></p>
<p><span style="font-family: 宋体;">编写单元测试的过程，实质是使用我们自己代码的过程。我们成了第一个真正意义上的体验者。在这个过程中，我们为了使代码易用会进行不断的重构。最终的交付代码必然会更优雅。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>4.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">建立程序员的自信</span></p>
<p><span style="font-family: 宋体;">我们习惯于两眼一抹黑，不管三七二十一就把代码写完了。</span>Code<span style="font-family: 宋体;">阶段结束，然后不断的调试，修修补补跑起来就过了</span>TR4<span style="font-family: 宋体;">。没人敢说，他代码一写完了，就能跑起来。这种做法是很没人性的，系统搞挂几次后就心里发虚，一点底气都没有。</span><span style="font-family: 宋体;">但是，</span><span style="font-family: 宋体;">这是我们的职业，我们为着自己的荣耀而战。在任何时候，都需要信心满盈。</span></p>
<h2 style="margin-left: 0cm; text-indent: 0cm;"><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">三、</span><span style="font-style: normal;">UT</span><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">的基本原则</span></h2>
<p style="margin-left: 22.3pt; text-indent: -18pt;"><span style="font-family: 宋体;">1.一个类、一个方法、一条路径</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">我们一次只测一个类的
一个方法。刚开始做单元测试的时候，很多人会自然而然的做成了功能测试。因为，前一部分执行的结果恰好为后一部分准备好了输入。而另一方面，连续的执行过
程组成了一个明确的场景，让具体的功能变得完整可见，这正是我们期待的，让我们变得有信心。那为什么不顺其自然呢？</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">原因在于：</span></p>
<p style="margin-left: 4.3pt;">1<span style="font-family: 宋体;">）、单元测试要保证一定的微粒度。从单元测试到功能测试，这之间的粒度会越来越大，越往后，我们会越少的关注细节。如果直接跳跃到功能测试上，会让我们遗漏掉一些问题，在以后的粗粒度测试中，它们会转变为很难重现或者不可重现的致命问题。</span></p>
<p style="margin-left: 4.3pt;">2<span style="font-family: 宋体;">）、上述场景之所以
会出现，是因为先写代码后写测试导致的。相当于代码已经集成，具备了做功能测试的一定条件。这个时候让再走回头路做单元测试，当然不如直接就做功能测试来
的顺当。所以，应该一个测试、一个方法，一个方法一个测试，这样不断的一步一步的循环迭代集成来的好。</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">另一方面，为了将一个方法的多个不同的执行路径分开，我们<span style="color: blue;">必须保证一次只测一个方法的一个路径。</span>这样，前置条件和后置条件就会很明确，容易准备测试环境。</span></p>
<p style="margin-left: 22.3pt; text-indent: -18pt;"><span style="font-family: 宋体;">2.重构以便于测试</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">面对着一个方法，你感到一筹莫展。并不是你的错，而是因为这个方法很烂。测一个方法就是在使用这个方法，你自己都这样无奈，将来真正使用的人岂不是要骂娘？</span><a href="http://www.liuming.com/"><span style="font-family: 宋体; color: windowtext; text-decoration: none;">雁过留声，人过留名</span></a><span style="font-family: 宋体;">。这个时候，重构一下很值得。</span></p>
<p style="margin-left: 22.3pt; text-indent: -18pt;"><span style="font-family: 宋体;">3.保证测试方法简洁</span></p>
<p style="margin-left: 4.3pt;"><span style="font-family: 宋体;">如果连测试方法都很复杂，难道我们还要再写测试用例来保证它的正确执行不成？这样岂不是麻烦大了！所以，测试方法一定要写的尽可能的简单，写到你认为白痴都能看懂的程度。</span></p>
<h2 style="margin-left: 0cm; text-indent: 0cm;"><span style="font-family: &quot;HG Mincho Light J&quot;; font-style: normal;">四、如何才能做</span><span style="font-style: normal;">UT </span></h2>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span style="font-family: 宋体; color: blue;">1.代码要首先可测，然后才能测。</span></p>
<p><span style="font-family: 宋体;">首先要遵守</span><span style="font-family: 宋体;">契约式设计。类的每一个方法都应该对外承担了一份契约，有明确的前置条件和后置条件。</span> <span style="font-family: 宋体;">当你对这个方法进行测试之前必须清楚明白这两个条件。一个有效的方法一定是做了什么事的。一定会产生一定的影响，我们可以通过对外围环境的改变来检测方法产生的作用是否如预期</span>(<span style="font-family: 宋体;">例如</span>,<span style="font-family: 宋体;">获取某一对象的属性进行检测</span>)<span style="font-family: 宋体;">。</span></p>
<p><span style="font-family: 宋体;">其次是，</span><span style="font-family: 宋体;">低</span>Ce<span style="font-family: 宋体;">和单一责任原则。一个方法对外的依赖应该单一，不应该取决于很多的外部环境。因为不同的外部环境越多，组合项就越多，要测的先决条件就越多。而一个方法对外部环境的影响太多，则意味着职责不单一，对于输出越难测。</span></p>
<p><span style="font-family: 宋体;">曾经听有人讲到，这些道理，你懂了就懂，不懂就不懂，说了没用。但我认为，如果你还以为这些只是大道理，如果你还想对它有点切身的感受，做单元测试是一个很好的途径。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span style="font-family: 宋体;">2.信任你该信任的。</span></p>
<p><span style="font-family: 宋体;">对于已经稳定的部分，类似于第三方包，平台部分，其至是遗留系统中已经证明是可靠的部分，都可以信任。这些是我们用例代码依赖的部分，是我们用来检验其它待测部分的基石。如果什么都要测，就会变成什么都测不了。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>3.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">单元测试要尽量少的增加开发人员的负担。</span></p>
<p><span style="font-family: 宋体;">一方面，我们实在被问题单压抑的太久了。所以，从全局上来看待这个问题，如果可以确实的减少后期的维护压力，对我们自身而言当然是有益的。所谓增加的负担，不过是提早了结了一些痛苦。</span></p>
<p><span style="font-family: 宋体;">另一方面，</span><span style="font-family: 宋体;">单元测试必须自动化</span> <span style="font-family: 宋体;">，必</span><span style="font-family: 宋体;">须</span><span style="font-family: 宋体;">简单，傻瓜化。</span><span style="font-family: 宋体;">这是我们要努力的目标。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span style="font-family: 宋体;">4.将调试的时间用来写单元测试</span></p>
<p style="margin-left: 21.2pt;"><span style="font-family: 宋体;">没有做过自动化单元测试的人永远也不能体会其给程序员带来的自信和好处。如果你还在调试，不如顺手加个测试。以后，保证同样的问题不会从你的眼皮下溜走。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>5.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">现在的单元测试难在哪里？</span></p>
<p><span style="font-family: 宋体;">难以打桩。因为我们对其它模块的关联是这样的。</span></p>
<p><span style="font-family: 宋体;">
<div align="center"><img alt="" src="../../images/blogjava_net/wukaichun600/1.png" height="264" width="505" /></div>
<br />
</span></p>
<p style="text-align: center;" align="center"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">这就是麻烦的所在，关联太多。如果我们要测，我们就要打桩。但是，</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>1.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">望而生畏，太多。</span></p>
<p style="margin-left: 35.35pt; text-indent: -14.15pt;"><span><span>2.<span style="font-family: &quot;Times New Roman&quot;; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;
</span></span></span><span style="font-family: 宋体;">无法下手，都是直接对象依赖，而不是接口依赖。</span></p>
<p><span style="font-family: 宋体;">所以，你让我来测这样的代码，我是不会干的。</span></p>
<p><span style="font-family: 宋体;">因为，我希望的是这样的：</span></p>
<p><span style="font-family: 宋体;">
<div align="center"><img alt="" src="../../images/blogjava_net/wukaichun600/2.png" height="253" width="650" /></div>
<br />
</span></p>
<p style="text-align: center;" align="center"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p><span style="font-family: 宋体;">但是，我们现在的代码欠债太多了。没有条件和能力再回去对这些代码进行单元测试。而且，这些功能经过这么多年的维护，大多已稳定。做的性价比也不高</span>,<span style="font-family: 宋体;">不够实惠。所以，不必要做。</span></p>
<p><span style="font-family: 宋体;">但在新功能开发过程中，这些老代码依旧会如恶梦一样纠缠着我们。让写单元测试过程中常常面临着举步维艰的境地。我们不得不在让代码变得可测与对代码的侵入性测试之间进行抉择。</span></p>
<p><br />
</p>
<p><span style="font-family: 宋体;">原地址:http://www.blogjava.net/wukaichun600/archive/2008/06/10/206999.html<br />
</span></p>
  <img src ="http://www.blogjava.net/zhuzi1987/aggbug/216141.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuzi1987/" target="_blank">竹子</a> 2008-07-20 09:29 <a href="http://www.blogjava.net/zhuzi1987/archive/2008/07/20/216141.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>