﻿<?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-桔子园-随笔分类-J2SE</title><link>http://www.blogjava.net/orangelizq/category/26251.html</link><description>orangelizq</description><language>zh-cn</language><lastBuildDate>Wed, 29 May 2013 18:48:20 GMT</lastBuildDate><pubDate>Wed, 29 May 2013 18:48:20 GMT</pubDate><ttl>60</ttl><item><title>[转]处理线程泄露</title><link>http://www.blogjava.net/orangelizq/archive/2013/05/29/399926.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 29 May 2013 08:43:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2013/05/29/399926.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/399926.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2013/05/29/399926.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/399926.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/399926.html</trackback:ping><description><![CDATA[<br /><a href="http://sunnylocus.iteye.com/blog/538282">http://sunnylocus.iteye.com/blog/538282</a><br /><br /><span style="font-size: x-small">当一个单线程化的控制台程序因为未捕获的异常终止的时候，程序停止运行，并生了栈追踪，这与典型的程序输出不同，当一个程序发生了异常说明有不稳定的因素存在。如果并发程序中线程失败就没那么容易发现了。栈追踪可能会从控制台输出，但是没有人会去一直在看控制台，并且，当线程失败的时候，应用程序可能看起来仍在工作。就象程序能跑在50个线程池上，也能够跑在49个线程的线程池上，区别在于50个人干的活要比49个人干的活要多的多。</span> 
<p><span style="font-size: x-small">　　导致线程死亡的的最主要的原因是RuntimeException。因为这些异常表明一个程序错误或者不可修复的错误，它们不着顺着栈的调用传递，此时，默认的行为是在控制台打印栈追踪的信息，并终止线程。</span></p>
<p><span style="font-size: x-small">　　我们举个例子，将奴隶主比作是你写的程序，奴隶比作是线程。假如你是奴隶主，你手下有5名奴隶，你分派他们一项任务去将不断开采来的石头搬到某个地方。如果中途有奴隶逃跑，那么搬运石头的效率就会下降，如果你没有措施发现奴隶逃跑，最后连一个奴隶也没有了，没有人再去搬石头了。当你发现程序有问题，比如程序刚启动的时候处理速度很快，以后越跑越慢，最后完全停止了，你可能不知道问题出在哪儿，其实这都是因为线程泄露引起的。想解决奴隶逃跑问题，不难，给每个奴隶戴上个报警器，一逃跑报警器就给你发信息，告诉哪个奴隶，因为什么原因逃跑了，可以根据需要再增加一名奴隶，让搬石头的奴隶数量始终维持在5名或着将信息记录到文件，便于分析导致线程泄露的原意改进程序。ok,我们用代码说话</span></p>
<p><span style="font-size: x-small">１、定义好报警器</span></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://sunnylocus.iteye.com/blog/538282#"><img alt="复制代码" src="http://sunnylocus.iteye.com/images/icon_copy.gif" /></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"><img class="star" alt="收藏代码" src="http://sunnylocus.iteye.com/images/icon_star.png" /><img class="spinner" style="display: none" src="http://sunnylocus.iteye.com/images/spinner.gif"  alt="" /></a></div></div>
<ol class="dp-j"><li><span class="keyword">package</span><span>&nbsp;com.bill99.thread.test; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.lang.Thread.UncaughtExceptionHandler; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="comment">//将泄露的线程信息输出到控制台 </span><span>&nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;UEHLogger&nbsp;</span><span class="keyword">implements</span><span>&nbsp;UncaughtExceptionHandler&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;uncaughtException(Thread&nbsp;t,&nbsp;Throwable&nbsp;e)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(String.format(</span><span class="string">"不好了，有奴隶逃跑了!奴隶姓名：%1$s,编号：%2$s,逃跑原因：%3$s"</span><span>,&nbsp;t.getName(),t.getId(),e.getMessage())); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"还剩"</span><span>+HelotPool.helotPool.getActiveCount()+</span><span class="string">"个奴隶"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre title="处理线程泄露" class="java" style="display: none" pre_index="0" source_url="http://sunnylocus.iteye.com/blog/538282" codeable_type="Blog" codeable_id="538282" name="code">package com.bill99.thread.test;

import java.lang.Thread.UncaughtExceptionHandler;

//将泄露的线程信息输出到控制台
public class UEHLogger implements UncaughtExceptionHandler {
	public void uncaughtException(Thread t, Throwable e) {
		System.out.println(String.format("不好了，有奴隶逃跑了!奴隶姓名：%1$s,编号：%2$s,逃跑原因：%3$s", t.getName(),t.getId(),e.getMessage()));
		System.out.println("还剩"+HelotPool.helotPool.getActiveCount()+"个奴隶");
	}
}
</pre>
<p>&nbsp;&nbsp;</p>
<p><span style="font-size: x-small">２、我们先建立一个奴隶工厂，每名奴隶出工厂的时候都会有一个报警器</span></p>
<p><span style="font-size: x-small">&nbsp;　　　 </p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://sunnylocus.iteye.com/blog/538282#"><img alt="复制代码" src="http://sunnylocus.iteye.com/images/icon_copy.gif" /></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"><img class="star" alt="收藏代码" src="http://sunnylocus.iteye.com/images/icon_star.png" /><img class="spinner" style="display: none" src="http://sunnylocus.iteye.com/images/spinner.gif"  alt="" /></a></div></div>
<ol class="dp-j"><li><span class="keyword">package</span><span>&nbsp;com.bill99.thread.test; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.ThreadFactory; &nbsp;&nbsp;</span></span></li><li><span></span><span class="comment">//奴隶制造工厂 </span><span>&nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;HelotFactory&nbsp;</span><span class="keyword">implements</span><span>&nbsp;ThreadFactory&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">volatile</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;helotId=</span><span class="number"><font color="#c00000">0</font></span><span>;</span><span class="comment">//奴隶编号 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//产生一个新奴隶 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;Thread&nbsp;newThread(Runnable&nbsp;r)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;helotThread&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Thread(r); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotThread.setName(</span><span class="string">"helot-Thread-"</span><span>+gethelotId());</span><span class="comment">//设置奴隶姓名 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotThread.setUncaughtExceptionHandler(</span><span class="keyword">new</span><span>&nbsp;UEHLogger());</span><span class="comment">//UEHLogger就是报警器 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;helotThread; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;gethelotId(){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;++helotId; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre title="处理线程泄露" class="java" style="display: none" pre_index="1" source_url="http://sunnylocus.iteye.com/blog/538282" codeable_type="Blog" codeable_id="538282" name="code">package com.bill99.thread.test;

import java.util.concurrent.ThreadFactory;
//奴隶制造工厂
public class HelotFactory implements ThreadFactory {
	private volatile int helotId=0;//奴隶编号
	//产生一个新奴隶
	public Thread newThread(Runnable r) {
		Thread helotThread = new Thread(r);
		helotThread.setName("helot-Thread-"+gethelotId());//设置奴隶姓名
		helotThread.setUncaughtExceptionHandler(new UEHLogger());//UEHLogger就是报警器
		return helotThread;
	}
	private int gethelotId(){
		return ++helotId;
	}
}
</pre>
<p>&nbsp;３、奴隶逃跑测试,看看是否会触发报警器</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://sunnylocus.iteye.com/blog/538282#"><img alt="复制代码" src="http://sunnylocus.iteye.com/images/icon_copy.gif" /></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"><img class="star" alt="收藏代码" src="http://sunnylocus.iteye.com/images/icon_star.png" /><img class="spinner" style="display: none" src="http://sunnylocus.iteye.com/images/spinner.gif"  alt="" /></a></div></div>
<ol class="dp-j"><li><span class="keyword">package</span><span>&nbsp;com.bill99.thread.test; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.ThreadFactory; &nbsp;&nbsp;</span></span></li><li><span></span><span class="comment">//奴隶逃跑测试 </span><span>&nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;HelotEscapeTest&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;ThreadFactory&nbsp;factory&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;HelotFactory(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;Thread&nbsp;helotThread&nbsp;=&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;HelotEscapeTest(){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Runnable&nbsp;task&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Runnable()&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;stoneNum=</span><span class="number"><font color="#c00000">1</font></span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;run()&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>(!Thread.interrupted()){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(helotThread.getName()+</span><span class="string">"&nbsp;搬第"</span><span>+stoneNum+</span><span class="string">"块石头.."</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stoneNum++; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><font color="#c00000">500</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>(InterruptedException&nbsp;e){e.printStackTrace();} &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(stoneNum&gt;</span><span class="number"><font color="#c00000">100</font></span><span>){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;RuntimeException(</span><span class="string">"又饿又累没力气搬石头了"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotThread&nbsp;=&nbsp;factory.newThread(task); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//开始干活 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;startWork(){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotThread.start(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelotEscapeTest&nbsp;test&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;HelotEscapeTest(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.startWork(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre title="处理线程泄露" class="java" style="display: none" pre_index="2" source_url="http://sunnylocus.iteye.com/blog/538282" codeable_type="Blog" codeable_id="538282" name="code">package com.bill99.thread.test;

import java.util.concurrent.ThreadFactory;
//奴隶逃跑测试
public class HelotEscapeTest {
	private ThreadFactory factory = new HelotFactory();
	private Thread helotThread = null;
	
	public HelotEscapeTest(){
		Runnable task = new Runnable() {
			int stoneNum=1;
			public void run() {
				while(!Thread.interrupted()){
					System.out.println(helotThread.getName()+" 搬第"+stoneNum+"块石头..");
					stoneNum++;
					try{
						Thread.sleep(500);
					} catch(InterruptedException e){e.printStackTrace();}
					if(stoneNum&gt;100){
						throw new RuntimeException("又饿又累没力气搬石头了");
					}
				}
			}
		};
		helotThread = factory.newThread(task);
	}
	//开始干活
	public void startWork(){
		helotThread.start();
	}
	public static void main(String[] args) {
		HelotEscapeTest test = new HelotEscapeTest();
		test.startWork();
	}
}
</pre>
<p>&nbsp;运行程序后，奴隶在搬完100块石头后不干了，报警器给出提示信息</p>
<p>&nbsp;&nbsp; <strong>不好了，有奴隶逃跑了!奴隶姓名：helot-Thread-1,编号：7,逃跑原因：又饿又累没力气搬石头了</strong></p>
<p>这样我就能找出线程泄露的原因了。如果在线程池中发生了泄露是否也能记录？</p>
<p>4、奴隶池测试</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://sunnylocus.iteye.com/blog/538282#"><img alt="复制代码" src="http://sunnylocus.iteye.com/images/icon_copy.gif" /></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"><img class="star" alt="收藏代码" src="http://sunnylocus.iteye.com/images/icon_star.png" /><img class="spinner" style="display: none" src="http://sunnylocus.iteye.com/images/spinner.gif"  alt="" /></a></div></div>
<ol class="dp-j"><li><span class="keyword">package</span><span>&nbsp;com.bill99.thread.test; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.Random; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.BlockingQueue; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.LinkedBlockingQueue; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.ThreadFactory; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.ThreadPoolExecutor; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.TimeUnit; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;HelotPoolTest&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;ThreadPoolExecutor&nbsp;helotPool; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;helotNum=&nbsp;</span><span class="number"><font color="#c00000">5</font></span><span>;&nbsp;&nbsp;</span><span class="comment">//奴隶数 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;Random&nbsp;random&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Random(</span><span class="number"><font color="#c00000">100</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;BlockingQueue&lt;Runnable&gt;&nbsp;queue&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;LinkedBlockingQueue&lt;Runnable&gt;(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;HelotPoolTest()&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadFactory&nbsp;factory&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;HelotFactory(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotPool&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;ThreadPoolExecutor(&nbsp;helotNum,helotNum,</span><span class="number"><font color="#c00000">1000</font></span><span>,TimeUnit.SECONDS,queue,factory); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//分配任务 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;assignTask(){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;MAX=</span><span class="number"><font color="#c00000">100</font></span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;MIN=</span><span class="number"><font color="#c00000">10</font></span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Runnable&nbsp;task&nbsp;=&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j=</span><span class="number"><font color="#c00000">0</font></span><span>;j&lt;</span><span class="number"><font color="#c00000">20</font></span><span>;j++){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;task&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Runnable()&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;stoneNum&nbsp;=&nbsp;random.nextInt(MAX&nbsp;-&nbsp;MIN&nbsp;+&nbsp;</span><span class="number"><font color="#c00000">1</font></span><span>)&nbsp;+&nbsp;MIN;</span><span class="comment">//&nbsp;生成10~100范围的随机数 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;run()&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number"><font color="#c00000">1</font></span><span>;&nbsp;i&nbsp;&lt;=&nbsp;stoneNum;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Thread.currentThread().getName()&nbsp;+&nbsp;</span><span class="string">"&nbsp;搬完"</span><span>+&nbsp;i&nbsp;+&nbsp;</span><span class="string">"块石头"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(i&nbsp;==&nbsp;</span><span class="number"><font color="#c00000">60</font></span><span>)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;RuntimeException(</span><span class="string">"搬完第60块石头不干了"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number"><font color="#c00000">100</font></span><span>);</span><span class="comment">//休息下 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span class="keyword">catch</span><span>(InterruptedException&nbsp;e){e.printStackTrace();} &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;queue.add(task); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//开始干活 </span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;startWork(){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helotPool.prestartAllCoreThreads(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelotPoolTest&nbsp;pool&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;HelotPoolTest(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.assignTask(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.startWork(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre title="处理线程泄露" class="java" style="display: none" pre_index="3" source_url="http://sunnylocus.iteye.com/blog/538282" codeable_type="Blog" codeable_id="538282" name="code">package com.bill99.thread.test;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class HelotPoolTest {
	
	public static ThreadPoolExecutor helotPool;
	private int helotNum= 5;  //奴隶数
	private Random random = new Random(100);
	
	private BlockingQueue&lt;Runnable&gt; queue = new LinkedBlockingQueue&lt;Runnable&gt;();
	
	public HelotPoolTest() {
		ThreadFactory factory = new HelotFactory();
		helotPool = new ThreadPoolExecutor( helotNum,helotNum,1000,TimeUnit.SECONDS,queue,factory);
	}
	//分配任务
	public void assignTask(){
		final int MAX=100;
		final int MIN=10;
		Runnable task = null;
		for(int j=0;j&lt;20;j++){
			task = new Runnable() {
				int stoneNum = random.nextInt(MAX - MIN + 1) + MIN;// 生成10~100范围的随机数
				public void run() {
					for (int i = 1; i &lt;= stoneNum; i++) {
						System.out.println(Thread.currentThread().getName() + " 搬完"+ i + "块石头");
						if (i == 60) {
							throw new RuntimeException("搬完第60块石头不干了");
						}
						try{
							Thread.sleep(100);//休息下
						}catch(InterruptedException e){e.printStackTrace();}
					}
				}
			};
			queue.add(task);
		}
	}
	//开始干活
	public void startWork(){
		helotPool.prestartAllCoreThreads();
	}
	public static void main(String[] args) {
		HelotPoolTest pool = new HelotPoolTest();
		pool.assignTask();
		pool.startWork();
	}
}
</pre>
<p>&nbsp;程序启动，用Jprofiler监控，刚启动时会有5名奴隶干活不一会就会有线程退出，最后5个线程全部退，报警器对每个线程退出都能记录到导致线程退出的原因。标准的Executor实现是:</p>
<p><span style="color: #ff0000">在需求不高时回收空闲的线程，在需求增加时添加新的线程，如果任务抛出了异常，就会用一个全新的工作线程取代出错的那个。</span></p>
<p><span style="color: #000000">JDK文档是这么说的，不过通过Jprofiler监控只有在5名奴隶全部逃跑，没人干活的时候ThreadPoolExecutor才会生成一个新线程继续搬石头，并不是只要一个线程退出就会马上生成新线程去代替。</span></p><img src ="http://www.blogjava.net/orangelizq/aggbug/399926.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2013-05-29 16:43 <a href="http://www.blogjava.net/orangelizq/archive/2013/05/29/399926.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]内部类详解 </title><link>http://www.blogjava.net/orangelizq/archive/2010/04/21/318984.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 21 Apr 2010 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2010/04/21/318984.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/318984.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2010/04/21/318984.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/318984.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/318984.html</trackback:ping><description><![CDATA[内部类详解 <br />1、定义 <br />  一个类的定义放在另一个类的内部，这个类就叫做内部类。 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> First {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Contents{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> f(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"In Class First's inner Class Contents method f()"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>}   </span></li><li><span> }  </span></li></ol></div><pre class="java" style="DISPLAY: none" name="code"> </pre>像这样的，Contents就叫做内部类 <br />内部类了解外围类，并能与之通信(后面详细讲) <br /><br />2、链接到外围类 <br />  创建了内部类对象时，它会与创造它的外围对象有了某种联系，于是能访问外围类的所有成员，不需任何特殊条件。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> First {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Contents{   </span></li><li><span>         </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> getStr(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"First.str="</font></span><span>+str);   </span></li><li><span>     }   </span></li><li><span>}   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> String str;   </span></li><li><span>    }   </span></li><li><span>   </span></li></ol></div>  在内部类Contents中，可以使用外围类First的字段str。 <br />  那么，它是如何实现的呢？ <br />  是这样的，用外围类创建内部类对象时，此内部类对象会秘密的捕获一个指向外围类的引用，于是，可以通过这个引用来访问外围类的成员。 <br />  通常，这些都是编译器来处理，我们看不到，也不用关心这个。 <br />  正是因为如此，我们创建内部类对象时，必须与外围类对象相关联。 <br />  注：嵌套类(后面会讲到)除外。 <br /><br />3、使用关键字.this与.new <br />  内部类中得到当前外围类对象的引用，可以使用.this关键字，注意与new的区别 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>    </span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num ;   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Test2(){   </span></li><li><span>       </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Test2(</span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">this</font></strong></span><span>.num = num;   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Inner{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Test2 getTest2(){   </span></li><li><span>        </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> Test2.</span><span class="keyword"><strong><font color="#7f0055">this</font></strong></span><span>;   </span></li><li><span>    }   </span></li><li><span>       </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Test2 newTest2(){   </span></li><li><span>        </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Test2();   </span></li><li><span>    }   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String [] args){   </span></li><li><span>    Test2 test = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Test2(</span><span class="number"><font color="#c00000">5</font></span><span>);   </span></li><li><span>    Test2.Inner inner = test.</span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Inner();   </span></li><li><span>    Test2 test2 = inner.getTest2();   </span></li><li><span>    Test2 test3 = inner.newTest2();   </span></li><li><span>    System.out.println(test2.num);   </span></li><li><span>    System.out.println(test3.num);   </span></li><li><span>}   </span></li><li><span>   </span></li></ol></div>  输出结果为5 0 <br />  使用.this后，得到时创建该内部类时使用的外围类对象的引用，new则是创建了一个新的引用。 <br /><br />  .new关键字 <br />  如果想直接创建一个内部类对象，而不是通过外围类对象的方法来得到，可以使用.new关键字 <br />  形式是这样的： <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>OutClass.InnerClass obj = outClassInstance.</span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> InnerClass();   </span></span></li></ol></div><br />  必须是外围类对象.new，而不能是外围类.new <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> First {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Contents{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> f(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"In Class First's inner Class Contents method f()"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> getStr(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"First.str="</font></span><span>+str);   </span></li><li><span>    }   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String [] args){   </span></li><li><span>    First first = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> First();   </span></li><li><span>    First.Contents contents = first.</span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Contents();   </span></li><li><span>    contents.f();   </span></li><li><span>}   </span></li><li><span>    }   </span></li><li><span>   </span></li></ol></div><pre class="java" style="DISPLAY: none" name="code"></pre>  必须通过外围类First的对象first来创建一个内部类的对象 <br />  而且需要注意的是，在创建外围类对象之前，不可能创建内部类的对象(嵌套类除外)。 <br />4、内部类与向上转型 <br />  将内部类向上转型为基类型，尤其是接口时，内部类就有了用武之地。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> Shape {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> paint();   </span></li><li><span>    }   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Painter {   </span></li><li><span>       </span></li><li><span>       </span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> InnerShape </span><span class="keyword"><strong><font color="#7f0055">implements</font></strong></span><span> Shape{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> paint(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"painter paint() method"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Shape getShape(){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> InnerShape();   </span></li><li><span>}      </span></li><li><span>       </span></li><li><span>       </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String []args){   </span></li><li><span>    Painter painter = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Painter();   </span></li><li><span>    Shape shape = painter. getShape();   </span></li><li><span>    shape.paint();   </span></li><li><span>}   </span></li><li><span>    }   </span></li><li><span>   </span></li></ol></div>  此时，内部类是private的，可以它的外围类Painter以外，没人能访问。 <br />  这样,private内部类给累的设计者提供了一种途径，通过这种方式可以完全阻止任何依赖于类型的编码，并完全隐藏实现的细节。 <br /><br />5、方法内的类 <br />  可以在方法内创建一个类。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> test(){   </span></span></li><li><span>ass Inner{   </span></li><li><span> </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> method(){   </span></li><li><span>ystem.out.println(</span><span class="string"><font color="#0000ff">"在方法内创建的类"</font></span><span>);   </span></li><li><span> }   </span></li><li><span>  </span></li><li><span>}   </span></li></ol></div><pre class="java" style="DISPLAY: none" name="code"></pre>  值得注意的是：方法内创建的类，不能加访问修饰符。 <br />  另外，方法内部的类也不是在调用方法时才会创建的，它们一样也被编译了(怎么知道的？后面会有讲解)。 <br /><br />6、匿名内部类 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Painter {   </span></span></li><li><span>ublic Shape getShape(){   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Shape(){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> paint(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"painter paint() method"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>};   </span></li><li><span>  </span></li><li><span>      </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String [] args){   </span></li><li><span>            Painter painter = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Painter();   </span></li><li><span>            Shape shape = painter.getShape();   </span></li><li><span>            shape.paint();   </span></li><li><span>      }   </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> Shape {   </span></li><li><span>ublic </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> paint();   </span></li><li><span>  }   </span></li></ol></div><pre class="java" style="DISPLAY: none" name="code"></pre>  注意，匿名内部类后面的分号不可缺少！ <br />   匿名类，顾名思义，就是没有名称。 <br />  getShape（）方法里，就使用了匿名内部类。 <br />  看上去很奇怪，不符合传统的写法？ <br />  第一眼看上去确实是这样的。 <br /><br />  这样写，意思是创建了一个实现了Shape的匿名类的对象。 <br />  匿名类可以创建，接口，抽象类，与普通类的对象。创建接口时，必须实现接口中所有方法。 <br />  <br />  这是无参的，如果需要参数呢？ <br />  可以直接传。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> B {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A getA(</span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> A(num){   </span></li><li><span>           </span></li><li><span>    };   </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> A {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num;   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A(</span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">this</font></strong></span><span>.num = num;   </span></li><li><span>}   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A(){   </span></li><li><span>       </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span></li></ol></div>  Ok，在这个例子中，可以为A的构造方法传入一个参数。在匿名内部类中，并没有使用到这个参数。 <br />  如果使用到了这个参数，那么这个参数就必须是final的。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> B {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A getA(</span><span class="keyword"><strong><font color="#7f0055">final</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> A(num){   </span></li><li><span>       </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> getNum(){   </span></li><li><span>                     </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> num;   </span></li><li><span>                  }   </span></li><li><span>    };   </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> A {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num;   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A(</span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">this</font></strong></span><span>.num = num;   </span></li><li><span>}   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A(){   </span></li><li><span>       </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span></li></ol></div>  如果不是final的，编译器就会提示出错。 <br />  另外，还可以在匿名内部类里定义属性 <br />  由于类是匿名的，自然没有构造器，如果想模仿构造器，可以采用实例初始化({}) <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> A getA(){   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> A(){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num = </span><span class="number"><font color="#c00000">0</font></span><span>;   </span></li><li><span>    String str;   </span></li><li><span>    {   </span></li><li><span>        str = </span><span class="string"><font color="#0000ff">"javaeye"</font></span><span>;   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"hello robbin"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>};   </span></li><li><span>   }   </span></li><li><span>   </span></li></ol></div>  匿名内部类通过实例初始化，可以达到类似构造器的效果~ <br /><br />另外可以通过匿名内部类来改造工厂方法。 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> Service {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> method1();   </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> ServiceFactory {   </span></li><li><span>Service getService();   </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Implemention1 </span><span class="keyword"><strong><font color="#7f0055">implements</font></strong></span><span> Service{   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> method1(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"In Implemention1 method method1()"</font></span><span>);   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> ServiceFactory factory = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> ServiceFactory(){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Service getService(){   </span></li><li><span>        </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Implemention1();   </span></li><li><span>    }   </span></li><li><span>};   </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Implemention2 </span><span class="keyword"><strong><font color="#7f0055">implements</font></strong></span><span> Service {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> method1(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"in Implemention2 method method1()"</font></span><span>);   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> ServiceFactory factory = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> ServiceFactory(){   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Service getService(){   </span></li><li><span>        </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Implemention2();   </span></li><li><span>    }   </span></li><li><span>};   </span></li><li><span>  </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Test {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String []args){   </span></li><li><span>    service(Implemention1.factory);   </span></li><li><span>    service(Implemention2.factory);   </span></li><li><span>       </span></li><li><span>    ServiceFactory factory1 = Implemention1.factory;   </span></li><li><span>    Service service1 = factory1.getService();   </span></li><li><span>    service1.method1();   </span></li><li><span>       </span></li><li><span>    ServiceFactory factory2 = Implemention1.factory;   </span></li><li><span>    Service service2 = factory2.getService();   </span></li><li><span>    service2.method1();   </span></li><li><span>}   </span></li><li><span>  }   </span></li></ol></div><br />在Implemention1和2中匿名内部类用在字段初始化地方。 <br />这样定义的工厂方法，代码上看起来是不是优雅一些？<img src="http://www.javaeye.com/images/smiles/icon_redface.gif" /><br /><br />7、嵌套类 <br />static的内部类就叫做嵌套类 <br />前面提到了很多次，嵌套类是个例外 <br />使用嵌套类时有两点需要注意： <br />   a、创建嵌套类对象时，不需要外围类 <br />   b、在嵌套类中，不能像普通内部类一样访问外围类的非static成员 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> StaticClass {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> num;   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> sum = </span><span class="number"><font color="#c00000">2</font></span><span>;   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> StaticInnerClass{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> getNum(){   </span></li><li><span>    </span><span class="comment"><font color="#008200">//只能访问sum，不能访问num </font></span><span>  </span></li><li><span>               </span><span class="keyword"><strong><font color="#7f0055">return</font></strong></span><span> sum;   </span></li><li><span>    }   </span></li><li><span>}   </span></li><li><span>  }   </span></li><li><span>  </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Test {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">static</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> main(String [] args){   </span></li><li><span>               </span><span class="comment"><font color="#008200">//可以直接通过new来创建嵌套类对象 </font></span><span>  </span></li><li><span>    StaticClass.StaticInnerClass inner = </span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> StaticClass.StaticInnerClass();   </span></li><li><span>    inner.getNum();   </span></li><li><span>}   </span></li><li><span>  }   </span></li></ol></div>  另外，嵌套类还有特殊之处，就是嵌套类中可以有static方法，static字段与嵌套类，而普通内部类中不能有这些。 <br /><br />  8、内部类标识符 <br />  我们知道每个类会产生一个.class文件，文件名即为类名 <br />  同样，内部类也会产生这么一个.class文件，但是它的名称却不是内部类的类名，而是有着严格的限制：外围类的名字，加上$,再加上内部类名字。 <br />  前面说到得定义在方法内的内部类，不是在调用方法时生成，而是与外围类一同编译，就可以通过查看.class文件的方式来证明。 <br /><br />  9、为何要内部类？ <br />    a、内部类提供了某种进入外围类的窗户。 <br />    b、也是最吸引人的原因，每个内部类都能独立地继承一个接口，而无论外围类是否已经继承了某个接口。 <br />  因此，内部类使多重继承的解决方案变得更加完整。 <br />  在项目中，需要多重继承，如果是两个接口，那么好办，接口支持多重继承。 <br />  如果是两个类呢？这时只有使用内部类了。 <br />  
<div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/442435#"><img alt="复制代码" src="http://www.javaeye.com/images/icon_copy.gif" /></a></div></div><ol class="dp-j"><li><span><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> One {   </span></span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inOne();   </span></li><li><span>   }   </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">interface</font></strong></span><span> Two {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inTwo();   </span></li><li><span>   }   </span></li><li><span>   </span><span class="comment"><font color="#008200">//两个接口，用普通类就可实现多重继承 </font></span><span>  </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> CommonClass </span><span class="keyword"><strong><font color="#7f0055">implements</font></strong></span><span> One,Two {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inOne(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"CommonClass inOne() method"</font></span><span>);   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inTwo(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"CommonClass inTwo() method"</font></span><span>);   </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">abstract</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Three {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">abstract</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inThree();   </span></li><li><span>   }   </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">abstract</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Four {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">abstract</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inFour();   </span></li><li><span>   }   </span></li><li><span>   </span><span class="comment"><font color="#008200">//两个抽象类，使用普通类无法实现多重继承 </font></span><span>  </span></li><li><span>      </span></li><li><span>   </span><span class="comment"><font color="#008200">//使用内部类可以实现 </font></span><span>  </span></li><li><span>   </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Contents </span><span class="keyword"><strong><font color="#7f0055">extends</font></strong></span><span> Three {   </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inThree(){   </span></li><li><span>    System.out.println(</span><span class="string"><font color="#0000ff">"In Contents inThress() method"</font></span><span>);   </span></li><li><span>}   </span></li><li><span>  </span></li><li><span></span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> InnerFour </span><span class="keyword"><strong><font color="#7f0055">extends</font></strong></span><span> Four{   </span></li><li><span>    </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> inFour(){   </span></li><li><span>        System.out.println(</span><span class="string"><font color="#0000ff">"In Contents"</font></span><span>);   </span></li><li><span>    }   </span></li><li><span>       </span></li><li><span>}   </span></li><li><span>   }   </span></li><li><span>   </span></li></ol></div><br />  另外，还有好多地方可以使用内部类。读过hibernate源代码的同学，应该可以发现，里面有好多内部类。 <br />  最常见的内部类，应该是Map.Entry了，可以看看源代码~  <br /><br />总结： <br />  内部类的特性大致就是上述了，特性很直观，了解了之后，使用也很简单。 <br />  但是，何时使用我说的并不是很明确，因为本人知识有限，使用内部类也不是很多。项目中很少用，好像就是ActiveMQ那里用了一些。 <br />  不过，相信大家在了解了内部类的特性之后，再随着时间的推移，慢慢积累经验，应该会做出自己的判断，会在何时使用内部类，怎样应用了。<img src="http://www.javaeye.com/images/smiles/icon_wink.gif" /><img src ="http://www.blogjava.net/orangelizq/aggbug/318984.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2010-04-21 15:03 <a href="http://www.blogjava.net/orangelizq/archive/2010/04/21/318984.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]领略内部类的“内部”</title><link>http://www.blogjava.net/orangelizq/archive/2010/04/21/318982.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 21 Apr 2010 07:00:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2010/04/21/318982.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/318982.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2010/04/21/318982.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/318982.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/318982.html</trackback:ping><description><![CDATA[内部类有两种情况： 
<p>(1) 在类中定义一个类(私有内部类，静态内部类)</p><p>(2) 在方法中定义一个类(局部内部类，匿名内部类)</p><p><strong><span style="COLOR: #800000">1、揭露类中内部类</span></strong></p><p>      我们首先看看类中内部类的两个特点：</p><p>     (1) 在外部类的作用范围内可以任意创建内部类对象，即使内部类是私有的(私有内部类)。即内部类对包围它的外部类可见。</p><p><span style="FONT-SIZE: small"></span></p><div class="bar"><div class="tools"><span><span class="comment"><font color="#008200">//代码2：内部类对外部类可见 </font></span><span>  </span></span></div></div><div class="dp-highlighter"><ol class="dp-j"><li><span></span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Outer{   </span></li><li><span>     </span><span class="comment"><font color="#008200">//创建私有内部类对象 </font></span><span>  </span></li><li><span>     </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> Inner in=</span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Inner();   </span></li><li><span>     </span><span class="comment"><font color="#008200">//私有内部类 </font></span><span>  </span></li><li><span>     </span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Inner{   </span></li><li><span>          ...   </span></li><li><span>     }   </span></li><li><span>}  </span> </li></ol></div><p><br />(2) 在内部类中可以访问其外部类的所有域，即使是私有域。即外部类对内部类可见。</p><div class="dp-highlighter"><div class="bar"><div class="tools"><span><span class="comment"><font color="#008200">//代码3：外部类对内部类可见 </font></span><span>  </span></span></div></div><ol class="dp-j"><li><span></span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Outer{   </span></li><li><span>       </span><span class="comment"><font color="#008200">//外部类私有数据域 </font></span><span>  </span></li><li><span>       </span><span class="keyword"><strong><font color="#7f0055">private</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> data=</span><span class="number"><font color="#c00000">0</font></span><span>;   </span></li><li><span>       </span><span class="comment"><font color="#008200">//内部类 </font></span><span>  </span></li><li><span>       </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Inner{   </span></li><li><span>           </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> print(){   </span></li><li><span>                 </span><span class="comment"><font color="#008200">//内部类访问外部私有数据域 </font></span><span>  </span></li><li><span>                 System.out.println(data);   </span></li><li><span>           }    </span></li><li><span>       }   </span></li><li><span>}  </span></li></ol></div><p>       问题来了：上面两个特点到底如何办到的呢？内部类的"内部"到底发生了什么？</p><p><span style="FONT-SIZE: small"><font size="2"><strong><span style="COLOR: #800000">      其实，内部类是Java编译器一手操办的。虚拟机并不知道内部类与常规类有什么不同。</span></strong>编译器是如何瞒住虚拟机的呢？</font></span></p><p><span style="FONT-SIZE: small"><br /></span></p><p><span style="FONT-SIZE: small"><font size="2">     对内部类进行编译后发现有两个class文件：<strong><span style="COLOR: #ff0000">Outer.class</span></strong>和<span style="COLOR: #ff0000"><strong><span>Outer$Inner.class</span></strong></span>。这说明<strong><span style="COLOR: #800000">内部类Inner仍然被编译成一个独立的类(Outer$Inner.class)，而不是Outer类的某一个域。</span></strong>虚拟机运行的时候，也是把Inner作为一种常规类来处理的。</font></span></p><p>        <span style="FONT-SIZE: small"><font size="2">但问题来了，即然是两个常规类，为什么他们之间可以互相访问私有域那(最开始提到的两个内部类特点)？这就要问问编译器到底把这两个类编译成什么东西了。</font></span></p><p>        <span style="FONT-SIZE: small"><font size="2">我们利用reflect反射机制来探查了一下内部类编译后的情况（关于探查类内部机制的代码提供在下面的附件里Reflect.java）。</font></span></p><p>        <span style="FONT-SIZE: small"><font size="2">(1)、编译代码2生成</font></span><span style="FONT-SIZE: small"><span style="FONT-SIZE: x-small"><font size="1">Outer$Inner.class</font></span><font size="2"> 文件后使用</font></span><span style="FONT-SIZE: small"><font size="2">ReflectUtil.reflect("Outer$Inner")</font></span><span style="FONT-SIZE: small"><font size="2">对内部类Inner进行反射。运行结果</font></span><span style="FONT-SIZE: small"><font size="2">发现了三个隐含的成分：           </font></span></p><div class="dp-highlighter"><div class="bar"><span><span>class Outer$Inner   </span></span></div><ol class="dp-xml"><li><span>{   </span></li><li><span>        Outer$Inner(Outer,Outer$Inner);  //包可见构造器   </span></li><li><span>        private Outer$Inner(Outer);   //私有构造器将设置this$0域   </span></li><li><span>        final Outer this$0;   //外部类实例域this$0   </span></li><li><span>}  </span><pre class="html" style="DISPLAY: none" name="code"> </pre></li></ol></div><p><span style="FONT-SIZE: small"><font size="2">      好了，现在我们可以解释上面的第一个内部类特点了：</font></span><strong><span style="FONT-SIZE: small; COLOR: #ff0000"><font size="2">为什么外部类可以创建内部类的对象？</font></span></strong></p><p><span style="FONT-SIZE: small"><font size="2">     <strong><span style="COLOR: #800000">首先编译器将外、内部类编译后放在同一个包中。</span></strong></font></span></p><p>        <span style="FONT-SIZE: small"><strong><font size="2"><span style="COLOR: #800000">然后在内部类中附加一个包可见构造器。</span></font></strong></span></p><p>        <span style="FONT-SIZE: small"><font size="2"><strong><span style="COLOR: #800000">这样，</span></strong><strong><span style="COLOR: #800000">虚拟机运行Outer类中Inner in=new Inner();</span><span style="COLOR: #800000">实际上调用的是包可见构造： new </span></strong></font></span><strong><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">Outer$Inner(this,null)。</font></span></strong></p><p>        <strong><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">因此即使是private内部类，也会通过隐含的包可见构造器成功的获得私有内部类的构造权限。</font></span></strong></p><p><span style="FONT-SIZE: small"><font size="2">  </font></span><span style="FONT-SIZE: small"><font size="2">   (2)、编译代码3生成</font></span><span style="FONT-SIZE: small"><font size="2">Outer.class文件，然后使用</font></span><span style="FONT-SIZE: small"><font size="2">ReflectUtil.reflect("Outer")</font></span><span style="FONT-SIZE: small"><font size="2">对外部类Outer进行反射</font></span><span style="FONT-SIZE: small"><font size="2">。 运行结果</font></span><span style="FONT-SIZE: small"><font size="2">发现一个隐含成分如下：</font></span></p><div class="dp-highlighter"><div class="bar"><div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://www.javaeye.com/topic/494230#"></a> </div></div><ol class="dp-xml"><li><span><span>class Outer   </span></span></li><li><span>{   </span></li><li><span>          static int access$0(Outer);  //静态方法，返回值是外部类私有域 data 的值。   </span></li><li><span>}  </span></li></ol></div><p><span style="FONT-SIZE: small"><font size="2">      现在可以解释第二个特点了：<strong><span style="COLOR: #ff0000">为什么内部类可以引用外部类的私有域？ </span></strong></font></span></p><p>        <strong><span style="COLOR: #800000"></span><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">原因的关键就在编译器在外围类中添加了静态方法access$0。 它将返回值作为参数传递给他的对象域data。这样内部类Inner中的打印语句：</font></span></strong></p><p><strong><span style="COLOR: #800000">                     </span><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">System.out.println(data);</font></span></strong></p><p><strong><span style="COLOR: #800000">         </span><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">实际上运行的时候调用的是：</font></span></strong></p><p><strong><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">                 S</font></span><span style="FONT-SIZE: small; COLOR: #800000"><font size="2">ystem.out.println(access$0(Outer));</font></span></strong></p><p><span style="COLOR: #ff0000"><strong><span style="FONT-SIZE: small"><font size="2">总结一下编译器对类中内部类做的手脚吧：</font></span></strong></span></p><p><span style="COLOR: #ff0000"><strong><span style="FONT-SIZE: small"><font size="2">(1)  在内部类中偷偷摸摸的创建了包可见构造器，从而使外部类获得了创建权限。</font></span></strong></span></p><p><span style="COLOR: #ff0000"><strong><span style="FONT-SIZE: small"><font size="2">(2)  在外部类中偷偷摸摸的创建了访问私有变量的静态方法，从而</font></span></strong></span><span style="COLOR: #ff0000"><strong><span style="FONT-SIZE: small"><font size="2">使</font></span></strong></span><span style="COLOR: #ff0000"><strong><span style="FONT-SIZE: small"><font size="2">内部类获得了访问权限。</font></span></strong></span></p><p><span style="COLOR: #ff0000"><strong>这样，类中定义的内部类无论私有，公有，静态都可以被包围它的外部类所访问。</strong></span></p><p> </p><p><strong><span style="COLOR: #800000">2、方法中内部类的特点</span></strong></p><p>      方法内部类也有两个特点</p><p>      (1)  方法中的内部类没有访问修饰符，即方法内部类对包围它的方法之外的任何东西都不可见。</p><p>      (2)  方法内部类只能够访问该方法中的局部变量，所以也叫局部内部类。而且这些局部变量一定要是final修饰的常量。</p><div class="dp-highlighter"><div class="bar"><div class="tools"><span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Outter{   </span></span></div></div><ol class="dp-j"><li><span>      </span><span class="keyword"><strong><font color="#7f0055">public</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">void</font></strong></span><span> outMethod(){   </span></li><li><span>             </span><span class="keyword"><strong><font color="#7f0055">final</font></strong></span><span> </span><span class="keyword"><strong><font color="#7f0055">int</font></strong></span><span> beep=</span><span class="number"><font color="#c00000">0</font></span><span>;   </span></li><li><span>             </span><span class="keyword"><strong><font color="#7f0055">class</font></strong></span><span> Inner{   </span></li><li><span>                   </span><span class="comment"><font color="#008200">//使用beep </font></span><span>  </span></li><li><span>             }   </span></li><li><span>             Inner in=</span><span class="keyword"><strong><font color="#7f0055">new</font></strong></span><span> Inner();   </span></li><li><span>      }   </span></li><li><span>}  </span></li></ol></div><p>      这又是为什么呢？</p><p>      (1) 我们首先对Outter类进行反射发现，Outter中再也没有返回私有域的隐藏方法了。</p><p>      (2) 对Inner类的反射发现，Inner类内部多了一个对beep变量的备份隐藏域：final int val$i;</p><p>      我们可以这样解释Inner类中的这个备份常量域，首先当JVM运行到需要创建Inner对象之后，Outter类已经全部运行完毕，这是垃圾回收机制很有可能释放掉局部变量beep。那么Inner类到哪去找beep变量呢？</p><p>    <strong><span style="COLOR: #800000">  编译器又出来帮我们解决了这个问题，他在Inner类中创建了一个beep的备份</span></strong>，也就是说即使Ouuter中的beep被回收了，Inner中还有一个备份存在，自然就不怕找不到了。</p><p>      但是问题又来了。如果Outter中的beep不停的在变化那。那岂不是也要让备份的beep变量无时无刻的变化。<strong><span style="COLOR: #ff0000">为了保持局部变量与局部内部类中备份域保持一致。</span></strong>编译器不得不规定死这些局部域必须是常量，一旦赋值不能再发生变化了。</p><p>      所以为什么局部内部类应用外部方法的域必须是常量域的原因所在了。</p><img src ="http://www.blogjava.net/orangelizq/aggbug/318982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2010-04-21 15:00 <a href="http://www.blogjava.net/orangelizq/archive/2010/04/21/318982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java中的内部类</title><link>http://www.blogjava.net/orangelizq/archive/2010/04/21/318981.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 21 Apr 2010 06:57:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2010/04/21/318981.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/318981.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2010/04/21/318981.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/318981.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/318981.html</trackback:ping><description><![CDATA[
		<div class="blog_content">
				<p>内部类：定义在其他类里面的类。<br />使用内部类的理由：<br />1.内部类方法能够访问外部类的任何数据成员包括私有成员。<br />2.对同一个包的其他类，内部类是不可见的。<br />3.匿名内部类能够方便的定义回调而不用写太多代码。</p>
				<p>非静态内部类没有默认的构造函数(无参数的)，非静态内部类的构造函数都有一个final的外围类对象引用this$0。<br />内部类的特殊语法规则：<br />1.相对内部类，引用其外部类隐式对象的形式：OuterClass.this<br />2.调用内部类的构造函数：outerObject.new InnerClass(construction parameters);<br />3.外部类外面引用内部类：OuterClass.InnerClass</p>
				<p>内部类是一种编译器现象与虚拟机无关。编译器将内部类翻译为用$分隔外部类名和内部类名的常规类文件，虚拟机对此并无所知。<br />使用javap -private OuterClass$InnerClass。javap这个工具确实挺不错的，对分析字节码和源码都有很大的帮助。<br />可以看出详细的内部类源码清单，其中包括了编译器自动添加的部分：<br />public class Outer<br />{<br /> public class Inner<br /> {<br /> }<br />}<br />当内部类是非静态内部类时相应的内部类的详细源码如下：<br />Compiled from "Outer.java"<br />public class Outer$Inner extends java.lang.Object{<br />    final Outer this$0;  //编译器自动在内部类里面添加了指向外部类对象的引用<br />    public Outer$Inner(Outer);  //内部类的构造函数默认有一个外部类对象作为参数。<br />}</p>
				<p>当内部类是静态内部类时：<br />Compiled from "Outer.java"<br />public class Outer$Inner extends java.lang.Object{<br />    public Outer$Inner(); //没有了对外部类对象的引用<br />}</p>
				<p>
						<br />如下代码模拟了上面内部类的情形唯一不同的是这里的Inner没有访问Outer私有数据的权限：<br />class Outer{<br />   Inner in = new Inner(this);<br />}</p>
				<p>class Inner{<br />   public Inner(Outer outer){<br />      this.outer = outer;<br />   }<br />   <br />   private Outer outer;<br />}</p>
				<p> </p>
				<p>//那么权限是如何控制的呢？当内部类中的方法访问到外部类的私有数据时(注意如果内部类没有方法去访问外部类的私有数据不会生成该静态方法static int access$000(Outer);)<br />public class Outer<br />{<br /> private int i;<br /> public void methodOne()<br /> {<br /> }</p>
				<p> class Inner<br /> {<br />  public void print(){<br />   System.out.println(Outer.this.i);<br />  }<br /> }<br />}</p>
				<p>相应的外部类详细源码如下：<br />public class Outer<br />{<br />   public Outer();</p>
				<p>   public void methodOne();<br />   static int access$000(Outer); //由编译器合成的用于内部类对外部类进行特殊访问权限的控制：这也是<br />                                //为什么内部类能够访问外部类中的私有数据的原因。<br />   private int i;<br />}</p>
				<p>内部类访问外部类的private数据的这种方式很可能导致危险，虽然access$000不是一个合法的Java方法名，但是熟悉class文件结构的黑客可以使用十六进制编辑器轻松创建一个用虚拟机指令调用那个方法的类文件。由于隐秘的访问方法需要拥有包可见性，所以攻击代码需要与被攻击类放在同一个包中。总之，如果内部类访问了外部类的私有数据域，就有可能通过附加在外部类所在包中的其他类访问私有数据。</p>
				<p>局部内部类：定义在方法之中，因此局部类没有public和private修饰符。对外界是完全隐藏的。<br />局部类不仅能够访问外部类，还能访问方法中的局部变量(或方法的参数)。不过那些局部变量要声明为final的。</p>
				<p>
						<br />匿名内部类：用外部类+$+数字的形式表示。一个外部类中有多个匿名内部类时，其生成的class文件对应有Outer$(1、2、3)的形式表示。注意到该匿名内部类是final的。<br />final class Outer$1<br />{<br />   Outer$1(Outer);</p>
				<p>   public void method();</p>
				<p>   final Outer this$0;<br />}</p>
				<p>嵌套类：当内部类不需要访问外部类的数据成员时应该使用嵌套类。注意只有内部类可以声明为static的其他不行。<br />在接口中声明的内部类自动转换为public static的，在接口中定义的成员自动都是public static的。</p>
				<p>内部类构造函数的可见性与其类的可见性相同。当内部类是private时其构造函数也是private的，当内部类是public的其构造函数也是public的。</p>
		</div>
		<script type="text/javascript">
				<!--
google_ad_client = "pub-4348265167276910";
/* 468x60, 个人博客 */
google_ad_slot = "2046406163";
google_ad_width = 468;
google_ad_height = 60;
//-->
		</script>
		<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
		</script>
		<script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js">
		</script>
		<script src="http://googleads.g.doubleclick.net/pagead/test_domain.js">
		</script>
		<script src="http://pagead2.googlesyndication.com/pagead/render_ads.js">
		</script>
		<script><![CDATA[oogle_protectAndRun("render_ads.js::google_render_ad", google_handleError, google_render_ad);]]&gt;</script>
<img src ="http://www.blogjava.net/orangelizq/aggbug/318981.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2010-04-21 14:57 <a href="http://www.blogjava.net/orangelizq/archive/2010/04/21/318981.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]一个计算机专业学生几年的Java编程经验汇总</title><link>http://www.blogjava.net/orangelizq/archive/2009/08/27/292844.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 27 Aug 2009 08:52:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/08/27/292844.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/292844.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/08/27/292844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/292844.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/292844.html</trackback:ping><description><![CDATA[<strong>1．
关于动态加载机制</strong><br />
学习Java比C++更容易理解OOP的思想，毕竟C++还混合了不少面向过程的成分。很多人都能背出来Java语言的特点，所谓的动态加载机制等等。当然概念往往是先记住而后消化的，可有多少人真正去体会过动态加载的机制，试图去寻找过其中的细节呢?
提供大家一个方法：<br />
在命令行窗口运行Java程序的时候，加上这个很有用的参数：<br />
<br />
java ?verbose
*.class<br />
<br />
这样会清晰的打印出被加载的类文件，大部分是jdk自身运行需要的，最后几行会明显的看到自己用到的那几个类文件被加载进来的顺序。即使你声明了一个类对象，不实例化也不会加载，说明只有真正用到那个类的实例即对象的时候，才会执行加载。这样是不是大家稍微能明白一点动态加载了呢？^_^<br />
<br />
<strong>2．
关于寻找class文件原理</strong><br />
建议大家在入门的时候在命令行窗口编译和运行，不要借助JCreator或者Eclipse等IDE去帮助做那些事情。尝试自己这样做：<br />
javac
-classpath yourpath *.java<br />
java -classpath yourpath
*.class<br />
也许很多人都能看懂，设置classpath的目的就是告诉编译器去哪里寻找你的class文件.
不过至少笔者今日才弄懂JVM去查询类的原理，编译器加载类要依靠classloader，
而classloader有3个级别，从高到低分别是BootClassLoader(名字可能不准确) , ExtClassLoader,
AppClassLoader.<br />
<br />
这3个加载器分别对应着编译器去寻找类文件的优先级别和不同的路径：BootClassLoader对应jre/classes路径，是编译器最优先寻找class的地方<br />
ExtClassLoader对应jre/lib/ext路径，是编译器次优先寻找class的地方<br />
AppClassLoader对应当前路径，所以也是编译器默认找class的地方<br />
<br />
其实大家可以自己写个程序简单的测试，对任何class，例如A,
<br />
调用new A().getClass().getClassLoader().toString()
打印出来就可以看到，把class文件放在不同的路径下再次执行，就会看到区别。特别注意的是如果打印出来是null就表示到了最高级 BootClassLoader,
因为它是C++编写的，不存在Java对应的类加载器的名字。<br />
<br />
寻找的顺序是一种向上迂回的思想，即如果本级别找不到，就只能去本级别之上的找，不会向下寻找。不过似乎从Jdk1.4到Jdk1.6这一特点又有改变，没有找到详细资料。所以就不举例子了。告诉大家设计这种体系的是Sun公司曾经的技术核心宫力先生，一个纯种华人哦！^_^<br />
<br />
这样希望大家不至于迷惑为什么总报错找不到类文件，不管是自己写的还是导入的第三方的jar文件（J2ee中经常需要导入的）。<br />
<br />
<strong>3．
关于jdk和jre</strong><br />
大家肯定在安装JDK的时候会有选择是否安装单独的jre，一般都会一起安装，我也建议大家这样做。因为这样更能帮助大家弄清楚它们的区别：<br />
<br />
Jre
是java runtime environment, 是java程序的运行环境。既然是运行，当然要包含jvm，也就是大家熟悉的虚拟机啦，
还有所有java类库的class文件，都在lib目录下打包成了jar。大家可以自己验证。至于在windows上的虚拟机是哪个文件呢？
学过MFC的都知道什么是dll文件吧，那么大家看看jre/bin/client里面是不是有一个jvm.dll呢？那就是虚拟机。<br />
<br />
Jdk 是java
development kit，是java的开发工具包，里面包含了各种类库和工具。当然也包括了另外一个Jre.
那么为什么要包括另外一个Jre呢？而且jdk/jre/bin同时有client和server两个文件夹下都包含一个jvm.dll。
说明是有两个虚拟机的。这一点不知道大家是否注意到了呢？<br />
<br />
相信大家都知道jdk的bin下有各种java程序需要用到的命令，与jre的bin目录最明显的区别就是jdk下才有javac，这一点很好理解，因为
jre只是一个运行环境而已。与开发无关，正因为如此，具备开发功能的jdk自己的jre下才会同时有client性质的jvm和server性质的 jvm，
而仅仅作为运行环境的jre下只需要client性质的jvm.dll就够了。<br />
<br />
记得在环境变量path中设置jdk/bin路径麽？这应该是大家学习Java的第一步吧，
老师会告诉大家不设置的话javac和java是用不了的。确实jdk/bin目录下包含了所有的命令。可是有没有人想过我们用的java命令并不是
jdk/bin目录下的而是jre/bin目录下的呢？不信可以做一个实验，大家可以把jdk/bin目录下的java.exe剪切到别的地方再运行
java程序，发现了什么？一切OK！<br />
<br />
那么有人会问了？我明明没有设置jre/bin目录到环境变量中啊？<br />
<br />
试想一下如果java为了提供给大多数人使用，他们是不需要jdk做开发的，只需要jre能让java程序跑起来就可以了，那么每个客户还需要手动去设置环境变量多麻烦啊？所以安装jre的时候安装程序自动帮你把jre的java.exe添加到了系统变量中，验证的方法很简单，大家看到了系统环境变量的
path最前面有&#8220;%SystemRoot%\system32;%SystemRoot%;&#8221;这样的配置，那么再去Windows/system32下面去看看吧，发现了什么？有一个java.exe。<br />
<br />
如果强行能够把jdk/bin挪到system32变量前面，当然也可以迫使使用jdk/jre里面的java，不过除非有必要，我不建议大家这么做。使用单独的jre跑java程序也算是客户环境下的一种测试。<br />
<br />
这下大家应该更清楚jdk和jre内部的一些联系和区别了吧？<br />
<img src ="http://www.blogjava.net/orangelizq/aggbug/292844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-08-27 16:52 <a href="http://www.blogjava.net/orangelizq/archive/2009/08/27/292844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]共享内存在java中的实现 </title><link>http://www.blogjava.net/orangelizq/archive/2009/05/13/270436.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 13 May 2009 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/05/13/270436.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/270436.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/05/13/270436.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/270436.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/270436.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 共享内存在java中的实现 &nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2009/05/13/270436.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/270436.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-05-13 16:08 <a href="http://www.blogjava.net/orangelizq/archive/2009/05/13/270436.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Find a way out of the ClassLoader maze</title><link>http://www.blogjava.net/orangelizq/archive/2009/04/26/267584.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 26 Apr 2009 08:58:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/04/26/267584.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/267584.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/04/26/267584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/267584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/267584.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Find a way out of the ClassLoader maze&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2009/04/26/267584.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/267584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-04-26 16:58 <a href="http://www.blogjava.net/orangelizq/archive/2009/04/26/267584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]走出ClassLoader迷宫</title><link>http://www.blogjava.net/orangelizq/archive/2009/03/25/261958.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 25 Mar 2009 14:52:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/03/25/261958.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/261958.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/03/25/261958.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/261958.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/261958.html</trackback:ping><description><![CDATA[&nbsp;
<p class="MsoNormal"><span lang="EN-US">Find a way out of the ClassLoader maze</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（走出</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">迷宫</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对于类加载器，普通</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用开发人员不需要了解太多。但对于系统开发人员，正确理解</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的类加载器模型是开发</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">系统软件的关键。很久以来，我一直对</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">许多问题感到很模糊，自己也在一直探讨</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的机制，但苦于</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这方面的文档太少，许多东西都是自己学习</span><span lang="EN-US">JDK</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">源码和看开源系统应用项目的代码总结出来，很不清晰。这篇文章在此方面做了一些深入的讲解。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><strong style="mso-bidi-font-weight: normal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">问题：何时使用</span><span lang="EN-US">Thread.getContextClassLoader()?<o:p></o:p></span></strong></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这是一个很常见的问题，但答案却很难回答。这个问题通常在需要动态加载类和资源的系统编程时会遇到。总的说来动态加载资源时，往往需要从三种类加载器里选择：系统或说程序的类加载器、当前类加载器、以及当前线程的上下文类加载器。在程序中应该使用何种类加载器呢？</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">系统类加载器通常不会使用。此类加载器处理启动应用程序时</span><span lang="EN-US">classpath</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指定的类，可以通过</span><span lang="EN-US">ClassLoader.getSystemClassLoader()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来获得。所有的</span><span lang="EN-US">ClassLoader.getSystemXXX()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口也是通过这个类加载器加载的。一般不要显式调用这些方法，应该让其他类加载器代理到系统类加载器上。由于系统类加载器是</span><span lang="EN-US">JVM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最后创建的类加载器，这样代码只会适应于简单命令行启动的程序。一旦代码移植到</span><span lang="EN-US">EJB</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span><span lang="EN-US">Web</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用或者</span><span lang="EN-US">Java Web Start</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用程序中，程序肯定不能正确执行。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">因此一般只有两种选择，当前类加载器和线程上下文类加载器。当前类加载器是指当前方法所在类的加载器。这个类加载器是运行时类解析使用的加载器，</span><span lang="EN-US">Class.forName(String)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span><span lang="EN-US">Class.getResource(String)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也使用该类加载器。代码中</span><span lang="EN-US">X.class</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的写法使用的类加载器也是这个类加载器。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">线程上下文类加载器在</span><span lang="EN-US">Java 2(J2SE)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时引入。每个线程都有一个关联的上下文类加载器。如果你使用</span><span lang="EN-US">new Thread()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方式生成新的线程，新线程将继承其父线程的上下文类加载器。如果程序对线程上下文类加载器没有任何改动的话，程序中所有的线程将都使用<span style="color: blue">系统类加载器</span>作为上下文类加载器。</span><span lang="EN-US">Web</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用和</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">企业级应用中，应用服务器经常要使用复杂的类加载器结构来实现</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">命名和目录接口</span><span lang="EN-US">)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、线程池、组件热部署等功能，因此理解这一点尤其重要。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为什么要引入线程的上下文类加载器？将它引入</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">并不是纯粹的噱头，由于</span><span lang="EN-US">Sun</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">没有提供充分的文档解释说明这一点，这使许多开发者很糊涂。实际上，上下文类加载器为同样在</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中引入的类加载代理机制提供了后门。通常</span><span lang="EN-US">JVM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中的类加载器是按照层次结构组织的，目的是每个类加载器（除了启动整个</span><span lang="EN-US">JVM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的原初类加载器）都有一个父类加载器。当类加载请求到来时，类加载器通常首先将请求代理给父类加载器。只有当父类加载器失败后，它才试图按照自己的算法查找并定义当前类。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">有时这种模式并不能总是奏效。这通常发生在</span><span lang="EN-US">JVM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">核心代码必须动态加载由应用程序动态提供的资源时。拿</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为例，它的核心是由</span><span lang="EN-US">JRE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">核心类</span><span lang="EN-US">(rt.jar)</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现的。但这些核心</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类必须能加载由第三方厂商提供的</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现。这种情况下调用父类加载器（原初类加载器）来加载只有其子类加载器可见的类，这种代理机制就会失效。解决办法就是让核心</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类使用线程上下文类加载器，从而有效的打通类加载器层次结构，逆着代理机制的方向使用类加载器。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">顺便提一下，</span><span lang="EN-US" style="color: blue">XML</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">解析</span><span lang="EN-US" style="color: blue">API(JAXP)</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也是使用此种机制。当</span><span lang="EN-US" style="color: blue">JAXP</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">还是</span><span lang="EN-US" style="color: blue">J2SE</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">扩展时，</span><span lang="EN-US" style="color: blue">XML</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">解析器使用当前累加载器方法来加载解析器实现。但当</span><span lang="EN-US" style="color: blue">JAXP</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">成为</span><span lang="EN-US" style="color: blue">J2SE</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">核心代码后，类加载机制就换成了使用线程上下文加载器，这和</span><span lang="EN-US" style="color: blue">JNDI</span><span style="color: blue; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的原因相似。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">好了，现在我们明白了问题的关键：这两种选择不可能适应所有情况。一些人认为线程上下文类加载器应成为新的标准。但这在不同</span><span lang="EN-US">JVM</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">线程共享数据来沟通时，就会使类加载器的结构乱七八糟。除非所有线程都使用同一个上下文类加载器。而且，使用当前类加载器已成为缺省规则，它们广泛应用在类声明、</span><span lang="EN-US">Class.forName</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">等情景中。即使你想尽可能只使用上下文类加载器，总是有这样那样的代码不是你所能控制的。这些代码都使用代理到当前类加载器的模式。混杂使用代理模式是很危险的。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">更为糟糕的是，某些应用服务器将当前类加载器和上下文类加器分别设置成不同的</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例。虽然它们拥有相同的类路径，但是它们之间并不存在父子代理关系。想想这为什么可怕：记住加载并定义某个类的类加载器是虚拟机内部标识该类的组成部分，如果当前类加载器加载类</span><span lang="EN-US">X</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">并接着执行它，如</span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">查找类型为</span><span lang="EN-US">Y</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的数据，上下文类加载器能够加载并定义</span><span lang="EN-US">Y</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，这个</span><span lang="EN-US">Y</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的定义和当前类加载器加载的相同名称的类就不是同一个，使用隐式类型转换就会造成异常。</span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-indent: 21pt; mso-char-indent-count: 2.0"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这种混乱的状况还将在</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中存在很长时间。在</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中还包括以下的功能使用不同的类加载器：</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">JNDI</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用线程上下文类加载器</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Class.getResource()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span><span lang="EN-US">Class.forName()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用当前类加载器</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">JAXP</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用上下文类加载器</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">java.util.ResourceBundle</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用调用者的当前类加载器</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">URL</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">协议处理器使用</span><span lang="EN-US">java.protocol.handler.pkgs</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">系统属性并只使用系统类加载器。</span></p>
<p class="MsoNormal" style="margin-left: 21pt; text-indent: -21pt; mso-list: l0 level1 lfo1; tab-stops: list 21.0pt"><span lang="EN-US" style="font-family: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">序列化</span><span lang="EN-US">API</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">缺省使用调用者当前的类加载器</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这些类加载器非常混乱，没有在</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文档中给以清晰明确的说明。</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal"><strong style="mso-bidi-font-weight: normal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">该如何选择类加载器？</span><span lang="EN-US"><o:p></o:p></span></strong></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如若代码是限于某些特定框架，这些框架有着特定加载规则，则不要做任何改动，让框架开发者来保证其工作（比如应用服务器提供商，尽管他们并不能总是做对）。如在</span><span lang="EN-US">Web</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用和</span><span lang="EN-US">EJB</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中，要使用</span><span lang="EN-US">Class.gerResource</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来加载资源。在其他情况下，需要考虑使用下面的代码，这是作者本人在工作中发现的经验：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">public abstract class ClassLoaderResolver</span></p>
<p class="MsoNormal"><span lang="EN-US">{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>/**</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* This method selects the best classloader instance to be used for</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* class/resource loading by whoever calls this method. The decision</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* typically involves choosing between the caller's current, thread context,</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* instance established by the last call to {@link #setStrategy}.</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* @return classloader to be used by the caller ['null' indicates the</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* primordial loader]<span style="mso-spacerun: yes">&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*/</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public static synchronized ClassLoader getClassLoader ()</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final Class caller = getCallerClass (0);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final ClassLoadContext ctx = new ClassLoadContext (caller);</span></p>
<p class="MsoNormal"><span lang="EN-US"></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return s_strategy.getClassLoader (ctx);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public static synchronized IClassLoadStrategy getStrategy ()</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return s_strategy;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final IClassLoadStrategy old = s_strategy;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s_strategy = strategy;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return old;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>/**</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* A helper class to get the call context. It subclasses SecurityManager</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* to make getClassContext() accessible. An instance of CallerResolver</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* only needs to be created, not installed as an actual security</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* manager.</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*/</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private static final class CallerResolver extends SecurityManager</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>protected Class [] getClassContext ()</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return super.getClassContext ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>} // End of nested class</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>/*</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* Indexes into the current method call context with a given</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* offset.</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*/</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private static Class getCallerClass (final int callerOffset)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return CALLER_RESOLVER.getClassContext () [CALL_CONTEXT_OFFSET +</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>callerOffset];</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private static IClassLoadStrategy s_strategy; // initialized in &lt;clinit&gt;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private static final CallerResolver CALLER_RESOLVER; // set in &lt;clinit&gt;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>static</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// This can fail if the current SecurityManager does not allow</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// RuntimePermission ("createSecurityManager"):</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp;</span>CALLER_RESOLVER = new CallerResolver ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>catch (SecurityException se)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>throw new RuntimeException ("ClassLoaderResolver: could not create CallerResolver: " + se);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s_strategy = new DefaultClassLoadStrategy ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US">} // End of class.</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可通过调用</span><span lang="EN-US">ClassLoaderResolver.getClassLoader()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法来获取类加载器对象，并使用其</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的接口来加载类和资源。此外还可使用下面的</span><span lang="EN-US">ResourceLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口来取代</span><span lang="EN-US">ClassLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">public abstract class ResourceLoader</span></p>
<p class="MsoNormal"><span lang="EN-US">{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>/**</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* @see java.lang.ClassLoader#loadClass(java.lang.String)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*/</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public static Class loadClass (final String name)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>throws ClassNotFoundException</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return Class.forName (name, false, loader);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>/**</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>* @see java.lang.ClassLoader#getResource(java.lang.String)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span>*/<span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public static URL getResource (final String name)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (loader != null)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return loader.getResource (name);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return ClassLoader.getSystemResource (name);</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>... more methods ...</span></p>
<p class="MsoNormal"><span lang="EN-US">} // End of class</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">决定应该使用何种类加载器的接口是</span><span lang="EN-US">IClassLoaderStrategy</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">public interface IClassLoadStrategy</span></p>
<p class="MsoNormal"><span lang="EN-US">{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>ClassLoader getClassLoader (ClassLoadContext ctx);</span></p>
<p class="MsoNormal"><span lang="EN-US">} // End of interface</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为了帮助</span><span lang="EN-US">IClassLoadStrategy</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做决定，给它传递了个</span><span lang="EN-US">ClassLoadContext</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象作为参数：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">public class ClassLoadContext</span></p>
<p class="MsoNormal"><span lang="EN-US">{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public final Class getCallerClass ()</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return m_caller;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>ClassLoadContext (final Class caller)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>m_caller = caller;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>private final Class m_caller;</span></p>
<p class="MsoNormal"><span lang="EN-US">} // End of class</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>ClassLoadContext.getCallerClass()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">返回的类在</span><span lang="EN-US">ClassLoaderResolver</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</span><span lang="EN-US">ResourceLoader</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用，这样做的目的是让其能找到调用类的类加载器（上下文加载器总是能通过</span><span lang="EN-US">Thread.currentThread().getContextClassLoader()</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来获得）。注意</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">调用类是静态获得的，因此这个接口不需现有业务方法增加额外的</span><span lang="EN-US">Class</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">参数，而且也适合于静态方法和类初始化代码。具体使用时，可以往这个上下文对象中添加具体部署环境中所需的其他属性。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上面代码看起来很像</span><span lang="EN-US">Strategy</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">设计模式，其思想是将&#8220;总是使用上下文类加载器&#8221;或者&#8220;总是使用当前类加载器&#8221;的决策同具体实现逻辑分离开。往往设计之初是很难预测何种类加载策略是合适的，该设计能够让你可以后来修改类加载策略。</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这儿有一个缺省实现，应该可以适应大部分工作场景：</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">public class DefaultClassLoadStrategy implements IClassLoadStrategy</span></p>
<p class="MsoNormal"><span lang="EN-US">{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>public ClassLoader getClassLoader (final ClassLoadContext ctx)</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;</span>final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ClassLoader result;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// If 'callerLoader' and 'contextLoader' are in a parent-child</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// relationship, always choose the child:</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;</span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (isChild (contextLoader, callerLoader))</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>result = callerLoader;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else if (isChild (callerLoader, contextLoader))</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>result = contextLoader;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// This else branch could be merged into the previous one,</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// but I show it here to emphasize the ambiguous case:</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>result = contextLoader;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// Precaution for when deployed as a bootstrap or extension class:</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (isChild (result, systemLoader))</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>result = systemLoader;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return result;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>... more methods ...</span></p>
<p class="MsoNormal"><span lang="EN-US">} // End of class</span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上面代码的逻辑很简单：如调用类的当前类加载器和上下文类加载器是父子关系，则总是选择子类加载器。对子类加载器可见的资源通常是对父类可见资源的超集，因此如果每个开发者都遵循</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的代理规则，这样做大多数情况下是合适的。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当前类加载器和上下文类加载器是兄弟关系时，决定使用哪一个是比较困难的。理想情况下，</span><span lang="EN-US">Java</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">运行时不应产生这种模糊。但一旦发生，上面代码选择上下文类加载器。这是作者本人的实际经验，绝大多数情况下应该能正常工作。你可以修改这部分代码来适应具体需要。一般来说，上下文类加载器要比当前类加载器更适合于框架编程，而当前类加载器则更适合于业务逻辑编程。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最后需要检查一下，以便保证所选类加载器不是系统类加载器的父亲，在开发标准扩展类库时这通常是个好习惯。</span></p>
<p class="MsoNormal"><span lang="EN-US"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">注意作者故意没有检查要加载资源或类的名称。</span><span lang="EN-US">Java XML API</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">成为</span><span lang="EN-US">J2SE</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">核心的历程应该能让我们清楚过滤类名并不是好想法。作者也没有试图检查哪个类加载器加载首先成功，而是检查类加载器的父子关系，这是更好更有保证的方法。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（全文完）<br />
<br />
</p>
</span>
<img src ="http://www.blogjava.net/orangelizq/aggbug/261958.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-03-25 22:52 <a href="http://www.blogjava.net/orangelizq/archive/2009/03/25/261958.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象初始化全过程</title><link>http://www.blogjava.net/orangelizq/archive/2008/12/25/248222.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 25 Dec 2008 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/12/25/248222.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/248222.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/12/25/248222.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/248222.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/248222.html</trackback:ping><description><![CDATA[<br />
/*&nbsp;&nbsp;&nbsp; <br />
&nbsp;*初始化全过程：&nbsp;&nbsp;&nbsp; <br />
&nbsp;*&nbsp;&nbsp;&nbsp; <br />
&nbsp;*1，&nbsp;&nbsp;&nbsp; 第一次创建MyClass类的对象，或者第一次访问MyClass的static方法或字段时，Java解释器会搜寻classpath，找到MyClass.class。&nbsp;&nbsp;&nbsp; <br />
&nbsp;*2，&nbsp;&nbsp;&nbsp; 装载MyClass.class后，会对所有的static数据进行初始化。这样第一个装载Class对象的时候，会先进行static成员的初始化。&nbsp;&nbsp;&nbsp; <br />
&nbsp;*3，&nbsp;&nbsp;&nbsp; 使用new MyClass()创建新对象的时候，MyClass对象的构建进程会先在堆里为对象分配足够的内存。 *&nbsp;&nbsp;&nbsp; <br />
&nbsp;*4，&nbsp;&nbsp;&nbsp; 清零这块新内存，把MyClass对象的primitive类型的成员赋上缺省值。&nbsp;&nbsp;&nbsp; <br />
&nbsp;*5，&nbsp;&nbsp;&nbsp; 执行定义成员数据时所作的初始化。&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;*6，&nbsp;&nbsp;&nbsp; 执行构造函数。&nbsp;&nbsp;&nbsp; <br />
&nbsp;*/&nbsp;&nbsp;&nbsp;&nbsp; <br />
import static net.mindview.util.Print.*;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
public class Beetle extends Insect&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; private int k = printInit("Beetle.k initialized");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; public Beetle()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("k = " + k);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("j = " + j);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; private static int x2 = printInit("static Beetle.x2 initialized");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("Beetle constructor");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Beetle b = new Beetle();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
class Insect&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; private int i = 9;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; protected int j;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; Insect()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("i = " + i + ", j = " + j);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j = 39;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; private static int x1 = printInit("static Insect.x1 initialized");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; static int printInit(String s)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(s);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 47;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
/* Output:&nbsp;&nbsp;&nbsp; <br />
static Insect.x1 initialized&nbsp;&nbsp;&nbsp; <br />
static Beetle.x2 initialized&nbsp;&nbsp;&nbsp; <br />
Beetle constructor&nbsp;&nbsp;&nbsp; <br />
i = 9, j = 0&nbsp;&nbsp;&nbsp; <br />
Beetle.k initialized&nbsp;&nbsp;&nbsp; <br />
k = 47&nbsp;&nbsp;&nbsp; <br />
j = 39&nbsp;&nbsp;&nbsp; <br />
*///:~&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
/****************************************************/&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
// 变量初始化先后顺序的示例&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
import static net.mindview.util.Print.*;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
//当创建Window的实例对象时会有消息提示&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
class Window&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; Window(int marker)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("Window(" + marker + ")");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
class House&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; Window w1 = new Window(1); // 构造函数前的变量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; House()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //构造函数里面的变量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("House()");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w3 = new Window(33); // 重新赋值w3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; Window w2 = new Window(2); // 构造函数后的变量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; void f()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("f()");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; Window w3 = new Window(3); // 结束类体时的对象&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
public class OrderOfInitialization&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; House h = new House();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h.f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
/*&nbsp;&nbsp;&nbsp; <br />
* 输出结果: Window(1) Window(2) Window(3) House() Window(33) f()&nbsp;&nbsp;&nbsp; <br />
*&nbsp;&nbsp;&nbsp; <br />
* 从结果看出，虽然域变量w2,w3排在构造函数后面，但它的输出却在构造函数前面&nbsp;&nbsp;&nbsp; <br />
*/&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
/****************************************************/&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
// 数组的初始化&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
import java.util.*;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
public class ArrayInit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //直接赋值方式，局限在于数组在大小编译确定&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Integer[] a = {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Integer(1),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Integer(2),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3, // 自动包装&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //new方式，适于参数数量未知的场合，或者参数类型未知的场合&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Integer[] b = new Integer[] {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Integer(1),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Integer(2),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3, // 自动包装&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Arrays.toString(a));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Arrays.toString(b));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
/* 输出结果:&nbsp;&nbsp;&nbsp; <br />
&nbsp;[1, 2, 3]&nbsp;&nbsp;&nbsp; <br />
&nbsp;[1, 2, 3]&nbsp;&nbsp;&nbsp; <br />
&nbsp;*///:~&nbsp;&nbsp;&nbsp; <br />
<img src ="http://www.blogjava.net/orangelizq/aggbug/248222.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-12-25 11:30 <a href="http://www.blogjava.net/orangelizq/archive/2008/12/25/248222.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java泛型编程指南</title><link>http://www.blogjava.net/orangelizq/archive/2008/09/16/229158.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Tue, 16 Sep 2008 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/09/16/229158.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/229158.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/09/16/229158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/229158.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/229158.html</trackback:ping><description><![CDATA[<p>Java泛型编程指南</p>
<p>此系列文章译自SUN的泛型编程指南, 看不懂译文的请看原文<br />
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf</p>
<p>一、绪言<br />
JDK1.5对JAVA语言进行了做了几个扩展，其中一个就是泛型。<br />
本指南旨在介绍泛型。如果你熟悉其它语言的构造类似的东西，特别是C++的模<br />
板（template），你会很快发现它们之间的相同点及重要的不同点；如果你在其他<br />
地方没看到过类似的东西，那反而更好，那样你就可以开始全新的学习，用不着去忘<br />
掉那些（对JAVA泛型）容易产生误解的东西。<br />
泛型允许你对类型进行抽象。最常见的例子是容器类型，比如那些在Collection<br />
层次下的类型。<br />
下面是那类例子的典型用法：</p>
<p>List myIntList = new LinkedList();//1<br />
myIntList.add(new Integer(0));//2<br />
Integer x = (Integer) myIntList.iterator().next();//3</p>
<p>第3行里的强制类型转换有点烦人，程序通常都知道一个特定的链表（list）里<br />
存放的是何种类型的数据，但却一定要进行类型转换。编译器只能保证迭代器返回的<br />
是一个对象，要保证对Integer类型变量的赋值是类型安全的话，必须进行类型转换。<br />
类型转换不但会引起程序的混乱，还可能会导致运行时错误，因为程序员可能会<br />
犯错误。<br />
如果程序员可以如实地表达他们的意图，即标记一个只能包含特定数据类型的链<br />
表，那会怎么样呢？这就是泛型背后的核心思想。下面是前面代码的泛型写法：<br />
List&lt;Integer&gt; myIntList = new LinkedList&lt;Integer&gt;();//1'<br />
myIntList.add(new Integer(0));//2'<br />
Integer x = myIntList.iterator().next();//3'</p>
<p>请注意变量myIntList的类型声明，它指明了这不仅仅是一个任意的List，还<br />
是一个Integer类型的List，写作List&lt;Integer&gt;。我们说List是一个接受类型（在<br />
这个例子是Integer）参数的泛华的接口，在创建链表对象的时候，我们也指定了一个<br />
类型参数。<br />
另外要注意的是在第3'行的类型转换已经不见了。<br />
现在你可能会想，我们所做的全部都是为了把混乱消除。我们没有在第3行把类<br />
型转换为Integer，而是在第1'行加了Integer类型参数；非也非也，这里面差别很<br />
大，编译器现在能够在编译期间检测程序的类型正确性。当我们把myIntList声明为<br />
类型List&lt;Integer&gt;的后，就意味着变量myIntList在何时何地的使用都是正确的，<br />
编译器保证了这一点。相反，类型转换只是告诉我们程序员认为它在程序的某个地方<br />
是正确的。<br />
实际的结果是，程序（特别是大型的程序）的可读性和健壮性得到了提高。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 二、定义简单的泛型</p>
<p>下面是java.util包里的List和Iterator接口定义的一个小小的引用：</p>
<p>public interface List&lt;E&gt;{<br />
void add(E x);<br />
Iterator&lt;E&gt; iterator();<br />
}<br />
public interface Iterator&lt;E&gt;{<br />
E next();<br />
boolean hasNext();<br />
}</p>
<p>除了尖括号里的东西，这里所有的都应该很熟悉了。那是List和Iterator接口<br />
的规范类型参数的声明。<br />
类型参数可以用在任何的泛型声明中，就像使用普通的类型一样（虽然有一些很<br />
重要的限制；看第7部分）。<br />
在绪言中，我们看到了List泛型声明的调用，比如List&lt;Integer&gt;。在调用里面<br />
（通常称为参数化类型），所有出现规范类型参数（这里是E）的全部都用实际的类型<br />
参数（这里是Integer）所代替。<br />
你可以想象成List&lt;Integer&gt;代表所有E都用Integer代替了的List：</p>
<p>public interface IntegerList{<br />
void add(Integer x)<br />
Iterator&lt;Integer&gt; iterator();<br />
}</p>
<p>这种想法是有所帮助的，但也会造成误解。<br />
它是有所帮助的，是因为参数化类型List&lt;integer&gt;有看起来像这种扩展的方法。<br />
它会造成误解，是因为泛型的声明实际上不会像那样去扩展；在源代码中、二进制<br />
文件中、硬盘和内在里，都没有代码的多个拷贝。如果你是一个C++程序员，你会明白<br />
这跟C++的模板（template）很不同。<br />
泛型声明是一次编译，永远使用，它会变成一个单独的class文件，就像一个普通<br />
的类或接口声明。<br />
类型参数跟用在方法或构造函数里的普通的参数类似，就像一个方法具有描述它运<br />
算用到的值的类型的规范值参一样，泛化声明具有规范类型参数。当一个方法被调用的<br />
时候，实际的参数将会被规范参数所代替而对方法求值。当一个泛化声明被调用的时候，<br />
实际类型参数将会代替规范类型参数。<br />
命名惯例要注意的一个地方。我们建议你用一些简炼（如果可以的话只用一个字<br />
符）但却映眼的名字作为规范类型参数名。在那些名字中最后避免小写字母，这样可<br />
以很容易把规范类型参数和普通的类或接口区分开来。就像前面的例子一样，很多容<br />
器类型使用E。我们将会在后面的例子里看到其他的惯例。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 三、泛型和子类化</p>
<p>http://xoj.blogone.net</p>
<p>我们来测试一下对泛型的理解，下面的代码是否正确呢？</p>
<p>List&lt;String&gt; ls = new ArrayList&lt;String&gt;();//1<br />
List&lt;Object&gt; lo = ls;//2</p>
<p>第1行肯定是正确的，问题的难点在于第2行；这样就归结为这个问题：一个字符<br />
串（String）链表（List）是不是一个对象链表？大部分人的直觉是：&#8220;肯定了！&#8221;<br />
那好，看一下下面这两行：</p>
<p>lo.add(new Object());//3<br />
String s = ls.get(0);//4:企图把一个对象赋值给字符串！</p>
<p>在这里我们把ls和lo搞混淆了。我们通过别名lo来访问字符串链表ls，插入不<br />
确定对象；结果就是ls不再存储字符串，当我们尝试从里面取出数据的时候就会出错。<br />
Java编译器当然不允许这样的事情发生了，所以第2行肯定会编译出错。<br />
一般来说，如果Foo是Bar的子类型（子类或子接口），而G又是某个泛型声明的<br />
话，G&lt;Foo&gt;并不是G&lt;Bar&gt;的子类型。这可能是学习泛型的时候最难的地方，因为它<br />
与我们的深层直觉相违背。<br />
直觉出错的问题在于它把集合里的东西假想为不会改变的，我们的本能把这些东<br />
西看作是不变的。<br />
举个例子，假设汽车公司为人口调查局提供一份驾驶员的列表，这看上去挺合理。<br />
假设Driver是Person的一个子类，则我们认为List&lt;Driver&gt;是一个List&lt;Person&gt;。<br />
而实际上提交的是一份驾驶员登记表的一个副本。否则的话，人口调查局将可以驾驶员<br />
的人加入到那份列表中去，汽车公司的纪录受到破坏。<br />
为了解决这类问题，我们需要考虑一些更灵活的泛型，到现在为止碰到的规则太<br />
受约束了。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 四、通配符</p>
<p>http://xoj.blogone.net</p>
<p>考虑一下写一个程序来打印一个集合对象（collection）里的所有元素。<br />
在旧版的语言里面，你可以会像下面那样写：</p>
<p>void printCollection(Collection c){<br />
Iterator i = c.iterator();<br />
for (k = 0; k &lt; c.size(); k++){<br />
System.out.println(i.next());<br />
}<br />
}</p>
<p>下面尝试着用泛型（和新的for循环语法）来写：</p>
<p>void printCollection(Collection&lt;Object&gt; c){<br />
for (Object e : c) {<br />
System.out.println(e);<br />
}<br />
}</p>
<p>这样的问题是新版本的代码还没旧版本的代码好用。就像我们刚示范的一样，<br />
Collection&lt;Object&gt;并不是所有类型的集合的父类型，所以它只能接受Collection&lt;Object&gt;<br />
对象，而旧版的代码却可以把任何类型的集合对象作为参数来调用。<br />
那么，什么才是所有集合类型的父类型呢？这个东西写作Collection&lt;?&gt;（读<br />
作&#8220;未知集合&#8221;），就是元素类型可以为任何类型的集合。这就是它为什么被称为&#8220;通<br />
配符类型&#8221;的原因。我们可以这样写：</p>
<p>void printCollection(Collection&lt;?&gt; c){<br />
for (Object e : c) {<br />
System.out.println(e);<br />
}<br />
}</p>
<p>现在，我们就可以以任何类型的集合对象作为参数来调用了。注意，在printCollection()<br />
方法里面，我们仍然可以从c对象中读取元素并赋予Object类型；因为无论集合里<br />
实际包含了什么类型，它肯定是对象，所以是类型安全的。但对它插入任意的对象<br />
的话则是不安全的：</p>
<p>Collection&lt;?&gt; c = new ArrayList&lt;String&gt;();<br />
c.add(new Object());//编译错误</p>
<p>由于我们并不知道c的元素类型是什么，因此我们不能对其插入对象。add()方法<br />
接受类型E，即集合的元素类型的参数。当实际的类型参数是?的时候，就代表是某未<br />
知类型。任何传递给add方法的参数，其类型必须是该未知类型的子类型。因为我们并<br />
不知道那是什么类型，所以我们传递不了任何参数。唯一的例外就是null，因为它是任<br />
何（对象）类型的成员。<br />
另外，假设有一个List&lt;?&gt;，我们可以调用get()方法并使用其返回结果。结果<br />
类型是一个未知类型，但我们都知道它是一个对象。因此把get()方法的返回结果赋<br />
值给对象类型，或者把它作为一个对象参数传递都是类型安全的。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp; 四、1-有界通配符</p>
<p>http://xoj.blogone.net</p>
<p>考虑一个简单的画图程序，它可以画长方形和圆等形状。为了表示这些形状，<br />
你可能会定义这样的一个类层次结构：</p>
<p>public abstract class Shape{<br />
public abstract void draw(Canvas c);<br />
}<br />
public class Circle extends Shape{<br />
private int x, y, radius;<br />
public void draw(Canvas c) { ... }<br />
public class Rectangle extends Shape {<br />
private int x, y, width, height;<br />
public void draw(Canvas c) { ... }<br />
}</p>
<p>这些类可以在canvas上描画：</p>
<p>public class Canvas {<br />
public void draw(Shape s) {<br />
s.draw(this);<br />
}<br />
}</p>
<p>任何的描画通常都包括有几种形状，假设它们用一个链表来表示，那么如果在<br />
Canvas里面有一个方法来画出所有的形状的话，那将会很方便：</p>
<p>public void drawAll(List&lt;Shape&gt; shapes) {<br />
for (Shape s: shapes) {<br />
s.draw(this);<br />
}<br />
}</p>
<p>但是现在，类型的规则说drawAll()方法只能对确切的Shape类型链表调用，<br />
比如，它不能对List&lt;Circle&gt;类型调用该方法。那真是不幸，因为这个方法所要<br />
做的就是从链表中读取形状对象，从而对List&lt;Circle&gt;类型对象进行调用。我们<br />
真正所想的是要让这个方法能够接受一个任何形状的类型链表：</p>
<p>public void drawAll(List&lt;? extends Shape&gt; shapes) { ... }</p>
<p>这里有一个很小但很重要的不同点：我们把类型List&lt;Shape&gt;替换为List&lt;? extends Shape&gt;。<br />
现在drawAll()方法可以接受任何Shape子类的链表，我们就可以如愿的对List&lt;Circle&gt;<br />
调用进行啦。<br />
List&lt;? extends Shape&gt;是一个有界通配符的例子。? 表示一个未知类型，<br />
就像我们之前所看到的通配符一样。但是，我们知道在这个例子里面这个未知类型<br />
实际是Shape的子类型（注：它可以是Shape本身，或者是它的子类，无须在字面上<br />
表明它是继承Shape类的）。我们说Shape是通配符的&#8220;上界&#8221;。<br />
如往常一样，使用通配符带来的灵活性得要付出一定的代价；代码就是现在在<br />
方法里面不能对Shape对象插入元素。例如，下面的写法是不允许的：</p>
<p>public void addRectangle(List&lt;? extends Shape&gt; shapes) {<br />
shapes.add(0, new Rectangle()); //编译错误<br />
}</p>
<p>你应该可以指出为什么上面的代码是不允许的。shapes.add()方法的第二个<br />
参数的类型是 ? 继承Shape，也就是一个未知的Shape的子类型。既然我们不知道<br />
类型是什么，那么我们就不知道它是否是Rectangle的父类型了；它可能是也可能<br />
不是一个父类型，因此在那里传递一个Rectangle的对象是不安全的。<br />
有界通配符正是需要用来处理汽车公司给人口调查局提交数据的例子方法。在<br />
我们的例子里面，我们假设数据表示为姓名（用字符串表示）对人（表示为引用类<br />
型，比如Person或它的子类型Driver等）的映射。Map&lt;K, V&gt;是有两个类型参数的<br />
一个泛型的例子，表示键值映射。<br />
请再一次注意规范类型参数的命名惯例：K表示键，V表示值。</p>
<p>public class Census {<br />
public static void<br />
addRegistry(Map&lt;String, ? extends Person&gt; registry){ ... }<br />
}<br />
...<br />
Map&lt;String, Driver&gt; allDrivers = ...;<br />
Census.addRegistry(allDrivers);<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;&nbsp; 五、泛型方法</p>
<p>http://xoj.blogone.net</p>
<p>考虑写这样一个方法，它接收一个数组和一个集合（collection）作为参数，<br />
并把数组里的所有对象放到集合里面。<br />
先试试这样：</p>
<p>static void fromArrayToCollection(Object[] a, Collection&lt;?&gt; c){<br />
for (Object o : a){<br />
c.add(o);//编译错误<br />
}<br />
}</p>
<p>到现在，你应该学会了避免把Collection&lt;Object&gt;作为集合参数的类型这种初学<br />
者的错误；你可能或可能没看出使用Collection&lt;?&gt;也是不行的，回想一下，你是不能<br />
把对象硬塞进一个未知类型的集合里面的。<br />
解决这类问题的方法是使用泛型方法。就像类型声明一样，方法也可以声明为泛型<br />
的，就是说，用一个或多个类型参数作为参数。</p>
<p>static &lt;T&gt; void fromArrayToCollection(T[]a, Collection&lt;T&gt; c){<br />
for (T o : a){<br />
c.add(o);//正确<br />
}<br />
}</p>
<p>对于集合元素的类型是数组类型的父类型，我们就可以调用这个方法。</p>
<p>Object[] oa = new Object[100];<br />
Collection&lt;Object&gt; co = new ArrayList&lt;Object&gt;();<br />
fromArrayToCollection(oa, co);// T是对象类型<br />
String[] sa = new String[100];<br />
Collection&lt;String&gt; cs = new ArrayList&lt;String&gt;();<br />
fromArrayToCollection(sa, cs);// T是字符串类型（String）<br />
fromArrayToCollection(sa, co);// T对象类型<br />
Integer[] ia = new Integer[100];<br />
Float[] fa = new Float[100];<br />
Number[] na = new Number[100];<br />
Collection&lt;Number&gt; cn = new ArrayList&lt;Number&gt;();<br />
fromArrayToCollection(ia, cn);// T是Number类型<br />
fromArrayToCollection(fa, cn);// T是Number类型<br />
fromArrayToCollection(na, cn);// T是Number类型<br />
fromArrayToCollection(na, co);// T是Number类型<br />
fromArrayToCollection(na, cs);// 编译错误</p>
<p>请注意，我们并没有把实际的类型实参传递给泛型方法，因为编译器会根据<br />
实参的类型为我们推断出类型实参。一般地，编译器推断得到可以正确调用的最<br />
接近的（the most specific）实参类型。<br />
现在有一个问题：我应该什么时候使用泛型方法，什么时候使用通配符类型<br />
呢？为了明白这个问题的答案，我们来看看Collection库里的几个方法：</p>
<p>interface Collection&lt;E&gt;{<br />
public boolean containsAll(Collection&lt;?&gt; c);<br />
public boolean addAll(Collection&lt;? extends E&gt; c);<br />
}</p>
<p>在这里我们也可以用泛型方法：</p>
<p>interface Collection&lt;E&gt;{<br />
public &lt;T&gt; boolean containsAll(Collection&lt;T&gt; c);<br />
public &lt;? extends E&gt;boolean addAll(Collection&lt;T&gt; c);<br />
//哈哈，类型变量也可以有界！<br />
}</p>
<p>但是，类型参数T在containsAll和addAll两个方法里面都只是用了一次。返<br />
回类型并不依赖于类型参数或其他传递给该方法的实参（这种是只有一个实参的简单<br />
情况）。这就告诉我们类型实参是用于多态的，它的作用只是对不同的调用可以有一<br />
系列的实际的实参类型。如果是那样的话，就应该使用通配符，通配符就是设计来支<br />
持灵活的子类型的，这也是我们这里所要表述的东西。<br />
泛型方法允许类型参数用于表述一个或多个的实参类型对方法或及其返回类型的<br />
依赖关系。如果没有那样的一个依赖关系的话，泛型方法就不应用使用。<br />
也有可能是一前一后一起使用泛型方法和通配符的情况，下面是Collections.copy()<br />
方法：</p>
<p>class Collections {<br />
public static &lt;T&gt; void copy(List&lt;T&gt; dest, list&lt; ? extends T&gt; src) {...}<br />
}</p>
<p>请注意这里两个参数类型的依赖关系，任何要从源链表src复制过来的对象都必<br />
须是对目标链表dst元素可赋值的；所以我们可以不管src的元素类型是什么，只要<br />
它是T类型的子类型。copy方法的方法头表示了使用一个类型参数，但是用通配符来<br />
作为第二个参数的元素类型的依赖关系。<br />
我们是可以用另外一种不用通配符来写这个方法头的办法。</p>
<p>class Collections {<br />
public static &lt;T, S extends T&gt;<br />
vod copy(List&lt;T&gt; dest, List&lt;S&gt; src) { ...}<br />
}</p>
<p>没问题，但是当第一个类型参数用作dst的类型和批二个类型参数S的上界的<br />
时候，S它本身在src类型里只能使用一次，没有其他的东西依赖于它。这就意味<br />
着我们可以用一个通配符来代替S了。使用通配符比声明显式的类型参数要来得清<br />
晰和简单，因此在可能的话都优先使用通配符。<br />
当通配符用于方法头外部，作为成员变量、局部变量和数组的类型的时候，同<br />
样也有优势。请看下面的例子。<br />
看回我们之前画图的那个问题，现在我们想要保留一份画图请求的历史记录。<br />
我们可以这样来维护这份历史记录，在Shape类里用一个静态的变量表示历史记录，<br />
然后在drawAll()方法里面把传递的实参储存到那历史记录变量里头。</p>
<p>static List&lt;List&lt;? extends Shape&gt;&gt; history = <br />
new ArrayList&lt;List&lt;? extends Shape&gt;&gt;();<br />
public void drawAll(List&lt;? extends Shape&gt; shapes){<br />
history.addLast(shapes);<br />
for (Shape s: shapes) {<br />
s.draw(this);<br />
}<br />
}</p>
<p>最后，我们再次留意一下使用类型参数的命名惯例。当没有更精确的类型来<br />
区分的时候，我们用T来表示类型，这是通常是在泛型方法里面的情况。如果有多<br />
个类型参数，我们可以用在字母表中与T相邻的字母来表示，比如S。如果一个泛<br />
型方法出现在一个泛型类里面，一个好的方法就是，应该避免对方法和类使用相<br />
同的类型参数以免发生混淆。这在嵌套泛型类里也一样。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 六、与遗留代码的交互</p>
<p><br />
到现在为止，我们所有的例子都是在一个假想的理想世界里面的，就是所有的<br />
人都在使用Java语言支持泛型的最新版本。<br />
唉，不过在现实中情况却不是那样。千百万行的代码都是用早期版本的语言<br />
来编写的，不可能把它们全部在一夜之间就转换过来。<br />
在后面的第10部分，我们将会解决把遗留代码转为用泛型这个问题。在这部分<br />
我们要看的是比较简单的问题：遗留代码与泛型代码如何交互？这个问题分为两个<br />
部分：在泛型代码中使用遗留代码和在遗留代码中使用泛型代码。<br />
免费linux公开课,，现在报名!<br />
&nbsp;</p>
<p><br />
&nbsp;&nbsp; 六-1 在泛型代码中使用遗留代码</p>
<p>[url=http://xoj.blogone.net][url]<br />
当你在享受在代码中使用泛型带来的好处的时候，你怎么样使用遗留代码呢？<br />
假设这样一个例子，你要使用com.Foodlibar.widgets这个包。Fooblibar.com<br />
的人要销售一个库存控制系统，主要部分如下：</p>
<p>package com.Fooblibar.widgets;<br />
public interface Part { ... }<br />
public class Inventory {<br />
/**<br />
*Adds a new Assembly to the inventory databse.<br />
*The assembly is given the name name, and consists of a set<br />
*parts specified by parts. All elements of the collection parts<br />
*must support the Part interface.<br />
**/<br />
public static void addAssembly(String name, Collection parts) {...}<br />
public static Assembly getAssembly(String name) {...}<br />
}<br />
public interface Assembly{<br />
Collection getParts();//Returns a collection of Parts<br />
}</p>
<p>现在，你可以用上面的API来增加新的代码，它可以很好的保证你调用参数恰当<br />
的addAssembly()方法，就是说传递的集合是一个Part类型的Collection对象，当<br />
然，泛型是最适合做这个：</p>
<p>package com.mycompany.inventory;<br />
import com.Fooblibar.widgets.*;<br />
public class Blade implements Part{<br />
...<br />
}<br />
public class Guillotine implements Part {<br />
}<br />
public class Main {<br />
public static void main(Sring[] args) {<br />
Collection&lt;Part&gt; c = new ArrayList&lt;Part&gt;();<br />
c.add(new Guillotine());<br />
c.add(new Blade());<br />
Inventory.addAssembly("thingee", c);<br />
Collection&lt;Part&gt; k = Inventory.getAssembly("thingee").getParts();<br />
}<br />
}</p>
<p>当我们调用addAssembly方法的时候，它想要的第二个参数是Collection类型的，<br />
实参是Collection&lt;Part&gt;类型，但却可以，为什么呢？毕竟，大多数集合存储的都不是<br />
Part对象，所以总的来说，编译器不会知道Collection存储的是什么类型的集合。<br />
在正规的泛型代码里面，Collection都带有类型参数。当一个像Collection这样<br />
的泛型不带类型参数使用的时候，称之为原生类型。<br />
很多人的第一直觉是Collection就是指Collection&lt;Object&gt;，但从我们先前所<br />
看到的可以知道，当需要的对象是Collection&lt;Object&gt;，而传递的却是Collection&lt;Part&gt;<br />
对象的时候，是类型不安全的。确切点的说法是Collection类型表示一个未知类型的<br />
集合，就像Collection&lt;?&gt;。<br />
稍等一下，那样做也是不正确的！考虑一下调用getParts()方法，它返回一个<br />
Collection对象，然后赋值给k，而k是Collection&lt;Part&gt;类型的；如果调用的结果<br />
是返回一个Collection&lt;?&gt;的对象，这个赋值可能是错误的。<br />
事实上，这个赋值是允许的，只是它会产生一个未检测警告。警告是需要的，因为<br />
编译器不能保证赋值的正确性。我们没有办法通过检测遗留代码中的getAssembly()方法<br />
来保证返回的集合的确是一个类型参数是Part的集合。程序里面的类型是Collection，<br />
我们可以合法的对此集合插入任何对象。<br />
所以，这不应该是错误的吗？理论上来说，答案是：是；但实际上如果是泛型代码<br />
调用遗留代码的话，这又是允许的。对这个赋值是否可接受，得取决于程序员自己，在<br />
这个例子中赋值是安全的，因为getAssembly()方法约定是返回以Part作为类型参数的<br />
集合，尽管在类型标记中没有表明。<br />
所以原生类型很像通配符类型，但它们没有那么严格的类型检测。这是有意设计成<br />
这样的，从而可以允许泛型代码可以与之前已有的遗留代码交互。<br />
在泛型代码中调用遗留代码固然是危险的，一旦把泛型代码和非泛型代码混合在一<br />
起，泛型系统所提供的全部安全保证就都变得无效了。但这仍比根本不使用泛型要好，<br />
最起码你知道你的代码是一致的。<br />
泛型代码出现的今天，仍然有很多非泛型代码，二者混合同时使用是不可避免的。<br />
如果一定要把遗留代码与泛型代码混合使用，请小心留意那些未检测警告。仔细的<br />
想想如何才能判定引发警告的代码是安全的。<br />
如果仍然出错，代码引发的警告实际不是类型安全的，那又怎么样呢？我们会看<br />
那样的情况，接下来，我们将会部分的观察编译器的工作方式。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp; 六-2 擦除和翻译</p>
<p><br />
public String loophole(Integer x){<br />
List&lt;String&gt; ys = new LinkedList&lt;String&gt;();<br />
List xs = ys;<br />
xs.add(x);//编译时未检测警告<br />
return ys.iterator().next();<br />
}</p>
<p>在这里我们定义了一个字符串类型的链表和一个一般的老式链表，我们先插入<br />
一个Integer对象，然后试图取出一个String对象，很明显这是错误的。如果我们<br />
忽略警告继续执行代码的话，程序将会在我们使用错误类型的地方出错。在运行时，<br />
代码执行大致如下：</p>
<p>public String loophole(Integer x) {<br />
List ys = new LinkedList;<br />
List xs = ys;<br />
xs.add(x);<br />
return (String)ys.iterator().next();//运行时出错<br />
}</p>
<p>当我们要从链表中取出一个元素，并把它当作是一个字符串对象而把它转换为<br />
String类型的时候，我们将会得到一个ClassCastException类型转换异常。在<br />
泛型版本的loophole()方法里面发生的就是这种情况。<br />
出现这种情况的原因是，Java的泛型是通过一个前台转换&#8220;擦除&#8221;的编译器实现<br />
的，你基本上可以认为它是一个源码对源码的翻译，这就是为何泛型版的loophole()<br />
方法转变为非泛型版本的原因。<br />
结果是，Java虚拟机的类型安全性和完整性永远不会有问题，就算出现未检测<br />
的警告。<br />
基本上，擦除会除去所有的泛型信息。尖括号里面的所有类型信息都会去掉，比<br />
如，参数化类型的List&lt;String&gt;会转换为List。类型变量在之后使用时会被类型<br />
变量的上界（通常是Object）所替换。当最后代码不是类型正确的时候，就会加入<br />
一个适当的类型转换，就像loophole()方法的最后一行。<br />
对&#8220;擦除&#8221;的完整描述不是本指南的范围内的内容，但前面我们所给的简单描述<br />
也差不多是那样了。了解这点很有好处，特别是当你想做诸如把现有API转为使用<br />
泛型（请看第10部分）这样复杂的东西，或者是想知道为什么它们会那样的时候。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; 六-3 在遗留代码中使用泛型</p>
<p>&nbsp;</p>
<p>现在我们来看看相反的情况。假设Fooblibar.com把他们的API转换为泛型的，<br />
但有些客户还没有转换。代码就会像下面的：</p>
<p>package com.Fooblibar.widgets;<br />
public interface Part { ... }<br />
publlic class Inventory {<br />
/**<br />
*Adds a new Assembly to the inventory database.<br />
*The assembly is given the name name, and consists of a set<br />
*parts specified by parts. All elements of the collection parts<br />
*must support the Part interface.<br />
**/<br />
public static void addAssembly(String name, Collection&lt;Part&gt; parts) {...}<br />
public static Assembly getAssembly(String name){ ... }<br />
}<br />
public interface Assembly {<br />
Collection&lt;Part&gt; getParts();//Return a collection of Parts<br />
}</p>
<p>客户代码如下：</p>
<p>package com.mycompany.inventory;<br />
import com.Fooblibar.widgets.*;<br />
public class Blade implements Part {<br />
...<br />
}<br />
public class Guillotine implements Part {<br />
...<br />
}<br />
public class Main {<br />
public static void main(String[] args){<br />
Collection c = new ArrayList();<br />
c.add(new Guillotine());<br />
c.add(new Blade());<br />
Inventory.addAssembly("thingee", c);//1: unchecked warning<br />
Collection k = Inventory.getAssembly("thingee").getParts();<br />
}<br />
}</p>
<p>客户代码是在引进泛型之前写下的，但是它使用了com.Fooblibar.widgets包和集<br />
合库，两个现在都是在用泛型的。在客户代码里面使用的泛型全部都是原生类型。<br />
第1行产生一个未检测警告，因为把一个原生Collection传递给了一个需要Part类型的<br />
Collection的地方，编译器不能保证原生的Collection是一个Part类型的Collection。<br />
不这样做的话，你也可以在编译客户代码的时候使用source 1.4这个标记来保证不<br />
会产生警告。但是这样的话你就不能使用所有JDK 1.5引入的新的语言特性。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><br />
&nbsp;&nbsp; 七、晦涩难懂的部分</p>
<p>七-1 泛型类为所有调用所共享<br />
下面的代码段会打印出什么呢？</p>
<p>List&lt;String&gt; l1 = new ArrayList&lt;String&gt;();<br />
List&lt;Integer&gt; l2 = new ArrayList&lt;Integer&gt;();<br />
System.out.println(l1.getClass() == l2.getClass());</p>
<p>你可能会说是false，但是你错了，打印的是true，因为所有泛型类的实例它们<br />
的运行时的类（run-time class）都是一样的，不管它们实际类型参数如何。<br />
泛型类之所以为泛型的，是因为它对所有可能的类型参数都有相同的行为，相同<br />
的类可以看作是有很多不同的类型。<br />
结果就是，一个类的静态的变量和方法也共享于所有的实例中，这就是为什么不<br />
允许在静态方法或初始化部分、或者在静态变量的声明或初始化中引用类型参数。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;</p>
<p>&nbsp;&nbsp; 七-2 强制类型转换和instanceof</p>
<p>泛型类在它所有的实例****享，就意味着判断一个实例是否是一个特别调用的泛<br />
型的实例是毫无意义的：</p>
<p>Collection cs = new ArrayList&lt;String&gt;();<br />
if (cs instanceof Collection&lt;String&gt;) {...}//非法</p>
<p>类似地，像这样的强制类型转换：</p>
<p>Collection&lt;String&gt; cstr = (Collection&lt;String&gt;) cs;//未检测警告</p>
<p>给出了一个未检测的警告，因为这里系统在运行时并不会检测。<br />
对于类型变量也一样：</p>
<p>&lt;T&gt; T BadCast(T t, Object o) {<br />
return (T) o;//未检测警告<br />
}</p>
<p>类型变量不存在于运行时，这就是说它们对时间或空间的性能不会造成影响。<br />
但也因此而不能通过强制类型转换可靠地使用它们了。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; 七-3 数组</p>
<p>数组对象的组件类型可能不是一个类型变量或一个参数化类型，除非它是一个<br />
（无界的）通配符类型。你可以声明元素类型是类型变量和参数华类型的数组类型，<br />
但元素类型不能是数组对象。<br />
这自然有点郁闷，但这个限制对避免下面的情况是必要的：</p>
<p>List&lt;Strign&gt;[] lsa = new List&lt;String&gt;[10];//实际上是不允许的<br />
Object o = lsa;<br />
Object[] oa = (Object[]) o;<br />
List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />
li.add(new Integer(8));<br />
oa[1] = li;//不合理，但可以通过运行时的赋值检测<br />
String s = lsa[1].get(0);//运行时出错：ClassCastException异常</p>
<p>如果参数化类型的数组允许的话，那么上面的例子编译时就不会有未检测的警告，<br />
但在运行时出错。对于泛型编程，我们的主要设计目标是类型安全，而特别的是这个<br />
语言的设计保证了如果使用了javac -source 1.5来编译整个程序而没有未检测的<br />
警告的话，它是类型安全的。<br />
但是你仍然会使用通配符数组，这与上面的代码相比有两个变化。首先是不使用<br />
数组对象或元素类型被参数化的数组类型，这样我们就需要在从数组中取出一个字符<br />
串的时候进行强制类型转换：</p>
<p>List&lt;?&gt;[] lsa = new List&lt;?&gt;[10];//没问题，无界通配符类型数组<br />
Object o = lsa;<br />
Object[] oa = (Object[]) o;<br />
List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />
li.add(new Integer(3));<br />
oa[1] = li;//正确<br />
String s = (String) lsa[1].get(0);//运行时错误，显式强制类型转换</p>
<p>第二个变化是，我们不创建元素类型被参数化的数组对象，但仍然使用参数化元素<br />
类型的数组类型，这是允许的，但引起现未检测警告。这样的程序实际上是不安全的，<br />
甚至最终会出错。</p>
<p>List&lt;String&gt;[] lsa = new List&lt;?&gt;[10];//未检测警告-这是不安全的！<br />
Object o = lsa;<br />
Object[] oa = (Object[]) o;<br />
List&lt;Integer&gt; li = new ArrayList&lt;integer&gt;();<br />
li.add(new Integer(3));<br />
oa[1]=li;//正确<br />
String s = lsa[1].get(0);//运行出错，但之前已经被警告</p>
<p>类似地，想创建一个元素类型是类型变量的数组对象的话，将会编译出错。</p>
<p>&lt;T&gt; T[] makeArray(T t){<br />
return new T[100];//错误<br />
}</p>
<p>因为类型变量并不存在于运行时，所以没有办法知道实际的数组类型是什么。<br />
要突破这类限制，我们可以用第8部分说到的用类名作为运行时标记的方法。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; 八、 把类名作为运行时的类型标记</p>
<p>JDK1.5中的一个变化是java.lang.Class是泛化的，一个有趣的例子是对<br />
容器外的东西使用泛型。<br />
现在Class类有一个类型参数T，你可能会问，T代表什么啊？它就代表Class<br />
对象所表示的类型。<br />
比如，String.class的类型是Class&lt;String&gt;，Serializable.class的<br />
类型是Class&lt;Serializable&gt;，这可以提高你的反射代码中的类型安全性。<br />
特别地，由于现在Class类中的newInstance()方法返回一个T对象，因此<br />
在通过反射创建对象的时候可以得到更精确的类型。<br />
其中一个方法就是显式传入一个factory对象，代码如下：</p>
<p>interface Factory&lt;T&gt; {T make();}<br />
public &lt;T&gt; Collection&lt;T&gt; select(Factory&lt;T&gt; factory, String statement){<br />
Collection&lt;T&gt; result = new ArrayList&lt;T&gt;();<br />
//用JDBC运行SQL查询<br />
for(/*遍历JDBC结果*/){<br />
T item = factory.make();<br />
/*通过SQL结果用反射和设置数据项*/<br />
result.add(item);<br />
}<br />
return result;<br />
}</p>
<p>你可以这样调用：</p>
<p>select(new Factory&lt;EmpInfo&gt;(){ public EmpInfo make() {<br />
return new EmpInfo();<br />
}}<br />
, "selection string");</p>
<p>或者声明一个EmpInfoFactory类来支持Factory接口：</p>
<p>class EmpInfoFactory implements Factory&lt;EmpInfo&gt;{<br />
...<br />
public EmpInfo make() { return new EmpInfo();}<br />
}</p>
<p>然后这样调用：</p>
<p>select(getMyEmpInfoFactory(), "selection string");</p>
<p>这种解决办法需要下面的其中之一：<br />
&#183; 在调用的地方使用详细的匿名工厂类(verbose anonymous factory classes)，或者<br />
&#183; 为每个使用的类型声明一个工厂类，并把工厂实例传递给调用的地方，这样有点不自然。</p>
<p>使用类名作为一个工厂对象是非常自然的事，这样的话还可以为反射所用。现在<br />
没有泛型的代码可能写作如下：</p>
<p>Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps");<br />
...<br />
public static Collection select(Class c, String sqlStatement) {<br />
Collection result = new ArrayList();<br />
/*用JDBC执行SQL查询*/<br />
for(/*遍历JDBC产生的结果*/){<br />
Object item = c.newInstance();<br />
/*通过SQL结果用反射和设置数据项*/<br />
result.add(item);<br />
}<br />
return result;<br />
}</p>
<p>但是，这样并不能得到我们所希望的更精确的集合类型，现在Class是泛化的，<br />
我们可以这样写：</p>
<p>Collection&lt;EmpInfo&gt; emps = <br />
sqlUtility.select(EmpInfo.class, "select * from emps");<br />
...<br />
public static &lt;T&gt; Collection&lt;T&gt; select(Class&lt;T&gt; c, String sqlStatement) {<br />
Collection&lt;T&gt; result = new ArrayList&lt;T&gt;();<br />
/*用JDBC执行SQL查询*/<br />
for(/*遍历JDBC产生的结果*/){<br />
T item = c.newInstance();<br />
/*通过SQL结果用反射和设置数据项*/<br />
result.add(item);<br />
}<br />
return result;<br />
}</p>
<p>这样就通过类型安全的方法来得到了精确的集合类型了。<br />
这种使用类名作为运行时类型标记的技术是一个很有用的技巧，是需要知道的。<br />
在处理注释的新的API中也有很多类似的情况。<br />
免费linux公开课,，现在报名!<br />
&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp; 九 通配符的其他作用</p>
<p>（more fun with wildcards，不知道如何译才比较妥当，呵呵。）</p>
<p>在这部分，我们将会仔细看看通配符的几个较为深入的用途。我们已经从几个<br />
有界通配符的例子中看到，它对从某一数据结构中读取数据是很有用的。现在来看<br />
看相反的情况，只对数据结构进行写操作。<br />
下面的Sink接口就是这类情况的一个简单的例子：</p>
<p>interface Sink&lt;T&gt; {<br />
flush(T t);<br />
}</p>
<p>我们可以想象在下面的示范的例子中使用它，writeAll()方法用于把coll集合<br />
里的所有元素填充(flush)到Sink接口变量snk中，并返回最后一个填充的元素。</p>
<p>public static &lt;T&gt; T writeAll(Collection&lt;T&gt; coll, Sink&lt;T&gt; snk){<br />
T last;<br />
for (T t: coll){<br />
last = t;<br />
snk.flush(last);<br />
}<br />
return last;<br />
}<br />
...<br />
Sink&lt;Object&gt; s;<br />
Collection&lt;String&gt; cs;<br />
String str = writeAll(cs, s);//非法调用</p>
<p>如注释所注，这里对writeAll()方法的调用是非法的，因为无有效的类型参数<br />
可以引用；String和Object都不适合作为T的类型，因为Collection和Sink的元素<br />
必须是相同类型的。<br />
我们可以通过使用通配符来改写writeAll()的方法头来处理，如下：</p>
<p>public static &lt;T&gt; T writeAll(Collection&lt;? extends T&gt;, Sink&lt;T&gt;) {...}<br />
...<br />
String str = writeAll(cs, s);//调用没问题，但返回类型错误</p>
<p>现在调用是合法的了，但由于T的类型跟元素类型是Object的s一样，因为返回的<br />
类型也是Object，因此赋值是不正确的。<br />
解决办法是使用我们之前从未见过的一种有界通配符形式：带下界的通配符。<br />
语法 ? super T 表示了是未知的T的父类型，这与我们之前所使用的有界<br />
（父类型：或者T类型本身，要记住的是，你类型关系是自反的）<br />
通配符是对偶有界通配符，即用 ? extends T 表示未知的T的子类型。</p>
<p>public static&lt;T&gt; T writeAll(Collection&lt;T&gt; coll, Sink&lt;? super T&gt; snk) {...}<br />
...<br />
String str = writeAll(cs, s);//正确！</p>
<p>使用这个语法的调用是合法的，指向的类型是所期望的String类型。</p>
<p>现在我们来看一个比较现实一点的例子，java.util.TreeSet&lt;E&gt;表示元素类型<br />
是E的树形数据结构里的元素是有序的，创建一个TreeSet对象的一个方法是使用参数<br />
是Comparator对象的构造函数，Comparator对象用于对TreeSet对象里的元素进行<br />
所期望的排序进行分类。</p>
<p>TreeSet(Comparator&lt;E&gt; c)</p>
<p>Comparator接口是必要的：</p>
<p>interface Comparator&lt;T&gt; {<br />
int compare(T fst, T snd);<br />
}</p>
<p>假设我们想要创建一个TreeSet&lt;String&gt;对象，并传入一下合适的Comparator<br />
对象，我们传递的Comparator是能够比较字符串的。我们可以用Comparator&lt;String&gt;，<br />
但Comparator&lt;Object&gt;也是可以的。但是，我们不能对Comparator&lt;Object&gt;对象<br />
调用上面所给的构造函数，我们可以用一个下界通配符来得到我们想要的灵活性：</p>
<p>TreeSet(Comparator&lt;? super E&gt; c)</p>
<p>这样就可以使用适合的Comparator对象啦。<br />
最后一个下界通配符的例子，我们来看看Collections.max()方法，这个方法<br />
返回作为参数传递的Collection对象中最大的元素。<br />
现在，为了max()方法能正常运行，传递的Collection对象中的所有元素都必<br />
须是实现了Comparable接口的，还有就是，它们之间必须是可比较的。<br />
先试一下泛化方法头的写法：</p>
<p>public static &lt;T extends Comparable&lt;T&gt;&gt;<br />
T max(Collection&lt;T&gt; coll)</p>
<p>那样，方法就接受一个自身可比较的（comparable）某个T类型的Collection<br />
对象，并返回T类型的一个元素。这样显得太束缚了。<br />
来看看为什么，假设一个类型可以与合意的对象进行比较：</p>
<p>class Foo implements Comparable&lt;Object&gt; {...}<br />
...<br />
Collection&lt;Foo&gt; cf = ...;<br />
Collectins.max(cf);//应该可以正常运行</p>
<p>cf里的每个对象都可以和cf里的任意其他元素进行比较，因为每个元素都是Foo<br />
的对象，而Foo对象可以与任意的对象进行比较，特别是同是Foo对象的。但是，使用<br />
上面的方法头，我们会发现这样的调用是不被接受的，指向的类型必须是Foo，但Foo<br />
并没有实现Comparable&lt;Foo&gt;。<br />
T对于自身的可比性不是必须的，需要的是T与其父类型是可比的，就像下面：<br />
（实际的Collections.max()方法头在后面的第10部分将会讲得更多）</p>
<p>public static &lt;T extends Comparable&lt;? super T&gt;&gt;<br />
T max(Collection&lt;T&gt; coll)</p>
<p>这样推理出来的结果基本上适用于想用Comparable来用于任意类型的用法：<br />
就是你想这样用Comparable&lt;? super T&gt;。<br />
总的来说，如果你有一个只能一个T类型参数作为实参的API的话，你就应该用<br />
下界</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/229158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-09-16 14:34 <a href="http://www.blogjava.net/orangelizq/archive/2008/09/16/229158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中多种计时器的比较与分析</title><link>http://www.blogjava.net/orangelizq/archive/2008/09/05/227175.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 05 Sep 2008 03:46:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/09/05/227175.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/227175.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/09/05/227175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/227175.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/227175.html</trackback:ping><description><![CDATA[<span class="postbody"><span style="font-size: 15px; line-height: normal"><strong>介绍</strong></span> <br />
<br />
计时器可以提供运行基于时间的工作任务的功能，在计时器的管理下，特定的任务可在某一时间运行一次，也可以按指定的时间间隔反复运行。在众多厂商提供的计时器中应用得比较多的有以下三种： <br />
<br />
● java.util.Timer <br />
Sun JDK 提供的一种轻量级的计时器。 <br />
<br />
● Commonj Timer <br />
IBM 和 BEA 联合制定和推出的一种适用于 J2EE 环境的计时器。<br />
&nbsp;<br />
● WebSphere Application Server Scheduler <br />
IBM WebSphere Application Server 提供的一种功能强大的计时器。 <br />
<br />
<br />
<span style="font-size: 15px; line-height: normal"><strong>java.util.Timer</strong> <br />
</span><br />
java.util.Timer 是 Sun JDK 提供的一种计时器，用于使后台线程按计划执行指定任务，这些任务可以被执行一次，也可以被定期执行。每个 Timer 对象对应一个后台线程，顺序地执行所有计时器任务。如果完成某个计时器任务的时间太长，那么它会&#8220;独占&#8221;计时器的任务执行线程，从而可能延迟后续任务的执行。对 Timer 对象最后的引用完成并且所有未处理的任务都已执行完成后，计时器的任务执行线程会正常终止（并且成为垃圾回收的对象）。</span><br />
<br />
java.util.Timer 简单易用，比较适合提供轻量级的计时器功能。由于其创建的线程会超出容器的管理范围，因此不能应用于管理的环境中。如果用户需要在 J2EE 环境中提供计时器功能，可考虑使用后面即将介绍的 Commonj Timer 或 WebSphere Application Server Scheduler。 <br />
<br />
<br />
<br />
<span style="font-size: 15px; line-height: normal"><strong>Commonj Timer</strong></span> <br />
<br />
Commonj Timer 是 Commonj 规范的一部分，它由 IBM 和 BEA 联合制定和推出，用以更好的响应客户和独立软件商的需求，给开发人员在开发可移植的服务端应用程序时提供一些更加简单和功能更加强大的方法。这个规范主要包括以下几个部分：Service Component Architecture，Service Data Objects，Work Manager and Timer 和 Enterprise Metadata Discovery。其中，Work Manager and Time 为在应用服务器中支持并发任务的执行提供了一些简单 API。这使用户可以方便地在 Servlet 和 EJB 中执行并发的计划任务，从而提高呑吐量，缩短服务端程序的响应时间，很好地解决了在 J2EE 环境中执行用户自定义的多线程并发与计时器服务的问题。 <br />
<br />
Commonj Timer API 包括三个接口：TimerManager, Timer 和 TimerListener。应用程序可以通过 TimerManager 来定期调用 TimerListener。每个 TimerManager 的 shcedule 方法返回一个 Timer 对象。用户可以通过 TimerManager 的 JNDI 名称在管理环境的上下文中查找 TimerManager。 <br />
<br />
<br />
<br />
java.util.Timer、 CommonJ Timer 和 WebSphere Application Server Scheduler 为用户提供了不同级别的、适用与不同范围的计时器，用户可以根据各自的需求使用不同的计时器，表 1 列出了 java.util.Timer、Commonj Timer 和 WebSphere Application Server Scheduler 之间比较结果，用户在使用计时器时可以用来参照比较。 <br />
<br />
表 1. 计时器比较结果 <br />
<strong>java.util.Timer</strong> <br />
来源：Sun <br />
优点：易于使用轻量级 <br />
缺点：创建的线程会超出容器管理范围 <br />
适用范围：非 J2EE 环境 <br />
<strong><br />
Commonj Timer</strong> <br />
来源：BEA and IBM <br />
优点：解决了 java.util.Timer 创建的线程超出容器管理范围的问题；不同于 JMX Timer Service 与 JMX framework 之间的紧耦合，提供了更加友好和独立的 API <br />
缺点：timer 是瞬时的、非事务性的，并且运行于创建它的 JVM 中，不适合于集群环境 <br />
适用范围：J2EE 普通环境 <br />
<br />
<strong>java.util.Timer</strong> <br />
来源：WebSphere Application Server Scheduler <br />
优点：易于管理 具有持久性和事务性 具有灵活的时间定制方式 具有扩展性，适用于集群环境 <br />
缺点：与 WebSphere Application Server 紧耦合 <br />
适用范围：J2EE 普通和集群环境 <br />
<br />
详见：http://www.ibm.com/developerworks/cn/java/j-lo-timer/index.html<br />
</span><!-- Attachments -->
<img src ="http://www.blogjava.net/orangelizq/aggbug/227175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-09-05 11:46 <a href="http://www.blogjava.net/orangelizq/archive/2008/09/05/227175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]java Serialization</title><link>http://www.blogjava.net/orangelizq/archive/2008/09/05/227169.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 05 Sep 2008 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/09/05/227169.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/227169.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/09/05/227169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/227169.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/227169.html</trackback:ping><description><![CDATA[<div id="BlogArticleDetail" style="font-size: 14px"><strong><font size="3">1. 什么是Serialization?</font></strong><br />
<br />
串行化(Serialization)是计算机科学中的一个概念，它是指将对象存储到介质（如文件、内在缓冲区等）中或是以二进制方式通过网络传输。之后可以通过反串行化从这些连续的位数据重新构建一个与原始对象状态相同的对象，因此在特定情况下也可以说是得到一个副本，但并不是所有情况都这样。<br />
<br />
Java有Serialization API为开发者提供了一种标准的机制来串行化类。<br />
<br />
<font color="#c0c0c0"><font color="#000000"><strong><font size="3">2. 为什么要Serilzation?</font></strong></font><br />
<br />
</font>特别地，串行化主要有三种用途：<br />
1）作为一种持久化机制<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果使用的是FileOutputStream流的方式，则数据将被自动地写入文件中，<br />
2）作为一种复制机制<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果使用的是<code class="Literal">ByteArrayOutputStream</code>流的方式，数据将写入内存中的字节数组中。该字节数组可以用来创建初始对象的副本，<br />
3）作为一种通信机制<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果是使用套接字（Socket）流的方式，则数据自动地通过网络连接传输一另一个端点，并由这个端点上的程序来决定做什么。<br />
<br />
<font size="3"><strong>3. Serialization的基本用法</strong></font><strong>：<font size="3">默认机制</font></strong><br />
<br />
将要串行化的类必须实现java.io.Serializable接口，或者是继承实现了该接口的类。然后通过java.io.ObjectOutputStream类来实现持久化，如果用保存到文件上还需要用到java.io.FileOutputStream类。因为ObjectOutputStream被认为是java.io包中的高级类所以可用它来包装低级的类FileOutputStream。在持久化过程中调用的一个方法是ObjectOutputStream对象的writeObject(obj)方法。<br />
<br />
当要从文件中恢复对象时，则是使用java.io.OjbectInputStream与FileInputStream类，调用一方法是ObjectInputStream对象的readObject()方法。<br />
<br />
<strong>示例1：</strong><br />
<font color="#808080"><br />
import java.io.*;<br />
<br />
public class Cat implements Serializable {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private String name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public Cat () {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.name = "new cat";<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public String getName() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public void setName(String name) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.name = name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
<br />
<br />
import java.io.*;<br />
<br />
public class CatDemo {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public static void main(String[] args) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Cat cat = new Cat();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> try { //串行化<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> FileOutputStream fos = new FileOutputStream("catDemo.out");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ObjectOutputStream oos = new ObjectOutputStream(fos);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> System.out.println(" 1&gt; " + cat.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cat.setName("My Cat"); &nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.writeObject(cat);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.close();&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> } catch (IOException ex) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ex.printStackTrace();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> try { //反串行化<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> FileInputStream fis = new FileInputStream("catDemo.out");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ObjectInputStream ois = new ObjectInputStream(fis);<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cat = (Cat) ois.readObject();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> System.out.println(" 2&gt; " + cat.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ois.close();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> } catch (IOException ex) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ex.printStackTrace();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<code>catch(ClassNotFoundException ex) {&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ex.printStackTrace();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }</code><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}</font><br />
<br />
<br />
<font size="3"><strong>4. Serialization常见问题</strong></font><br />
<br />
正如前面提到的，所有可串行化的类必须直接或是通过继承方式间接地实现java.io.Serializable接口，由于Object类关没有实现这个接口，所以并不是所有类的对象都是可串行化的。像AWT与Swing的GUI组件、字符串、数组等都是可串行化的，而像一些系统级的类（Thread<code>，OutputStream</code> 等）和Socket类是不可串行化的。<br />
<br />
<font color="#0000ff">问题一：如果在一个可串行化的类中Has-As不可串行化的类该怎么处理？</font><br />
在这种情况下在运行时会抛出NotSerializableException<wbr>。<br />
<br />
为了解决类似问题，Java中提供了transient关键字来跳过对不可串行化类的对象的处理。但这依然可能会引起一些问题，在反串行化时，被标识为transient变量不会恢复到其原始状态，而是提供默认值，如示例２中的pig引用将赋值为null，age变量赋值为0;<br />
<br />
<em>附：基本类型和引用类型的默认值<br />
对象引用：null<br />
byte, short, int, long ：0<br />
float, double：0.0<br />
boolean：false<br />
char：'\u0000'(这是Unicode字符集的空格)</em><br />
<br />
<strong>示例2：</strong><br />
<code><font color="#808080">import java.io.*;<br />
public class NewPig2 implements Serializable {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private String newName;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private <font color="#0000ff"><strong>transient</strong></font> Pig pig = new Pig();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private <font color="#0000ff"><strong>transient</strong></font> int age = 2;&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public NewPig2() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> newName = "new Pig 2";<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //pig = new Pig();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public Pig getPig() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.pig;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public void setName(String name) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.newName = name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public String getName() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.newName;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public int getAge() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.age;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
</font></code><code><br />
<font color="#0000ff">问题二：如果父类不可串行化，子类实现了Serializable会怎样？</font><br />
如果有一个Animal类是不可串行化的，而有一个Dog类继承自Animal类并且实现了Serializabl接口，则没有串行化时没有任何问题，但是在反串行化时将会重新调用Animal的构造函数，如示例3所示。<br />
<br />
示例3的运行结果如下：</code><code><br />
1&gt; No Color - new Dog<br />
2&gt; Green - My Dog<br />
4&gt; No Color - My Dog<br />
因为Animal不可串行化，所以必须运行构造函数，但不会在实现Serializable的反串行化类上运行构造函数。<br />
</code><code><br />
示例3：<br />
<font color="#808080"><br />
public class Animal {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private String color;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <font color="#333399">public Animal () {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.color = "No Color";<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }</font><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public void setColor(String color) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.color = color;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public String getColor () {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.color;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
<br />
<br />
import java.io.*;<br />
public class Dog extends Animal implements Serializable {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private String name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public Dog () {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.name = "new Dog";<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public String getName() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public void setName(String name) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.name = name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
<br />
import java.io.*;<br />
public class DogTest {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public static void main(String[] args) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Dog dog = new Dog();&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> System.out.println(" 1&gt; " + dog.getColor() + " - " + dog.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dog.setColor("Green");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dog.setName("My Dog");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> System.out.println(" 2&gt; " + dog.getColor() + " - " + dog.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> try {//串行化<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> FileOutputStream fos = new FileOutputStream("myDog.out");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ObjectOutputStream oos = new ObjectOutputStream(fos);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.writeObject(dog);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.close();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> } catch (Exception ex) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ex.printStackTrace();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> try {//反串行化<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> FileInputStream fis = new FileInputStream("myDog.out");<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ObjectInputStream ois = new ObjectInputStream(fis);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dog = (Dog) ois.readObject(); &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> System.out.println(" 4&gt; " + dog.getColor() + " - " + dog.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ois.close();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> } catch (Exception ex) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ex.printStackTrace();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
<br />
</font><strong><font size="3">5.</font></strong></code><strong>自定义默认方法：使用writeOject和readObject<br />
</strong><font size="2"><br />
假如在一个可以串行化的类中有一个不可串行化的对象，但又想保存该对象的状态信息该如何是好？在这样情况下可以在这个可串行化的类中实现writeObject()和readObject()。<br />
<br />
<strong>示例4:</strong><br />
<font color="#808080">import java.io.*;<br />
import java.util.Scanner;<br />
<br />
public class NewPig3 implements Serializable {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private String newName;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private transient Pig pig = new Pig();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> private transient int age = 2;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public NewPig3() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> newName = "new Pig 3";<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //pig = new Pig();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <font color="#0000ff"><strong>private void writeObject(ObjectOutputStream oos) throws IOException {</strong></font><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.defaultWriteObject();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> oos.writeChars(pig.getName());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //oos.writeInt(this.age);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <font color="#0000ff"><strong>private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {</strong></font><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ois.defaultReadObject();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pig = new Pig();<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Scanner in = new Scanner(ois);<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> if (in.hasNextLine()) {&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pig.setName(in.nextLine());<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public Pig getPig() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.pig;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public void setName(String name) {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> this.newName = name;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public String getName() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.newName;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> public int getAge() {<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> return this.age;<br />
&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }<br />
}<br />
</font></font></div>
<img src ="http://www.blogjava.net/orangelizq/aggbug/227169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-09-05 11:26 <a href="http://www.blogjava.net/orangelizq/archive/2008/09/05/227169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]软件版本号定义 </title><link>http://www.blogjava.net/orangelizq/archive/2008/07/07/213053.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 07 Jul 2008 08:05:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/07/07/213053.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/213053.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/07/07/213053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/213053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/213053.html</trackback:ping><description><![CDATA[<p>版本控制比较普遍的 3 种命名格式 :<br />
<strong>一、GNU 风格的版本号命名格式 :</strong><br />
主版本号 . 子版本号 [. 修正版本号 [. 编译版本号 ]]<br />
Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]<br />
示例 : 1.2.1, 2.0, 5.0.0 build-13124<br />
<br />
<strong>二、Windows 风格的版本号命名格式 :</strong><br />
主版本号 . 子版本号 [ 修正版本号 [. 编译版本号 ]]<br />
Major_Version_Number.Minor_Version_Number[Revision_Number[.Build_Number]]<br />
示例: 1.21, 2.0<br />
<br />
<strong>三、.Net Framework 风格的版本号命名格式:</strong><br />
主版本号.子版本号[.编译版本号[.修正版本号]]<br />
Major_Version_Number.Minor_Version_Number[.Build_Number[.Revision_Number]]<br />
版本号由二至四个部分组成：主版本号、次版本号、内部版本号和修订号。主版本号和次版本号是必选的；内部版本号和修订号是可选的，但是如果定义了修订号部分，则内部版本号就是必选的。所有定义的部分都必须是大于或等于 0 的整数。<br />
<br />
<strong>应根据下面的约定使用这些部分：</strong><br />
Major ：具有相同名称但不同主版本号的程序集不可互换。例如，这适用于对产品的大量重写，这些重写使得无法实现向后兼容性。<br />
Minor ：如果两个程序集的名称和主版本号相同，而次版本号不同，这指示显著增强，但照顾到了向后兼容性。例如，这适用于产品的修正版或完全向后兼容的新版本。<br />
Build ：内部版本号的不同表示对相同源所作的重新编译。这适合于更改处理器、平台或编译器的情况。<br />
Revision ：名称、主版本号和次版本号都相同但修订号不同的程序集应是完全可互换的。这适用于修复以前发布的程序集中的安全漏洞。<br />
程序集的只有内部版本号或修订号不同的后续版本被认为是先前版本的修补程序 (Hotfix) 更新。<br />
<br />
<strong>版本号管理策略</strong><br />
<strong>一、GNU 风格的版本号管理策略：</strong><br />
1．项目初版本时，版本号可以为 0.1 或 0.1.0, 也可以为 1.0 或 1.0.0，如果你为人很低调，我想你会选择那个主版本号为 0 的方式；<br />
2．当项目在进行了局部修改或 bug 修正时，主版本号和子版本号都不变，修正版本号加 1；<br />
3. 当项目在原有的基础上增加了部分功能时，主版本号不变，子版本号加 1，修正版本号复位为 0，因而可以被忽略掉；<br />
4．当项目在进行了重大修改或局部修正累积较多，而导致项目整体发生全局变化时，主版本号加 1；<br />
5．另外，编译版本号一般是编译器在编译过程中自动生成的，我们只定义其格式，并不进行人为控制。<br />
<br />
<strong>二、Window 下的版本号管理策略：</strong><br />
1．项目初版时，版本号为 1.0 或 1.00；<br />
2. 当项目在进行了局部修改或 bug 修正时，主版本号和子版本号都不变，修正版本号加 1；<br />
3. 当项目在原有的基础上增加了部分功能时，主版本号不变，子版本号加 1，修正版本号复位为 0，因而可以被忽略掉；<br />
4. 当项目在进行了重大修改或局部修正累积较多，而导致项目整体发生全局变化时，主版本号加 1；<br />
5. 另外 , 编译版本号一般是编译器在编译过程中自动生成的，我们只定义其格式，并不进行人为控制。<br />
另外，还可以在版本号后面加入 Alpha、Beta、Gamma、Current、RC (Release Candidate)、Release、Stable 等后缀，在这些后缀后面还可以加入 1 位数字的版本号。<br />
对于用户来说，如果某个软件的主版本号进行了升级，用户还想继续那个软件，则发行软件的公司一般要对用户收取升级费用；而如果子版本号或修正版本号发生了升级，一般来说是免费的。<br />
<br />
=============附录软件版本名称============<br />
<strong>&#945;（alphal） 内部测试版</strong><br />
&#945;版，此版本表示该软件仅仅是一个初步完成品，通常只在软件开发者内部交流，也有很少一部分发布给专业测试人员。一般而言，该版本软件的 bug 较多，普通用户最好不要安装。<br />
<strong>&#946;（beta）外部测试版</strong><br />
该版本相对于&#945;版已有了很大的改进，消除了严重的错误，但还是存在着一些缺陷，需要经过大规模的发布测试来进一步消除。这一版本通常由软件公司免费发布，用户可从相关的站点下载。通过一些专业爱好者的测试，将结果反馈给开发者，开发者们再进行有针对性的修改。该版本也不适合一般用户安装。<br />
<strong>&#947;（gamma）版</strong><br />
该版本已经相当成熟了，与即将发行的正式版相差无几，如果用户实在等不及了，尽可以装上一试。<br />
<strong>trial（试用版）</strong><br />
试用版软件在最近的几年里颇为流行，主要是得益于互联网的迅速发展。该版本软件通常都有时间限制，过期之后用户如果希望继续使用，一般得交纳一定的费用进行注册或购买。有些试用版软件还在功能上做了一定的限制。<br />
<strong>unregistered（未注册版）</strong><br />
未注册版与试用版极其类似，只是未注册版通常没有时间限制，在功能上相对于正式版做了一定的限制，例如绝大多数网络电话软件的注册版和未注册版，两者之间在通话质量上有很大差距。还有些虽然在使用上与正式版毫无二致，但是动不动就会弹出一个恼人的消息框来提醒你注册，如看图软件acdsee、智能陈桥汉字输入软件等。<br />
<strong>demo 演示版</strong><br />
在非正式版软件中，该版本的知名度最大。demo版仅仅集成了正式版中的几个功能，颇有点像 unregistered。不同的是，demo版一般不能通过升级或注册的方法变为正式版。<br />
以上是软件正式版本推出之前的几个版本，&#945;、&#946;、&#947;可以称为测试版，大凡成熟软件总会有多个测试版，如 windows 98 的&#946;版，前前后后将近有10个。这么多的测试版一方面为了最终产品尽可能地满足用户的需要，另一方面也尽量减少了软件中的bug 。而 trial 、unregistered 、demo有时统称为演示版，这一类版本的广告色彩较浓，颇有点先尝后买的味道，对于普通用户而言自然是可以免费尝鲜了。<br />
<strong>正式版，不同类型的软件的正式版本通常也有区别。</strong><br />
<strong>release 最终释放版</strong><br />
该版本意味"最终释放版"，在出了一系列的测试版之后，终归会有一个正式版本，对于用户而言，购买该版本的软件绝对不会错。该版本有时也称为标准版。一般情况下，release不会以单词形式出现在软件封面上，取而代之的是符号 (r) ，如 windows nt(r) 4.0、ms-dos(r) 6.22 等。<br />
<strong>registered 注册版</strong><br />
很显然，该版本是与 unregistered 相对的注册版。注册版、release和下面所讲的standard版一样，都是软件的正式版本，只是注册版软件的前身有很大一部分是从网上下载的。<br />
<strong>standard 标准版</strong><br />
这是最常见的标准版，不论是什么软件，标准版一定存在。标准版中包含了该软件的基本组件及一些常用功能，可以满足一般用户的需求。其价格相对高一级版本而言还是"平易近人"的。<br />
<strong>deluxe 豪华版</strong><br />
顾名思义即为"豪华版"。豪华版通常是相对于标准版而言的，主要区别是多了几项功能，价格当然会高出一大块，不推荐一般用户购买。此版本通常是为那些追求"完美"的专业用户所准备的。<br />
<strong>reference</strong><br />
该版本型号常见于百科全书中，比较有名的是微软的encarta系列。 reference是最高级别，其包含的主题、图像、影片剪辑等相对于standard和deluxe版均有大幅增加，容量由一张光盘猛增至三张光盘，并且加入了很强的交互功能，当然价格也不菲。可以这么说，这一版本的百科全书才能算是真正的百科全书，也是发烧友们收藏的首选。<br />
<strong>professional（专业版）</strong><br />
专业版是针对某些特定的开发工具软件而言的。专业版中有许多内容是标准版中所没有的，这些内容对于一个专业的软件开发人员来说是极为重要的。如微软的visual foxpro标准版并不具备编译成可执行文件的功能，这对于一个完整的开发项目而言显然是无法忍受的，若客户机上没有foxpro将不能使用。如果用专业版就没有这个问题了。<br />
<strong>enterprise（企业版）</strong><br />
企业版是开发类软件中的极品（相当于百科全书中的reference版）。拥有一套这种版本的软件可以毫无障碍地开发任何级别的应用软件。如著名的visual c++的企业版相对于专业版来说增加了几个附加的特性，如sql调试、扩展的存储过程向导、支持as/400对ole db的访问等。而这一版本的价格也是普通用户无法接受的。如微软的visual studios 6.0 enterprise 中文版的价格为 23000 元。<br />
<strong>其他版本，除了以上介绍的一些版本外，还有一些专有版本名称。</strong><br />
<strong>update（升级版）</strong><br />
升级版的软件是不能独立使用的，该版本的软件在安装过程中会搜索原有的正式版，如果不存在，则拒绝执行下一步。如microsoft office 2000升级版、windows 9x升级版等等。<br />
<strong>oem版</strong><br />
oem 版通常是捆绑在硬件中而不单独销售的版本。将自己的产品交给别的公司去卖，保留自己的著作权，双方互惠互利，一举两得。<br />
<strong>单机（网络）版</strong><br />
网络版在功能、结构上远比单机版复杂，如果留心一下软件的报价，你就会发现某些软件单机版和网络版的价格相差非常大，有些网络版甚至多一个客户端口就要加不少钱。<br />
<strong>普及版</strong><br />
该版本有时也会被称为共享版，其特点是价格便宜（有些甚至完全免费）、功能单一、针对性强（当然也有占领市场、打击盗版等因素）。与试用版不同的是，该版本的软件一般不会有时间上的限制。当然，如果用户想升级，最好还是去购买正式版。<br />
Enhance 增强版或者加强版 属于正式版<br />
Free 自由版<br />
Full version 完全版 属于正式版<br />
shareware 共享版<br />
Release 发行版 有时间限制<br />
Upgrade 升级版<br />
Retail 零售版<br />
Cardware 属共享软件的一种，只要给作者回复一封电邮或明信片即可。（有的作者并由此提供注册码等），目前这种形式已不多见。<br />
Plus 属增强版，不过这种大部分是在程序界面及多媒体功能上增强。<br />
Preview 预览版<br />
Corporation &amp; Enterprise 企业版<br />
Standard 标准版<br />
Mini 迷你版也叫精简版只有最基本的功能<br />
Premium -- 贵价版<br />
Professional -- 专业版<br />
Express -- 特别版<br />
Deluxe -- 豪华版<br />
Regged -- 已注册版<br />
CN -- 简体中文版<br />
CHT -- 繁体中文版<br />
EN -- 英文版<br />
Multilanguage -- 多语言版 </p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/213053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-07-07 16:05 <a href="http://www.blogjava.net/orangelizq/archive/2008/07/07/213053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]JDK命令简单总结</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/12/174767.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 12 Jan 2008 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/12/174767.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/174767.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/12/174767.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/174767.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/174767.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/12/174767.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/174767.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-12 10:48 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/12/174767.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 在Servlet与JSP中取得当前文件所在的相对路径与绝对路径</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/12/174766.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 12 Jan 2008 02:45:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/12/174766.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/174766.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/12/174766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/174766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/174766.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/12/174766.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/174766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-12 10:45 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/12/174766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 关于Java栈与堆的思考</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/12/174764.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 12 Jan 2008 02:42:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/12/174764.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/174764.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/12/174764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/174764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/174764.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/12/174764.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/174764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-12 10:42 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/12/174764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] Java 文件路径</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/12/174762.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 12 Jan 2008 02:41:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/12/174762.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/174762.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/12/174762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/174762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/174762.html</trackback:ping><description><![CDATA[<div class="postText">Java 文件路径<br />
<br />
<span style="color: #0000ff">1.如何获得当前文件路径</span><br />
<br />
常用：<br />
<br />
字符串类型：System.getProperty("user.dir");<br />
<br />
综合：<br />
<br />
<pre>package com.zcjl.test.base;<br />
import java.io.File;<br />
public class Test {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.currentThread().getContextClassLoader()<wbr></wbr>.getResource(""));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getClassLoader().getResource(""));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(ClassLoader.getSystemResource(""));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getResource(""));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Test.class.getResource("/"));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(new File("").getAbsolutePath());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(System.getProperty("user.dir"));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</pre>
<br />
<br />
<span style="color: #0000ff">2.Web服务中</span><br />
<br />
(1).Weblogic<br />
<br />
WebApplication的系统文件根目录是你的weblog<wbr></wbr>ic安装所在根目录。<br />
例如：如果你的weblogic安装在c:\bea<wbr></wbr>\weblogic700..... <br />
那么，你的文件根路径就是c:\.<br />
所以，有两种方式能够让你访问你的服务器端的文件：<br />
a.使用绝对路径：<br />
比如将你的参数文件放在c:\yourconfig<wbr></wbr>\yourconf.properties，<br />
直接使用 new FileInputStream("yourconfig<wbr></wbr>/yourconf.properties");<br />
b.使用相对路径：<br />
相对路径的根目录就是你的webapplication的根路径<wbr></wbr>，即WEB-INF的上一级目录，将你的参数文件放在yourwe<wbr></wbr>bapp\yourconfig\yourconf<wbr></wbr>.properties， <br />
这样使用：<br />
new FileInputStream("./yourconfig<wbr></wbr>/yourconf.properties");<br />
这两种方式均可，自己选择。<br />
<br />
(2).Tomcat<br />
<br />
在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin<br />
<br />
(3).Resin<br />
<br />
不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成S<wbr></wbr>ERVLET <br />
的路径为根.比如用新建文件法测试File f = new File("a.htm");<br />
这个a.htm在resin的安装目录下 <br />
<br />
(4).如何读相对路径哪？<br />
<br />
在Java文件中getResource或getResource<wbr></wbr>AsStream均可<br />
<br />
例：getClass().getResourceAsStrea<wbr></wbr>m(filePath);//filePath可以是"<wbr></wbr>/filename",这里的/代表web发布根路径下WEB<wbr></wbr>-INF/classes <br />
<br />
(5).获得文件真实路径<br />
<br />
string&nbsp;&nbsp;file_real_path=request.getRealPath("mypath/filename");&nbsp;&nbsp;<br />
<br />
通常使用request.getRealPath("/");&nbsp;&nbsp;<br />
<br />
<span style="color: #0000ff"><br />
3.文件操作的类<br />
</span><br />
<pre>import java.io.*;<br />
import java.net.*;<br />
import java.util.*;<br />
//import javax.swing.filechooser.*;<br />
//import org.jr.swing.filter.*;<br />
<br />
/**<br />
* 此类中封装一些常用的文件操作。<br />
* 所有方法都是静态方法，不需要生成此类的实例，<br />
* 为避免生成此类的实例，构造方法被申明为private类型的。<br />
* @since&nbsp;&nbsp;0.1<br />
*/<br />
<br />
public class FileUtil {<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 私有构造方法，防止类的实例化，因为工具类不需要实例化。<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;private FileUtil() {<br />
<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 修改文件的最后访问时间。<br />
&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br />
&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出<wbr></wbr>，这些信息输出是否保留还在考<br />
<br />
虑中。&lt;/b&gt;<br />
&nbsp;&nbsp; * @param file 需要修改最后访问时间的文件。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static void touch(File file) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;long currentTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (!file.exists()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println("file not found:" + file.getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println("Create a new file:" + file.getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (file.createNewFile()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.out.println("Succeeded!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("Create file failed!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (IOException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("Create file failed!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean result = file.setLastModified(currentTime);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (!result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;System.err.println("touch failed: " + file.getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 修改文件的最后访问时间。<br />
&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br />
&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出<wbr></wbr>，这些信息输出是否保留还在考<br />
<br />
虑中。&lt;/b&gt;<br />
&nbsp;&nbsp; * @param fileName 需要修改最后访问时间的文件的文件名。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static void touch(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;touch(file);<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 修改文件的最后访问时间。<br />
&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br />
&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出<wbr></wbr>，这些信息输出是否保留还在考<br />
<br />
虑中。&lt;/b&gt;<br />
&nbsp;&nbsp; * @param files 需要修改最后访问时间的文件数组。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static void touch(File[] files) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; files.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;touch(files);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 修改文件的最后访问时间。<br />
&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br />
&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出<wbr></wbr>，这些信息输出是否保留还在考<br />
<br />
虑中。&lt;/b&gt;<br />
&nbsp;&nbsp; * @param fileNames 需要修改最后访问时间的文件名数组。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static void touch(String[] fileNames) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File[] files = new File[fileNames.length];<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; fileNames.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files = new File(fileNames);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;touch(files);<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 判断指定的文件是否存在。<br />
&nbsp;&nbsp; * @param fileName 要判断的文件的文件名<br />
&nbsp;&nbsp; * @return 存在时返回true，否则返回false。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean isFileExist(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return new File(fileName).isFile();<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 创建指定的目录。<br />
&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br />
&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;<wbr></wbr>/b&gt;<br />
&nbsp;&nbsp; * @param file 要创建的目录<br />
&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean makeDirectory(File file) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File parent = file.getParentFile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (parent != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return parent.mkdirs();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 创建指定的目录。<br />
&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br />
&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;<wbr></wbr>/b&gt;<br />
&nbsp;&nbsp; * @param fileName 要创建的目录的目录名<br />
&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean makeDirectory(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return makeDirectory(file);<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 清空指定目录中的文件。<br />
&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都<wbr></wbr>会返回false。<br />
&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br />
&nbsp;&nbsp; * @param directory 要清空的目录<br />
&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false<wbr></wbr>.<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean emptyDirectory(File directory) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean result = false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;File[] entries = directory.listFiles();<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; entries.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!entries.delete()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 清空指定目录中的文件。<br />
&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都<wbr></wbr>会返回false。<br />
&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br />
&nbsp;&nbsp; * @param directoryName 要清空的目录的目录名<br />
&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false<wbr></wbr>。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean emptyDirectory(String directoryName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File dir = new File(directoryName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return emptyDirectory(dir);<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<br />
&nbsp;&nbsp; * @param dirName 要删除的目录的目录名<br />
&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean deleteDirectory(String dirName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return deleteDirectory(new File(dirName));<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<br />
&nbsp;&nbsp; * @param dir 要删除的目录<br />
&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.1<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static boolean deleteDirectory(File dir) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if ( (dir == null) || !dir.isDirectory()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new IllegalArgumentException(<wbr></wbr>"Argument " + dir +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<wbr></wbr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " is not a directory. ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;File[] entries = dir.listFiles();<br />
&nbsp;&nbsp;&nbsp;&nbsp;int sz = entries.length;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; sz; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (entries.isDirectory()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!deleteDirectory(entries)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!entries.delete()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (!dir.delete()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;}<br />
<br />
<br />
<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 返回文件的URL地址。<br />
&nbsp;&nbsp; * @param file 文件<br />
&nbsp;&nbsp; * @return 文件对应的的URL地址<br />
&nbsp;&nbsp; * @throws MalformedURLException<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp; * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件<wbr></wbr>路径转换为URL。<br />
&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请使用File.toURL方法。<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static URL getURL(File file) throws MalformedURLException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;String fileURL = "file:/" + file.getAbsolutePath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;URL url = new URL(fileURL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return url;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 从文件路径得到文件名。<br />
&nbsp;&nbsp; * @param filePath 文件的路径，可以是相对路径也可以是绝对路径<br />
&nbsp;&nbsp; * @return 对应的文件名<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getFileName(String filePath) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(filePath);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return file.getName();<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 从文件名得到文件绝对路径。<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 对应的文件路径<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getFilePath(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return file.getAbsolutePath();<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 将DOS/Windows格式的路径转换为UNIX<wbr></wbr>/Linux格式的路径。<br />
&nbsp;&nbsp; * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转<wbr></wbr>换为这种方式比较方便，<br />
&nbsp;&nbsp; * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS<wbr></wbr>/Windows也将它当作路径分隔符。<br />
&nbsp;&nbsp; * @param filePath 转换前的路径<br />
&nbsp;&nbsp; * @return 转换后的路径<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String toUNIXpath(String filePath) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return filePath.replace('\\', '/');<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 从文件名得到UNIX风格的文件绝对路径。<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 对应的UNIX风格的文件路径<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.4<br />
&nbsp;&nbsp; * @see #toUNIXpath(String filePath) toUNIXpath<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getUNIXfilePath(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;File file = new File(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return toUNIXpath(file.getAbsolutePath());<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到文件的类型。<br />
&nbsp;&nbsp; * 实际上就是得到文件名中最后一个"."后面的部分。<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 文件名中的类型部分<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getTypePart(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('.');<br />
&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1 || point == length - 1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(point + 1, length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到文件的类型。<br />
&nbsp;&nbsp; * 实际上就是得到文件名中最后一个"."后面的部分。<br />
&nbsp;&nbsp; * @param file 文件<br />
&nbsp;&nbsp; * @return 文件名中的类型部分<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getFileType(File file) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return getTypePart(file.getName());<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到文件的名字部分。<br />
&nbsp;&nbsp; * 实际上就是路径中的最后一个路径分隔符后的部分。<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 文件名中的名字部分<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getNamePart(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = getPathLsatIndex(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else if (point == length - 1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int secondPoint = getPathLsatIndex(fileName, point - 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (secondPoint == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (length == 1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, point);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(secondPoint + 1, point);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(point + 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到文件名中的父路径部分。<br />
&nbsp;&nbsp; * 对两种路径分隔符都有效。<br />
&nbsp;&nbsp; * 不存在时返回""。<br />
&nbsp;&nbsp; * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"<wbr></wbr>/path/"返回""。<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 父路径，不存在或者已经是父目录时返回""<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getPathPart(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = getPathLsatIndex(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;int length = fileName.length();<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else if (point == length - 1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int secondPoint = getPathLsatIndex(fileName, point - 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (secondPoint == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, secondPoint);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(0, point);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到路径分隔符在文件路径中首次出现的位置。<br />
&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br />
&nbsp;&nbsp; * @param fileName 文件路径<br />
&nbsp;&nbsp; * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static int getPathIndex(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.indexOf('/');<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.indexOf('\\');<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return point;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置后首次出现的位置。<br />
&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br />
&nbsp;&nbsp; * @param fileName 文件路径<br />
&nbsp;&nbsp; * @param fromIndex 开始查找的位置<br />
&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回<wbr></wbr>-1。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static int getPathIndex(String fileName, int fromIndex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.indexOf('/', fromIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.indexOf('\\', fromIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return point;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到路径分隔符在文件路径中最后出现的位置。<br />
&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br />
&nbsp;&nbsp; * @param fileName 文件路径<br />
&nbsp;&nbsp; * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static int getPathLsatIndex(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('/');<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.lastIndexOf('\\');<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return point;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置前最后出现的位置。<br />
&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br />
&nbsp;&nbsp; * @param fileName 文件路径<br />
&nbsp;&nbsp; * @param fromIndex 开始查找的位置<br />
&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回<wbr></wbr>-1。<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static int getPathLsatIndex(String fileName, int fromIndex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int point = fileName.lastIndexOf('/', fromIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (point == -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point = fileName.lastIndexOf('\\', fromIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;return point;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 将文件名中的类型部分去掉。<br />
&nbsp;&nbsp; * @param filename 文件名<br />
&nbsp;&nbsp; * @return 去掉类型部分的结果<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String trimType(String filename) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int index = filename.lastIndexOf(".");<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (index != -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return filename.substring(0, index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return filename;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;/**<br />
&nbsp;&nbsp; * 得到相对路径。<br />
&nbsp;&nbsp; * 文件名不是目录名的子节点时返回文件名。<br />
&nbsp;&nbsp; * @param pathName 目录名<br />
&nbsp;&nbsp; * @param fileName 文件名<br />
&nbsp;&nbsp; * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件<wbr></wbr>名<br />
&nbsp;&nbsp; * @since&nbsp;&nbsp;0.5<br />
&nbsp;&nbsp; */<br />
&nbsp;&nbsp;public static String getSubpath(String pathName,String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int index = fileName.indexOf(pathName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (index != -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName.substring(index + pathName.length() + 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fileName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
}</pre>
<br />
<br />
<span style="color: #0000ff">4.遗留问题</span><br />
<br />
目前new FileInputStream()只会使用绝对路径<wbr></wbr>，相对没用过，因为要相对于web服务器地址，比较麻烦<br />
<br />
还不如写个配置文件来的快哪<br />
<br />
<span style="color: #0000ff">5.按Java文件类型分类读取配置文件</span><br />
<br />
配置文件是应用系统中不可缺少的，可以增加程序的灵活性。 java.util.Properties是从jdk1<wbr></wbr>.2就有的类，一直到现在都支持load()方法，jdk1.4以后save(output,string) -&gt;store(output,string)。如果只是单纯的读<wbr></wbr>，根本不存在烦恼的问题。web层可以通过Thread.currentThread().getContextClassLoader().<br />
getResourceAsStream("xx.properties ")获取；Application可以通过new FileInputStream("xx.properties");直接在classes一级获取。关键是有时我们需要通过we<wbr></wbr>b修改配置文件，我们不能将路径写死了。经过测试觉得有以下心得：<br />
<br />
1.servlet中读写。如果运用Struts或者Servlet可以直接在初始化参数中<wbr></wbr>配置，调用时根据servlet的getRealPath("/<wbr></wbr>")获取真实路径，再根据String file = this.servlet.getInitParameter("abc");获取相对的WEB-INF的相对路径。<br />
例：<br />
<pre>InputStream input = Thread.currentThread().getContextClassLoader().<br />
getResourceAsStream("abc.properties");<br />
Properties prop = new Properties();<br />
prop.load(input);<br />
input.close();<br />
OutputStream out = new FileOutputStream(path);<br />
prop.setProperty("abc", "test");<br />
prop.store(out, "&#8211;test&#8211;");<br />
out.close();</pre>
<br />
<br />
2.直接在jsp中操作，通过jsp内置对象获取可操作的绝对地址。 <br />
例：<br />
<pre>// jsp页面<br />
String path = pageContext.getServletContext().getRealPath("/");<br />
String realPath = path+"/WEB-INF/classes/abc<wbr></wbr>.properties";<br />
<br />
//java 程序<br />
InputStream in = getClass().getClassLoader()<wbr></wbr>.getResourceAsStream("abc.properties"); // abc.properties放在webroot/WEB-INF/classes/目录下<br />
prop.load(in);<br />
in.close();<br />
<br />
OutputStream out = new FileOutputStream(path); // path为通过页面传入的路径<br />
prop.setProperty("abc", "abcccccc");<br />
prop.store(out, "&#8211;test&#8211;");<br />
out.close();</pre>
<br />
<br />
3.只通过Java程序操作资源文件<br />
<pre>InputStream in = new FileInputStream("abc.properties"); // 放在classes同级<br />
<br />
OutputStream out = new FileOutputStream("abc.properties");</pre>
<br />
</div>
<img src ="http://www.blogjava.net/orangelizq/aggbug/174762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-12 10:41 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/12/174762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象中间件之Java RMI (3) </title><link>http://www.blogjava.net/orangelizq/archive/2007/12/01/164542.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 01 Dec 2007 13:01:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/12/01/164542.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/164542.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/12/01/164542.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/164542.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/164542.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;3.2.2 编译源程序首先将两个接口类Compute.class和Task.class打成JAR包（compute.jar）传到客户端，或者向客户端添加类路径也行。这样客户端的类才能正确编译。编译过程不在详述。需要被其他JVM动态加载的类要能够通过网络远程访问到，因此可以将它们放在一个Web服务器上，通过HTTP访问。当然在本机上测试时也可以使用文件路径，在此使用文件路径，...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/12/01/164542.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/164542.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-12-01 21:01 <a href="http://www.blogjava.net/orangelizq/archive/2007/12/01/164542.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象中间件之Java RMI (2) </title><link>http://www.blogjava.net/orangelizq/archive/2007/12/01/164541.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 01 Dec 2007 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/12/01/164541.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/164541.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/12/01/164541.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/164541.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/164541.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; 三、Java RMI使用RMI 应用通常有两个分开的程序组成，一个服务端程序和一个客户端程序。一个典型的服务端程序创建一些远程对象，使得对这些远程对象的引用可以被访问，等待客户端调用这些远程对象提供的方法。一个典型的客户端程序获取远程引用，指向一个或者多个服务端上的远程对象，然后调用这些远程对象所提供的方法。通常我们称这为分布式对象应用程序。3.1 RMI的工作方式分布式...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/12/01/164541.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/164541.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-12-01 20:55 <a href="http://www.blogjava.net/orangelizq/archive/2007/12/01/164541.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象中间件之Java RMI (1)</title><link>http://www.blogjava.net/orangelizq/archive/2007/12/01/164540.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 01 Dec 2007 12:45:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/12/01/164540.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/164540.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/12/01/164540.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/164540.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/164540.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;对象中间件之Java RMI作者：orangelizq&nbsp;&nbsp; 2007-11-30一、中间件简介1.1 中间件的定义中间件(middleware)是基础软件的一大类，属于可复用的软件范畴。中间件在操作系统软件，网络和数据库之上，应用软件之下，总的作用是为处于自己上层的应用软件提供运行于开发的环境，帮助用户灵活、高效的开发和集成复杂的应用软件。[1]...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/12/01/164540.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/164540.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-12-01 20:45 <a href="http://www.blogjava.net/orangelizq/archive/2007/12/01/164540.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java clone()</title><link>http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Wed, 17 Oct 2007 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/153573.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/153573.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/153573.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp; 现在Clone已经不是一个新鲜词语了，伴随着&#8220;多莉&#8221;的产生这个词语确实很&#8220;火&#8221;过一阵子，在java中也有这么一个概念，它可以让我们很方便的&#8220;制造&#8221;出一个对象的副本来，下面来具体看看java中的Clone机制是如何工作的？<br />
&nbsp;&nbsp;&nbsp;&nbsp; 1． Clone&amp;Copy<br />
&nbsp;&nbsp;&nbsp;&nbsp; 假设现在有一个Employee对象，Employee tobby =new Employee(&#8220;CMTobby&#8221;,5000)，通<br />
常我们会有这样的赋值Employee cindyelf=tobby，这个时候只是简单了copy了一下reference，cindyelf和tobby都指向内存中同一个object，这样cindyelf或者tobby的一个操作都可能影响到对方。打个比方，如果我们通过cindyelf.raiseSalary()方法改变了salary域的值，那么tobby通过getSalary()方法得到的就是修改之后的salary域的值，显然这不是我们愿意看到的。我们希望得到tobby的一个精确拷贝，同时两者互不影响，这时候我们就可以使用Clone来满足我们的需求。Employee cindy=tobby.clone()，这时会生成一个新的Employee对象，并且和tobby具有相同的属性值和方法。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2． Shallow Clone&amp;Deep Clone<br />
Clone是如何完成的呢？<span style="color: #0000ff">Object在对某个对象实施Clone时对其是一无所知的，它仅仅是简单地执行域对域的copy，这就是Shallow Clone。</span>这样，问题就来了咯，以Employee为例，它里面有一个域hireDay不是基本型别的变量，而是一个reference变量，经过Clone之后就会产生一个新的Date型别的reference，它和原始对象中对应的域指向同一个Date对象，这样克隆类就和原始类共享了一部分信息，而这样显然是不利的，过程下图所示：<br />
<div align="center">
<div align="center"><img height="265" alt="" src="http://www.blogjava.net/images/blogjava_net/orangelizq/d6935ca561cf4f969134c597eb6437f6.png" width="515" border="0" /></div>
</div>
<p>&nbsp;</p>
<p>这个时候我们就需要进行deep Clone了，对那些非基本型别的域进行特殊的处理，例如本例中的hireDay。我们可以重新定义Clone方法，对hireDay做特殊处理，如下代码所示：<br />
<br />
&nbsp;&nbsp; class Employee implements Cloneable</p>
<pre style="text-indent: 24pt">{</pre>
<pre><span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public Object clone() throws CloneNotSupportedException</span></pre>
<pre><span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span></pre>
<pre><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Employee cloned = (Employee) super.clone();</span></pre>
<pre style="text-indent: 18pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cloned.hireDay = (Date) hireDay.clone()</span></pre>
<pre style="text-indent: 18pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cloned;</span></pre>
<pre><span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></pre>
<pre style="text-indent: 30pt">}<br />
<br />
3． Clone()方法的保护机制<br />
在Object中Clone()是被申明为protected的，这样做是有一定的道理的，以Employee<br />
类为例，通过申明为protected，就可以保证只有Employee类里面才能&#8220;克隆&#8221;Employee对象，原理可以参考我前面关于public、protected、private的学习笔记。<br />
4． Clone()方法的使用<br />
Clone()方法的使用比较简单，注意如下几点即可：<br />
a． <span style="color: #0000ff">什么时候使用shallow Clone，什么时候使用deep Clone，这个主要看具体对象的域是什么性质的，基本型别还是reference variable</span><br />
b． <span style="color: #0000ff">调用Clone()方法的对象所属的类(Class)必须implements Clonable接口，否则在调用Clone方法的时候会抛出CloneNotSupportedException。<br />
</span></pre><img src ="http://www.blogjava.net/orangelizq/aggbug/153573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-10-17 14:52 <a href="http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]JAVA IO流</title><link>http://www.blogjava.net/orangelizq/archive/2007/10/01/150026.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 01 Oct 2007 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/10/01/150026.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/150026.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/10/01/150026.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/150026.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/150026.html</trackback:ping><description><![CDATA[<p>Input和Output</p>
<p>1. stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out stream）都包括两种类型：</p>
<p>1.1 以字节为导向的stream<br />
以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br />
<span style="color: #0000ff">Input stream：<br />
</span>1) ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br />
2) StringBufferInputStream：把一个String对象作为InputStream<br />
3) FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br />
4) PipedInputStream：实现了pipe的概念，主要在线程中使用<br />
5) SequenceInputStream：把多个InputStream合并为一个InputStream<br />
<span style="color: #0000ff">Out stream：</span><br />
1) ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br />
2) FileOutputStream：把信息存入文件中<br />
3) PipedOutputStream：实现了pipe的概念，主要在线程中使用<br />
4) SequenceOutputStream：把多个OutStream合并为一个OutStream</p>
<p>1.2 以Unicode字符为导向的stream<br />
以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型：<br />
<span style="color: #0000ff">Input stream</span><br />
1) CharArrayReader：与ByteArrayInputStream对应<br />
2) StringReader：与StringBufferInputStream对应<br />
3) FileReader：与FileInputStream对应<br />
4) PipedReader：与PipedInputStream对应<br />
<span style="color: #0000ff">Out stream<br />
</span>1) CharArrayWrite：与ByteArrayOutputStream对应<br />
2) StringWrite：无与之对应的以字节为导向的stream<br />
3) FileWrite：与FileOutputStream对应<br />
4) PipedWrite：与PipedOutputStream对应</p>
<p>以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，只是在操作时的导向不同。如CharArrayReader：和</p>
<p>ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是后者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。</p>
<p>1.3 两种不现导向的stream之间的转换<br />
InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。</p>
<p><br />
2. stream添加属性</p>
<p>2.1 &#8220;为stream添加属性&#8221;的作用</p>
<p>运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。</p>
<p>如果我们要往一个文件中写入数据，我们可以这样操作：<br />
FileOutStream fs = new FileOutStream(&#8220;test.txt&#8221;);<br />
然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现&#8220;先把要写入文件的数据先缓存到内存中，再把缓存中的数据写入文件中&#8221;的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类，为FileOutStream添加我们所需要的功能。</p>
<p>2.2 <span style="color: #0000ff">FilterInputStream</span>的各种类型</p>
<p>2.2.1 用于封装以字节为导向的InputStream<br />
1) DataInputStream：从stream中读取基本类型（int、char等）数据。<br />
2) BufferedInputStream：使用缓冲区<br />
3) LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br />
4) PushbackInputStream：很少用到，一般用于编译器开发</p>
<p>2.2.2 用于封装以字符为导向的InputStream<br />
1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br />
2) BufferedReader：与BufferedInputStream对应<br />
3) LineNumberReader：与LineNumberInputStream对应<br />
4) PushBackReader：与PushbackInputStream对应</p>
<p>2.3 <span style="color: #0000ff">FilterOutStream</span>的各种类型</p>
<p>2.3.1 用于封装以字节为导向的OutputStream<br />
1) DataIOutStream：往stream中输出基本类型（int、char等）数据。<br />
2) BufferedOutStream：使用缓冲区<br />
3) PrintStream：产生格式化输出</p>
<p>2.3.2 用于封装以字符为导向的OutputStream<br />
1) BufferedWrite：与对应<br />
2) PrintWrite：与对应</p>
<p>3. <span style="color: #0000ff">RandomAccessFile</span></p>
<p>1) 可通过RandomAccessFile对象完成对文件的读写操作<br />
2) 在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br />
3) 可以直接跳到文件中指定的位置</p>
<p>4. I/O应用的一个例子</p>
<p>import java.io.*;<br />
public class TestIO{<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws IOException{<br />
//1.以行为单位从一个文件读取数据<br />
&nbsp;&nbsp;&nbsp; BufferedReader in =new BufferedReader(new FileReader(\"F:<br />
epalonTestIO.java\"));<br />
&nbsp;&nbsp;&nbsp; String s, s2 = new String();<br />
&nbsp;&nbsp;&nbsp; while((s = in.readLine()) != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s2 += s + "\\";<br />
&nbsp;&nbsp;&nbsp; in.close();</p>
<p>//1b. 接收键盘的输入<br />
&nbsp;&nbsp;&nbsp; BufferedReader stdin =new BufferedReader(new InputStreamReader(System.in));<br />
&nbsp;&nbsp;&nbsp; System.out.println(\"Enter a line:\");<br />
&nbsp;&nbsp;&nbsp; System.out.println(stdin.readLine());</p>
<p>//2. 从一个String对象中读取数据<br />
&nbsp;&nbsp;&nbsp; StringReader in2 = new StringReader(s2);<br />
&nbsp;&nbsp;&nbsp; int c;<br />
&nbsp;&nbsp;&nbsp; while((c = in2.read()) != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println((char)c);<br />
&nbsp;&nbsp;&nbsp; in2.close();</p>
<p>//3. 从内存取出格式化输入<br />
&nbsp;&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataInputStream in3 =new DataInputStream(new ByteArrayInputStream(s2.getBytes()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(true)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println((char)in3.readByte());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch(EOFException e){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(\"End of stream\");<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>//4. 输出到文件<br />
&nbsp;&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedReader in4 =new BufferedReader(new StringReader(s2)); //把s2当作输入对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrintWriter out1 =new PrintWriter(new BufferedWriter(new FileWriter(\"F:epalon TestIO.out\")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int lineCount = 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((s = in4.readLine()) != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out1.println(lineCount&nbsp; + \"：\" + s);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out1.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in4.close();<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch(EOFException ex){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(\"End of stream\");<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>//5. 数据的存储和恢复<br />
&nbsp;&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataOutputStream out2 = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(\"F:epalon Data.txt\")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out2.writeDouble(3.1415926);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out2.writeChars(" Thas was pi:writeChars ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out2.writeBytes( "Thas was pi:writeByte ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out2.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataInputStream in5 = new DataInputStream(new BufferedInputStream(new FileInputStream(\"F:epalon Data.txt\")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedReader in5br = new BufferedReader(new InputStreamReader(in5));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(in5.readDouble());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(in5br.readLine());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(in5br.readLine());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch(EOFException e){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(\"End of stream\");<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>//6. 通过RandomAccessFile操作文件<br />
&nbsp;&nbsp;&nbsp; RandomAccessFile rf = new RandomAccessFile(\"F:epalon rtest.dat\", \"rw\");<br />
&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rf.writeDouble(i*1.414);<br />
&nbsp;&nbsp;&nbsp; rf.close();</p>
<p>&nbsp;&nbsp;&nbsp; rf = new RandomAccessFile(\"F:epalon rtest.dat\", \"r\");<br />
&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(\"Value \" + i + \"：\" + rf.readDouble());<br />
&nbsp;&nbsp;&nbsp; rf.close();</p>
<p>&nbsp;&nbsp;&nbsp; rf = new RandomAccessFile(\"F:epalon rtest.dat\", \"rw\");<br />
&nbsp;&nbsp;&nbsp; rf.seek(5*8);<br />
&nbsp;&nbsp;&nbsp; rf.writeDouble(47.0001);<br />
&nbsp;&nbsp;&nbsp; rf.close();</p>
<p>&nbsp;&nbsp;&nbsp; rf = new RandomAccessFile(\"F:epalon rtest.dat\", \"r\");<br />
&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;10; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(\"Value \" + i + \"：\" + rf.readDouble());<br />
&nbsp;&nbsp;&nbsp; rf.close();<br />
&nbsp;&nbsp;&nbsp; }<br />
}</p>
<p>关于代码的解释（以区为单位）：<br />
1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称&#8220;缓存字节读取方式&#8221;）。<br />
1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。<br />
2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br />
4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。<br />
5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正确显示。也要以基本类型的形式进行读取。<br />
6区是通过RandomAccessFile类对文件进行操作。<br />
</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/150026.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-10-01 10:44 <a href="http://www.blogjava.net/orangelizq/archive/2007/10/01/150026.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]如何成为一个合格的java程序员</title><link>http://www.blogjava.net/orangelizq/archive/2007/09/30/149953.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 30 Sep 2007 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/09/30/149953.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/149953.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/09/30/149953.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/149953.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/149953.html</trackback:ping><description><![CDATA[<p>如何成为一个合格的java程序员<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1、语法：必须比较熟悉，在写代码的时候IDE的编辑器对某一行报错应该能够根据报错信息知道是什么样的语法错误并且知道任何修正。</p>
<p>　　2、命令：必须熟悉JDK带的一些常用命令及其常用选项，命令至少需要熟悉：appletviewer、HtmlConverter、jar、java、javac、javadoc、javap、javaw、native2ascii、serialver，如果这些命令你没有全部使用过，那么你对java实际上还很不了解。</p>
<p>　　3、工具：必须至少熟练使用一种IDE的开发工具，例如Eclipse、Netbeans、JBuilder、Jdeveloper、IDEA、JCreator或者Workshop，包括进行工程管理、常用选项的设置、插件的安装配置以及进行调试。</p>
<p>　　4、API：Java的核心API是非常庞大的，但是有一些内容笔者认为是必须熟悉的，否则不可能熟练的运用Java，包括：</p>
<p>　　 1）、java.lang包下的80％以上的类的功能的灵活运用。</p>
<p>　　 2）、java.util包下的80％以上的类的灵活运用，特别是集合类体系、规则表达式、zip、以及时间、随机数、属性、资源和Timer。</p>
<p>　　 3）、java.io包下的60％以上的类的使用，理解IO体系的基于管道模型的设计思路以及常用IO类的特性和使用场合。</p>
<p>　　 4）、java.math包下的100％的内容。</p>
<p>　　 5）、java.net包下的60％以上的内容，对各个类的功能比较熟悉。</p>
<p>　　 6）、java.text包下的60％以上的内容，特别是各种格式化类。</p>
<p>　　 7）、熟练运用JDBC。</p>
<p>　　 8）、java.security包下40％以上的内容，如果对于安全没有接触的话根本就不可能掌握java。</p>
<p>　　 9）、AWT的基本内容，包括各种组件事件、监听器、布局管理器、常用组件、打印。</p>
<p>　　 10）、Swing的基本内容，和AWT的要求类似。</p>
<p>　　 11）、XML处理，熟悉SAX、DOM以及JDOM的优缺点并且能够使用其中的一种完成XML的解析及内容处理。</p>
<p>　　5、测试：必须熟悉使用junit编写测试用例完成代码的自动测试。</p>
<p>　　6、管理：必须熟悉使用ant完成工程管理的常用任务，例如工程编译、生成javadoc、生成jar、版本控制、自动测试。</p>
<p>　　7、排错：应该可以根据异常信息比较快速的定位问题的原因和大致位置。</p>
<p>　　8、思想：必须掌握OOP的主要要求，这样使用Java开发的系统才能是真正的Java系统。</p>
<p>　　9、规范：编写的代码必须符合流行的编码规范，例如类名首字母大写，成员和方法名首字母小写，方法名的第一个单词一般是动词，包名全部小写等，这样程序的可读性才比较好。<br />
</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/149953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-09-30 17:01 <a href="http://www.blogjava.net/orangelizq/archive/2007/09/30/149953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>