﻿<?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/zlsunnan/category/5586.html</link><description>世界总是反反覆覆错错落落地飘去 来不及叹息 生活不是平平淡淡从从容容的东西 不能放弃</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 07:56:06 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 07:56:06 GMT</pubDate><ttl>60</ttl><item><title>Java集合概要及面试问题总结</title><link>http://www.blogjava.net/zlsunnan/archive/2006/07/23/59702.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Sun, 23 Jul 2006 13:12:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/07/23/59702.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/59702.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/07/23/59702.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/59702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/59702.html</trackback:ping><description><![CDATA[
		<div class="postcontent">
				<p>Java提供了数种持有对象的方式，包括语言内置的Array，还有就是utilities中提供的容器类(container classes)，又称群集类(collection classes)。集合在java中非常重要，在讨论之前，先来看几个面试中的经典问题。<br />1 Collection 和 Collections的区别。<br />2 List, Set, Map是否继承自Collection接口。<br />3 ArrayList和Vector的区别。<br />4 HashMap和Hashtable的区别。<br />篇尾有答案，我们开始正题。<br /><br />集合Collection接口<br />--Collection 是任何对象组，元素各自独立，通常拥有相同的套用规则。Set、List由它派生。<br />基本操作：增加元素add(Object obj); addAll(Collection c);<br />删除元素：remove(Object obj); removeAll(Collection c);<br />求交集：retainAll(Collection c);<br /><br />访问/遍历集合元素的好办法是使用Iterator接口(迭代器用于取代Enumeration)<br />Public interface Iterator{<br />Public Boolean hasNext(0;<br />Public Object next(0;<br />Public void remove(0;<br />}<br /><br />集set<br />--没有重复项目的集合<br />有三种特定类型的集可用<br />HashSet-基于散列表的集，加进散列表的元素要实现hashCode()方法<br />LinkedHashSet-对集迭代时，按增加顺序返回元素<br />TreeSet-基于（平衡）树的数据结构<br /><br />清单List<br />--位置性集合。加进清单的元素可以加在清单中特定位置或加到末尾<br />有两个特定版本<br />ArrayList(数组表)-类似于Vector，都用于缩放数组维护集合。区别：<br />一.同步性:Vector是线程安全的，也就是说是同步的，而ArrayList是线程序不安全的，不是同步的 <br />二.数据增长:当需要增长时,Vector默认增长为原来一培，而ArrayList却是原来的一半<br />LinkedList(链表)-是双向链表，每个节点都有两个指针指向上一节点和下一节点。<br />用在FIFO，用addList()加入元素 removeFirst()删除元素<br />用在FILO,用addFirst()/removeLast()<br />ListIterator提供双向遍历next() previous()，可删除、替换、增加元素<br /><br />映射表Map<br />--用于关键字/数值对，像个Dictionary<br />处理Map的三种集合<br />关键字集KeySet()<br />数值集value()<br />项目集enrySet()<br />四个具体版本<br />HashMap-散列表的通用映射表<br />LinkedHashMap-扩展HashMap，对返回集合迭代时，维护插入顺序<br />WeakHashMap-基于弱引用散列表的映射表，如果不保持映射表外的关键字的引用，则内存回收程序会回收它<br />TreeMap-基于平衡树的映射表 <br /><br />Collections类，用于同步集合，还能改变集合只读方式的类<br />e.g.:<br />Map mp=new HashMap()<br />mp=Collections.synchronizedMap(mp); //生成线程安全的映射表<br />mp=Collections.unmodifiableMap(mp); //生成只读映射表<br /><br />Comparable 自然顺序的排序类 Comparator 面向树的集合排序类<br /><br />容器分类学(Container taxonomy)<br />集合接口： Collection List Set;Map Iterator ListIterator。<br />抽象类： AbstractCollection AbstractList AbstractSet AbstractMap AbstractSequentiaList。<br /><br />老版本中的集合类型<br />Vector类<br />Vector，就是向量。一种异构的混合体，可以动态增加容量。对它的操作简要如下<br />比如我们有一个Vector: Vector myVec=new Vector(a_Array.length)<br />取得vector的长度:myVec.size();<br />赋值：set(int position,Object obj) / setElementAt(Object obj, int position) –不支持动态增长<br />add(Object obj )/ addElement(Object obj) 在Vector末尾加入对象<br />e.g.：myVec.add(new a_Array[0]);<br />取出元素：get(int position) / getElement(int position)<br /><br />Stack类<br />是Vector的子类。就是数据结构里讲滥了的堆栈（这个词可简称栈，不要混淆于heap-堆）。后进先出的存取方式。<br />Stack()构造空栈<br />Empty()叛空<br />Search()检查堆栈是否有元素<br />Peek()取得栈顶元素<br />Pop()弹栈<br />Push()入栈<br /><br />Enumeration接口<br />Dictionary类<br />字典。关键字/数值方式存取数据，如果映射没有此关键字，取回null。<br /><br />Hashtable类<br />是Dictionary结构的具体实现。<br /><br />面试题答案<br />1、Collection 和 Collections的区别。<br />Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。Collections是针对集合类的一个帮助类，他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作 <br />Collection是集合类的上级接口，即是java.util下的接口，它是各种集合结构的父接口。继承于它的接口主要有Set 和List。<br />2、List, Set, Map是否继承自Collection接口? List，Set是，Map不是<br />3、ArrayList和Vector的区别。<br />一.同步性:Vector是线程安全的，也就是说是同步的，而ArrayList是线程序不安全的，不是同步的 <br />二.数据增长:当需要增长时,Vector默认增长为原来一培，而ArrayList却是原来的一半<br />4、HashMap和Hashtable的区别 <br />一.历史原因:Hashtable是基于陈旧的Dictionary类的，HashMap是Java 1.2引进的Map接口的一个实现 <br />二.同步性:Hashtable是线程安全的，也就是说是同步的，而HashMap是线程序不安全的，不是同步的 <br />三.值：只有HashMap可以让你将空值作为一个表的条目的key或value <br /></p>
				<p id="TBPingURL">
						<br />补充有关集合类的几个面试题目如下:<br />1、Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别<br />答：Set里的元素是不能重复的，那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等<br />equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖，为的是当两个分离的对象的内容和类型相配的话，返回真值</p>
		</div>
<img src ="http://www.blogjava.net/zlsunnan/aggbug/59702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-07-23 21:12 <a href="http://www.blogjava.net/zlsunnan/archive/2006/07/23/59702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SynchronizedMap</title><link>http://www.blogjava.net/zlsunnan/archive/2006/07/02/56184.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Sun, 02 Jul 2006 11:28:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/07/02/56184.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/56184.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/07/02/56184.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/56184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/56184.html</trackback:ping><description><![CDATA[
		<blockquote>Doug Lea的<code>util.concurrent</code>包除了包含许多其他有用的并发构造块之外，还包含了一些主要集合类型<code>List</code>和<code>Map</code>的高性能的、线程安全的实现。Brian Goetz向您展示了用<code>ConcurrentHashMap</code>替换<code>Hashtable</code>或<code>synchronizedMap</code>，将有多少并发程序获益。</blockquote>
		<p>在Java类库中出现的第一个关联的集合类是<code>Hashtable</code>，它是JDK 1.0的一部分。<code>Hashtable</code>提供了一种易于使用的、线程安全的、关联的map功能，这当然也是方便的。然而，线程安全性是凭代价换来的——<code>Hashtable</code>的所有方法都是同步的。 此时，无竞争的同步会导致可观的性能代价。<code>Hashtable</code>的后继者<code>HashMap</code>是作为JDK1.2中的集合框架的一部分出现的，它通过提供一个不同步的基类和一个同步的包装器<code>Collections.synchronizedMap</code>，解决了线程安全性问题。 通过将基本的功能从线程安全性中分离开来，<code>Collections.synchronizedMap</code>允许需要同步的用户可以拥有同步，而不需要同步的用户则不必为同步付出代价。</p>
		<p>
				<code>Hashtable</code> 和<code> synchronizedMap</code>所采取的获得同步的简单方法（同步<code>Hashtable</code>中或者同步的<code>Map</code>包装器对象中的每个方法）有两个主要的不足。首先，这种方法对于可伸缩性是一种障碍，因为一次只能有一个线程可以访问hash表。 同时，这样仍不足以提供真正的线程安全性，许多公用的混合操作仍然需要额外的同步。虽然诸如<code>get()</code> 和<code> put()</code>之类的简单操作可以在不需要额外同步的情况下安全地完成，但还是有一些公用的操作序列 ，例如迭代或者put-if-absent（空则放入），需要外部的同步，以避免数据争用。</p>
		<p>
				<b>
						<span class="atitle2">有条件的线程安全性</span>
				</b>
				<br />同步的集合包装器<code> synchronizedMap</code> 和<code> synchronizedList</code>，有时也被称作<em>有条件地线程安全</em>——所有 单个的操作都是线程安全的，但是多个操作组成的操作序列却可能导致数据争用，因为在操作序列中控制流取决于前面操作的结果。 <b>清单1</b>中第一片段展示了公用的put-if-absent语句块——如果一个条目不在<code>Map</code>中，那么添加这个条目。不幸的是， 在<code>containsKey()</code>方法返回到<code>put()</code> 方法被调用这段时间内，可能会有另一个线程也插入一个带有相同键的值。如果您想确保只有一次插入，您需要用一个对<code>Map m</code>进行同步的同步块将这一对语句包装起来。</p>
		<p>
				<b>清单1</b>中其他的例子与迭代有关。在第一个例子中，<code>List.size()</code> 的结果在循环的执行期间可能会变得无效，因为另一个线程可以从这个列表中删除条目。如果时机不得当，在刚好进入循环的最后一次迭代之后有一个条目被另一个线程删除 了，则<code>List.get()</code>将返回<code>null</code>，而<code>doSomething()</code> 则很可能会抛出一个<code>NullPointerException</code>异常。那么，采取什么措施才能避免这种情况呢？如果当您正在迭代一个<code><code>List </code></code>时另一个线程也 可能正在访问这个 <code>List</code>，那么在进行迭代时您必须使用一个<code>synchronized </code>块将这个<code>List </code>包装起来， 在<code>List</code> 1 上同步，从而锁住整个<code>List</code>。这样做虽然解决了数据争用问题，但是在并发性方面付出了更多的代价，因为在迭代期间锁住整个<code>List</code>会阻塞其他线程，使它们在很长一段时间内不能访问这个列表。</p>
		<p>集合框架引入了迭代器，用于遍历一个列表或者其他集合，从而优化了对一个集合中的元素进行迭代的过程。然而，在<code>java.util</code> 集合类中实现的迭代器极易崩溃，也就是说，如果在一个线程正在通过一个<code>Iterator</code>遍历集合时，另一个线程也来修改这个 集合，那么接下来的<code>Iterator.hasNext()</code> 或<code> Iterator.next()</code>调用将抛出<code>ConcurrentModificationException</code>异常。就拿 刚才这个例子来讲，如果想要防止出现<code>ConcurrentModificationException</code>异常，那么当您正在进行迭代时，您必须 使用一个在 <code>List l</code>上同步的<code>synchronized</code>块将该 <code>List</code> 包装起来，从而锁住整个 <code>List</code>。（或者，您也可以调用<code>List.toArray()</code>，在 不同步的情况下对数组进行迭代，但是如果列表比较大的话这样做代价很高）。</p>
		<b>
				<b>清单 1. 同步的map中的公用竞争条件</b>
		</b>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code>
    Map m = Collections.synchronizedMap(new HashMap());
    List l = Collections.synchronizedList(new ArrayList());

    // put-if-absent idiom -- contains a race condition
    // may require external synchronization
    if (!map.containsKey(key))
      map.put(key, value);

    // ad-hoc iteration -- contains race conditions
    // may require external synchronization
    for (int i=0; i&lt;list.size(); i++) {
      doSomething(list.get(i));
    }

    // normal iteration -- can throw ConcurrentModificationException
    // may require external synchronization
    for (Iterator i=list.iterator(); i.hasNext(); ) {
      doSomething(i.next());
    }
</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<b>
						<span class="atitle3">信任的错觉</span>
				</b>
				<br />
				<code>synchronizedList</code> 和<code> synchronizedMap</code>提供的有条件的线程安全性也带来了一个隐患<code>——</code>开发者会假设，因为这些集合都是同步的，所以它们都是线程安全的，这样一来他们对于正确地同步混合操作这件事就会疏忽。其结果是尽管表面上这些程序在负载较轻的时候能够正常工作，但是一旦负载较重，它们就会开始抛出<code>NullPointerException</code> 或<code> ConcurrentModificationException</code><code>。</code></p>
		<p>
				<b>
						<span class="atitle2">可伸缩性问题</span>
				</b>
				<br />可伸缩性指的是一个应用程序在工作负载和可用处理资源增加时其吞吐量的表现情况。一个可伸缩的程序能够通过使用更多的处理器、内存或者I/O带宽来相应地处理更大的工作负载。锁住某个共享的资源以获得独占式的访问这种做法会形成可伸缩性瓶颈——它使其他线程不能访问那个资源，即使有空闲的处理器可以调用那些线程也无济于事。为了取得可伸缩性，我们必须消除或者减少我们对独占式资源锁的依赖。</p>
		<p>同步的集合包装器以及早期的<code>Hashtable</code> 和<code> Vector</code>类带来的更大的问题是，它们在单个的锁 上进行同步。这意味着一次只有一个线程可以访问集合，如果有一个线程正在读一个<code>Map</code>，那么所有其他想要读或者写这个<code>Map</code>的线程就必须等待。最常见的<code>Map</code>操作，<code>get()</code> 和<code> put()</code>，可能比表面上要进行更多的处理——当遍历一个hash表的bucket以期找到某一特定的key时，<code>get()</code>必须对大量的候选bucket调用<code>Object.equals()</code>。如果key<font color="#000000">类</font>所使用的<code>hashCode()</code>函数不能将value均匀地分布在整个hash表范围内，或者存在大量的hash冲突，那么某些bucket链就会比其他的链长很多，而遍历一个长的hash链以及对该hash链上一定百分比的元素调用 <code>equals()</code>是一件很慢的事情。在上述条件下，调用 <code>get()</code> 和<code> put()</code> 的代价高的问题不仅仅是指访问过程的缓慢，而且，当有线程正在遍历那个hash链时，所有其他线程都被锁在外面，不能访问这个<code>Map</code>。</p>
		<p>（哈希表根据一个叫做hash的数字关键字（key）将对象存储在bucket中。hash value是从对象中的值计算得来的一个数字。每个不同的hash value都会创建一个新的bucket。要查找一个对象，您只需要计算这个对象的hash value并搜索相应的bucket就行了。通过快速地找到相应的bucket，就可以减少您需要搜索的对象数量了。译者注）</p>
		<p>
				<code>get()</code>执行起来可能会占用大量的时间，而在某些情况下，前面已经作了讨论的有条件的线程安全性问题会让这个问题变得还要糟糕得多。<a href="http://www.mywelt.net/#listing1">清单1</a> 中演示的争用条件常常使得对单个集合的锁在单个操作执行完毕之后还必须继续保持一段较长的时间。如果您要在整个迭代期间都保持对集合的锁，那么其他的线程就会在锁外停留很长的一段时间，等待解锁。</p>
		<p>
				<b>
						<span class="atitle3">实例：一个简单的cache</span>
				</b>
				<br />
				<code>Map</code>在服务器应用中最常见的应用之一就是实现一个<code>cache。</code>服务器应用可能需要缓存文件内容、生成的页面、数据库查询的结果、与经过解析的XML文件相关的DOM树，以及许多其他类型的数据。cache的主要用途是重用前一次处理得出的结果 以减少服务时间和增加吞吐量。cache工作负载的一个典型的特征就是检索大大多于更新，因此（理想情况下）cache能够提供非常好的<code>get()</code>性能。不过，使用会 妨碍性能的cache还不如完全不用cache。</p>
		<p>如果使用<code> synchronizedMap</code> 来实现一个cache，那么您就在您的应用程序中引入了一个潜在的可伸缩性瓶颈。因为一次只有一个线程可以访问<code>Map</code>，这 些线程包括那些要从<code>Map</code>中取出一个值的线程以及那些要将一个新的<code>(key, value)</code>对插入到该map中的线程。</p>
		<p>
				<b>
						<span class="atitle3">减小锁粒度</span>
				</b>
				<br />提高<code>HashMap</code>的并发性同时还提供线程安全性的一种方法是废除对整个表使用一个锁的方式，而采用对hash表的每个bucket都使用一个锁的方式（或者，更常见的是，使用一个锁池，每个锁负责保护几个bucket） 。这意味着多个线程可以同时地访问一个<code>Map</code>的不同部分，而不必争用单个的集合范围的锁。这种方法能够直接提高插入、检索以及移除操作的可伸缩性。不幸的是，这种并发性是以一定的代价换来的——这使得对整个 集合进行操作的一些方法（例如 <code>size()</code> 或<code> isEmpty()</code>）的实现更加困难，因为这些方法要求一次获得许多的锁，并且还存在返回不正确的结果的风险。然而，对于某些情况，例如实现cache，这样做是一个很好的折衷——因为检索和插入操作比较频繁，而 <code>size()</code> 和<code> isEmpty()</code>操作则少得多。</p>
		<p>
				<b>
						<span class="atitle2">ConcurrentHashMap</span>
				</b>
				<br />
				<code>util.concurrent</code> 包中的<code>ConcurrentHashMap</code>类（也将出现在JDK 1.5中的<code>java.util.concurrent</code>包中）是对<code>Map</code>的线程安全的实现，比起<code>synchronizedMap</code>来，它提供了好得多的并发性。多个读操作几乎总可以并发地执行，同时进行的读和写操作通常也能并发地执行，而同时进行的写操作仍然可以不时地并发进行（相关的类也提供了类似的多个读线程的并发性，但是，只允许有一个活动的写线程）<code>。ConcurrentHashMap</code>被设计用来优化检索操作；实际上，成功的 <code>get()</code> 操作完成之后通常根本不会有锁着的资源。要在不使用锁的情况下取得线程安全性需要一定的技巧性，并且需要对Java内存模型（Java Memory Model）的细节有深入的理解。<code>ConcurrentHashMap</code>实现，加上<code>util.concurrent</code>包的其他部分，已经被研究正确性和线程安全性的并发专家所正视。在下个月的文章中，我们将看看<code>ConcurrentHashMap</code>的实现的细节。</p>
		<p>
				<code>ConcurrentHashMap</code> 通过稍微地松弛它对调用者的承诺而获得了更高的并发性。检索操作将可以返回由最近完成的插入操作所插入的值，也可以返回在步调上是并发的插入操作所添加的值（但是决不会返回一个没有意义的结果）。由<code>ConcurrentHashMap.iterator()</code>返回的<code>Iterators</code>将每次最多返回一个元素，并且决不会抛出<code>ConcurrentModificationException</code>异常，但是可能会也可能不会反映在该迭代器被构建之后发生的插入操作或者移除操作。在对 集合进行迭代时，不需要表范围的锁就能提供线程安全性。在任何不依赖于锁整个表来防止更新的应用程序中，可以使用<code>ConcurrentHashMap</code>来替代<code>synchronizedMap</code>或<code>Hashtable</code>。</p>
		<p>上述改进使得<code>ConcurrentHashMap</code>能够提供比<code>Hashtable</code>高得多的可伸缩性，而且，对于很多类型的公用案例（比如共享的cache）来说，还不用损失其效率。</p>
		<p>
				<b>
						<span class="atitle3">好了多少？</span>
				</b>
				<br />表 1对<code>Hashtable</code> 和<code> ConcurrentHashMap</code>的可伸缩性进行了粗略的比较。在每次运行过程中，<i>n</i> 个线程并发地执行一个死循环，在这个死循环中这些线程从一个<code>Hashtable</code> 或者 <code>ConcurrentHashMap</code>中检索随机的key value，发现在执行<code>put()</code>操作时有80%的检索失败率，在执行操作时有1%的检索成功率。测试所在的平台是一个双处理器的Xeon系统，操作系统是Linux。数据显示了10,000,000次迭代以毫秒计的运行时间，这个数据是在将对<code>ConcurrentHashMap的</code>操作标准化为一个线程的情况下进行统计的。您可以看到，当线程增加到多个时，<code>ConcurrentHashMap</code>的性能仍然保持上升趋势，而<code>Hashtable</code>的性能则随着争用锁的情况的出现而立即降了下来。</p>
		<p>比起通常情况下的服务器应用，这次测试中线程的数量看上去有点少。然而，因为每个线程都在不停地对表进行操作，所以这与实际环境下使用这个表的更多数量的线程的争用情况基本等同。</p>
		<p>
				<b>表 1.Hashtable 与 ConcurrentHashMap在可伸缩性方面的比较</b>
		</p>
		<table cellspacing="0" cellpadding="3" border="1">
				<tbody>
						<tr valign="top">
								<td>
										<b>线程数</b>
								</td>
								<td>
										<b>ConcurrentHashMap</b>
								</td>
								<td>
										<b>Hashtable</b>
								</td>
						</tr>
						<tr valign="top">
								<td>1</td>
								<td>1.00</td>
								<td>1.03</td>
						</tr>
						<tr valign="top">
								<td>2</td>
								<td>2.59</td>
								<td>32.40</td>
						</tr>
						<tr valign="top">
								<td>4</td>
								<td>5.58</td>
								<td>78.23</td>
						</tr>
						<tr valign="top">
								<td>8</td>
								<td>13.21</td>
								<td>163.48</td>
						</tr>
						<tr valign="top">
								<td>16</td>
								<td>27.58</td>
								<td>341.21</td>
						</tr>
						<tr valign="top">
								<td>32</td>
								<td>57.27</td>
								<td>778.41</td>
						</tr>
				</tbody>
		</table>
		<p>
		</p>
		<p>
				<b>
						<span class="atitle2">CopyOnWriteArrayList</span>
				</b>
				<br />在那些遍历操作大大地多于插入或移除操作的并发应用程序中，一般用<code>CopyOnWriteArrayList</code>类替代<code>ArrayList</code>。如果是用于存放一个侦听器（listener）列表，例如在AWT或Swing应用程序中，或者在常见的JavaBean中，那么这种情况很常见（相关的<code>CopyOnWriteArraySet</code>使用一个<code>CopyOnWriteArrayList</code>来实现<code>Set</code>接口） 。</p>
		<p>如果您正在使用一个普通的<code>ArrayList</code>来存放一个侦听器列表，那么只要该列表是可变的，而且可能要被多个线程访问，您 就必须要么在对其进行迭代操作期间，要么在迭代前进行的克隆操作期间，锁定整个列表，这两种做法的开销都很大。当对列表执行会引起列表发生变化的操作时，<code>CopyOnWriteArrayList</code>并不是为列表创建一个全新的副本，它的迭代器肯定能够返回在迭代器被创建时列表的状态，而不会抛出<code>ConcurrentModificationException</code>。在对列表进行迭代之前不必克隆列表或者在迭代期间锁 定列表，因为迭代器所看到的列表的副本是不变的。换句话说，<code>CopyOnWriteArrayList</code>含有对一个不可变数组的一个可变的引用，因此，只要保留好那个引用，您就可以获得不可变的线程安全性的好处，而且不用锁 定列表。</p>
		<p>
				<b>
						<span class="atitle2">结束语</span>
				</b>
				<br />同步的集合类<code>Hashtable</code> 和<code> Vector</code>，以及同步的包装器类 <code>Collections.synchronizedMap</code> 和<code> Collections.synchronizedList</code>，为<code>Map</code> 和<code> List</code>提供了基本的有条件的线程安全的实现。然而，某些因素使得它们并不适用于具有高度并发性的应用程序中——它们的 集合范围的单锁特性对于可伸缩性来说是一个障碍，而且，很多时候还必须在一段较长的时间内锁定一个集合，以防止出现<code>ConcurrentModificationException</code>s异常。 <code>ConcurrentHashMap</code> 和<code> CopyOnWriteArrayList</code>实现提供了更高的并发性，同时还保住了线程安全性，只不过在对其调用者的承诺上打了点折扣。<code>ConcurrentHashMap</code> 和<code> CopyOnWriteArrayList</code>并不是在您使用<code>HashMap</code> 或<code> ArrayList</code>的任何地方都一定有用，但是它们是设计用来优化某些特定的公用解决方案的。许多并发应用程序将从对它们的使用中获得好处。</p>
<img src ="http://www.blogjava.net/zlsunnan/aggbug/56184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-07-02 19:28 <a href="http://www.blogjava.net/zlsunnan/archive/2006/07/02/56184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java类型转换的一个工具类</title><link>http://www.blogjava.net/zlsunnan/archive/2006/06/08/51551.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Thu, 08 Jun 2006 15:07:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/06/08/51551.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/51551.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/06/08/51551.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/51551.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/51551.html</trackback:ping><description><![CDATA[我只把自己常用的数据类型互相转换做成了一个类，大家也可以根据自己的需要把其他的各种情况中用到的转换方法补充进来使其更实用更强大
<div align="center"><script type="text/javascript"><!--
				google_ad_client = "pub-5535238706151070";
				google_ad_width = 468;
				google_ad_height = 60;
				google_ad_format = "468x60_as";
				google_ad_type = "text_image";
				google_ad_channel ="";
				google_color_border = "ffffff";
				google_color_bg = "ffffff";
				google_color_link = "990000";
				google_color_url = "008000";
				google_color_text = "000000";
				//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script></div><p></p><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" fixed="" table-layout:=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />/* <br />* Created on 2005-6-6 <br />* Made In GamVan <br />*/ <br />package com.gamvan.tools; <br />public class TypeChange { <br /><br /><br />public static String nullOfString(String str){ <br />if(str==null){ <br />str = ""; <br />} <br />return str; <br />} <br /><br />public static byte stringToByte(String str){ <br />byte b = 0; <br />if(str!=null){ <br />try{ <br />b = Byte.parseByte(str); <br />}catch(Exception e){ <br /><br />} <br />} <br />return b; <br />} <br /><br />public static boolean stringToBoolean(String str){ <br />if(str==null){ <br />return false; <br />}else{ <br />if(str.equals("1")){ <br />return true; <br />}else if(str.equals("0")){ <br />return false; <br />}else{ <br />try{ <br />return Boolean.parseBoolean(str); <br />}catch(Exception e){ <br />return false; <br />} <br />} <br />} <br />} <br /><br />public static int stringToInt(String str){ <br />int i=0; <br />if(str!=null){ <br />try{ <br />i = Integer.parseInt(str.trim()); <br />}catch(Exception e){ <br />i = 0; <br />} <br /><br />}else{ <br />i = 0; <br />} <br />return i; <br />} <br />public static short stringToShort(String str){ <br />short i=0; <br />if(str!=null){ <br />try{ <br />i = Short.parseShort(str.trim()); <br />}catch(Exception e){ <br />i = 0; <br />} <br />}else{ <br />i = 0; <br />} <br />return i; <br />} <br /><br /><br />public static double stringToDouble(String str){ <br />double i=0; <br />if(str!=null){ <br />try{ <br />i = Double.parseDouble(str.trim()); <br />}catch(Exception e){ <br />i = 0; <br />} <br />}else{ <br />i = 0; <br />} <br />return i; <br />} <br /><br />public static String intToString(int i){ <br />String str = ""; <br />try{ <br />str = String.valueOf(i); <br />}catch(Exception e){ <br />str = ""; <br />} <br />return str; <br />} <br /><br /><br />public static long doubleToLong(double d){ <br />long lo=0; <br />try{ <br />//double转换成long前要过滤掉double类型小数点后数据 <br />lo = Long.parseLong(String.valueOf(d).substring(0,String.valueOf(d).lastIndexOf("."))); <br />}catch(Exception e){ <br />lo=0; <br />} <br />return lo; <br />} <br /><br />public static int doubleToInt(double d){ <br />int i=0; <br />try{ <br />//double转换成long前要过滤掉double类型小数点后数据 <br />i = Integer.parseInt(String.valueOf(d).substring(0,String.valueOf(d).lastIndexOf("."))); <br />}catch(Exception e){ <br />i=0; <br />} <br />return i; <br />} <br /><br />public static double longToDouble(long d){ <br />double lo=0; <br />try{ <br />lo = Double.parseDouble(String.valueOf(d)); <br />}catch(Exception e){ <br />lo=0; <br />} <br />return lo; <br />} <br /><br />public static int longToInt(long d){ <br />int lo=0; <br />try{ <br />lo = Integer.parseInt(String.valueOf(d)); <br />}catch(Exception e){ <br />lo=0; <br />} <br />return lo; <br />} <br />public static long stringToLong(String str) { <br />Long li = new Long(0); <br />try{ <br />li = Long.valueOf(str); <br />}catch(Exception e){ <br />//li = new Long(0); <br />} <br />return li.longValue(); <br />} <br />public static String longToString(long li) { <br />String str = ""; <br />try{ <br />str = String.valueOf(li); <br />}catch(Exception e){ <br /><br />} <br />return str; <br />} <br /><br />}</td></tr></tbody></table><p>相信大家都能看懂，这里就不解释了，关于使用我举个例子字符串转换成Int类型  如下</p><p></p><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" fixed="" table-layout:=""><tbody><tr><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />int i = TypeChange.stringToInt("213324");</td></tr></tbody></table><img src ="http://www.blogjava.net/zlsunnan/aggbug/51551.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-06-08 23:07 <a href="http://www.blogjava.net/zlsunnan/archive/2006/06/08/51551.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>充分利用java.util.Collections类 </title><link>http://www.blogjava.net/zlsunnan/archive/2006/05/24/47915.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Wed, 24 May 2006 15:30:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/05/24/47915.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/47915.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/05/24/47915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/47915.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/47915.html</trackback:ping><description><![CDATA[java.util.Collections类包含很多有用的方法，可以使程序员的工作变得更加容易，但是这些方法通常都没有被充分地利用。这一篇文章将通过一些方法的范例化，使读者了解这些方法带来的方便。
<table cellspacing="0" cellpadding="0" width="0" align="right" border="0"><tbody><tr><td align="middle"><img src="http://www.zdnet.com.cn/ads/image/advertisement_e1.gif" /></td></tr><tr><td><!--start banner ad--><!--ba--><script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=410882803?"></script><a href="http://ad.cn.doubleclick.net/click;h=v5|33ef|0|0|%2a|e;44306;0-0;0;6694709;31-1|1;0|0|0;;~sscs=%3f" target="_top"><img alt="Click here to find out more!" src="http://m.cn.2mdn.net/viewad/817-grey.gif" border="0" /></a><noscript><a href="http://ad.cn.doubleclick.net/jump/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=410882803?"><img src="http://ad.cn.doubleclick.net/ad/messagingplus.zdnet.com.cn/developer/code;sz=1x1;ord=410882803?" border="0" /></a></noscript><!--end banner ad--></td></tr></tbody></table><p>Javadoc给出Collections类最完整的描述：“这一个类包含可以操作或返回集合的专用静态类。”</p><p>使用copy方法可以将一个java.util.List复制到其他：</p><p><font color="#0066ff">Collections.copy(newList, sourceList);</font></p><p>如果你需要使一个列表随机化，可以调用shuffle方法：</p><p><font color="#0066ff">shuffle(list);</font></p><p>这一方法可以记录列表中的内容。如果你想返回随机列表的来源处，可以调用它的sibling方法：</p><p><font color="#0066ff">shuffle(List list, Random random) </font></p><p>如果你需要建立一个不能更改的集合，可以使用<i>unmodifiableCollection</i><i>(Collection c)</i>方法。这一方法包含sibling方法，sibling方法可以处理特定类型的Set, List, 和Map。对于Set和Map，你可以通过使用一些sorted方法来获得结果对象的排序。</p><p><font color="#0066ff">List newList = Collections.unmodifiableList(myList); </font></p><p>如果你是在编写需要Enumeration对象操作方面的代码，可以充分利用enumeration方法：</p><p><font color="#0066ff">Enumeration e = Collections.enumeration(collection);</font></p><p>当你想使用一个简单对象来代替多个对象的时候，请使用<i>fill(List list, Object obj)</i>方法。</p><p>其他的，当你需要具有相同对象的多个引用的列表时，可以使用<i>nCopies</i><i>(int n, Object o)</i>方法。</p><p>当你需要在其他列表中查找一个列表的位置时，请使用<i>indexOfSubList</i><i>(List source, List target)</i>或者<i>lastIndexOfSubList</i><i>(List source, List target)</i>方法。如果目标列表存在于其他列表中，这一方法将返回包含列表起始位置的索引。如果在列表中无法找到目标列表，这两种方法都返回-1。</p><p>如果你需要使一个列表的排序相反化(反序)，则可以调用reverse方法，这一方法可以使列表现有元素的序号重新排序。</p><p>正如你所看到的，collections类包含很多方法。也许你很少使用到某些方法，但你可以会经常使用到其中的一些。</p><p>同样，也存在三种空的集合域。EMPTY_LIST, EMPTY_MAP, 和EMPTY_SET可以帮助你避免不必要的对象生成，特别是当返回数据的空集合的时候。例如：</p><p><font color="#0066ff">public List getData(Criteria c) {<br />  ...<br />  <br />  if ( noData ) {<br />      return Collections.EMPTY_LIST;<br />  }<br />  <br />  <font color="#006600">// otherwise<br />  // gather and return data</font><br />  ... <br />} </font></p><p>你可以建立一个没有元素的列表，但是当可以避免建立对象的时候最好不要建立对象。</p><img src ="http://www.blogjava.net/zlsunnan/aggbug/47915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-05-24 23:30 <a href="http://www.blogjava.net/zlsunnan/archive/2006/05/24/47915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中对HashMap的深度分析与比较</title><link>http://www.blogjava.net/zlsunnan/archive/2006/03/09/34557.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Thu, 09 Mar 2006 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/03/09/34557.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/34557.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/03/09/34557.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/34557.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/34557.html</trackback:ping><description><![CDATA[在Java的世界里，无论类还是各种数据，其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题，于是就开始研究这方面的问题。找遍了大大小小的论坛，也把《Java 虚拟机规范》，《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》，和《Thinking in Java》翻了也找不到很好的答案，于是一气之下把JDK的 src 解压出来研究，扩然开朗，遂写此文，跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。 <BR>&nbsp;<BR>　　HashMap可谓JDK的一大实用工具，把各个Object映射起来，实现了“键－－值”对应的快速存取。但实际里面做了些什么呢？
<P>　　在这之前，先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量，其默认值是 16×0.75＝12； 这个很重要，对效率很一定影响！当存入HashMap的对象超过这个容量时，HashMap 就会重新构造存取表。这就是一个大问题，我后面慢慢介绍，反正，如果你已经知道你大概要存放多少个对象，最好设为该实际容量的能接受的数字。</P>
<P>　　两个关键的方法，put和get：</P>
<P>　　先有这样一个概念，HashMap是声明了 Map，Cloneable, Serializable 接口，和继承了 AbstractMap 类，里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现，当然还有一个很重要的继承了Map.Entry 的 Entry 内部类，由于大家都有源代码，大家有兴趣可以看看这部分，我主要想说明的是 Entry 内部类。它包含了hash，value，key 和next 这四个属性，很重要。put的源码如下</P>
<P>　　public Object put(Object key, Object value) {<BR>　　Object k = maskNull(key); </P>
<P>　　这个就是判断键值是否为空，并不很深奥，其实如果为空，它会返回一个static Object 作为键值，这就是为什么HashMap允许空键值的原因。</P>
<P>　　int hash = hash(k);<BR>　　int i = indexFor(hash, table.length); </P>
<P>　　这连续的两步就是 HashMap 最牛的地方！研究完我都汗颜了，其中 hash 就是通过 key 这个Object的 hashcode 进行 hash，然后通过 indexFor 获得在Object table的索引值。</P>
<P>　　table？？？不要惊讶，其实HashMap也神不到哪里去，它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法，我跟JDK的作者 Doug 联系过，他建议我看看《The art of programing vol3》可恨的是，我之前就一直在找，我都找不到，他这样一提，我就更加急了，可惜口袋空空啊！！！</P>
<P>　　不知道大家有没有留意 put 其实是一个有返回的方法，它会把相同键值的 put 覆盖掉并返回旧的值！如下方法彻底说明了 HashMap 的结构，其实就是一个表加上在相应位置的Entry的链表：</P>
<P style="BACKGROUND: #eeeeee">for (Entry e = table[i]; e != null; e = e.next) {<BR>　if (e.hash == hash &amp;&amp; eq(k, e.key)) {<BR>　　Object oldvalue = e.value;<BR>　　e.value = value; //把新的值赋予给对应键值。<BR>　　e.recordAccess(this); //空方法，留待实现<BR>　　return oldvalue; //返回相同键值的对应的旧的值。<BR>　}<BR>}<BR>modCount++; //结构性更改的次数<BR>addEntry(hash, k, value, i); //添加新元素，关键所在！<BR>return null; //没有相同的键值返回<BR>} </P>
<P>　　我们把关键的方法拿出来分析：</P>
<P>　　void addEntry(int hash, Object key, Object value, int bucketIndex) {<BR>　　table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);&nbsp; </P>
<P>　　因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引，如：key＝“33”和key＝Object g的hash都是－8901334，那它经过indexfor之后的索引一定都为i，这样在new的时候这个Entry的next就会指向这个原本的table[i]，再有下一个也如此，形成一个链表，和put的循环对定e.next获得旧的值。到这里，HashMap的结构，大家也十分明白了吧？</P>
<P>　　if (size++ &gt;= threshold) //这个threshold就是能实际容纳的量<BR>　　resize(2 * table.length); //超出这个容量就会将Object table重构 </P>
<P>　　所谓的重构也不神，就是建一个两倍大的table（我在别的论坛上看到有人说是两倍加1，把我骗了），然后再一个个indexfor进去！注意！！这就是效率！！如果你能让你的HashMap不需要重构那么多次，效率会大大提高！</P>
<P>　　说到这里也差不多了，get比put简单得多，大家，了解put，get也差不了多少了。对于collections我是认为，它是适合广泛的，当不完全适合特有的，如果大家的程序需要特殊的用途，自己写吧，其实很简单。（作者是这样跟我说的，他还建议我用LinkedHashMap,我看了源码以后发现，LinkHashMap其实就是继承HashMap的，然后override相应的方法，有兴趣的同人，自己looklook）建个 Object table，写相应的算法，就ok啦。</P>
<P>　　举个例子吧，像 Vector，list 啊什么的其实都很简单，最多就多了的同步的声明，其实如果要实现像Vector那种，插入，删除不多的，可以用一个Object table来实现，按索引存取，添加等。</P>
<P>　　如果插入，删除比较多的，可以建两个Object table，然后每个元素用含有next结构的，一个table存，如果要插入到i，但是i已经有元素，用next连起来，然后size＋＋，并在另一个table记录其位置。</P><img src ="http://www.blogjava.net/zlsunnan/aggbug/34557.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-03-09 22:25 <a href="http://www.blogjava.net/zlsunnan/archive/2006/03/09/34557.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java打包详解</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/28/25684.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Wed, 28 Dec 2005 03:01:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/28/25684.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/25684.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/28/25684.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/25684.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/25684.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom>兄弟，对java着迷吗，或者是为了自己的生计，不论怎样都欢迎你进入精彩java世界，welcome！可能你刚刚对每个人说：Hello World！也或者……ok！这已经足够了。那就让我们开始吧，开始这个魔幻世界的旅程： <BR><BR>jar文件听说过吗，没有？或者陌生！好，没关系，这就是我们的第一站：打包发布。 <BR><BR>为什么会有这个玩意呢，首先，这是jar的全称：JavaTM Archive (JAR) file，是的，就是java存档文件。这有点类似zip文件，想一想它是干什么的用的呢，压缩！？没错就是要压缩，将我们原先零散的东西放到一下，重新组织，所有这些目的只有一个：方便！好了，不用管他是怎么压缩的，我们的重点是哪些是我们要压缩的(输入)，还有压缩成了什么(输出)，进而将它发布(部署)。 <BR><BR>那我们的输入(要压缩的东西)主要是class文件，还有辅助的资源(这其中可能有图片，jsp文件，html文件等等)。Jar技术在jdk1.1版本中就已存在，在1.2中又有了增强。接下来说说jar的好处吧，这是官方的描述：安全，快速下载，压缩，猎取包，版本化包，可携。 <BR><BR>说了这么多，我们现在开始实施。 <BR><BR>先打开命令提示符(win2000或在运行筐里执行cmd命令，win98为DOS提示符)，输入jar –help,然后回车(如果你盘上已经有了jdk1.1或以上版本)，看到什么： <BR><BR>用法：jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... <BR><BR>选项： <BR><BR>-c 创建新的存档<BR>-t 列出存档内容的列表<BR>-x 展开存档中的命名的（或所有的〕文件<BR>-u 更新已存在的存档<BR>-v 生成详细输出到标准输出上<BR>-f 指定存档文件名<BR>-m 包含来自标明文件的标明信息<BR>-0 只存储方式；未用ZIP压缩格式<BR>-M 不产生所有项的清单（manifest〕文件<BR>-i 为指定的jar文件产生索引信息<BR>-C 改变到指定的目录，并且包含下列文件： <BR><BR>如果一个文件名是一个目录，它将被递归处理。 <BR><BR>清单（manifest〕文件名和存档文件名都需要被指定，按'm' 和 'f'标志指定的相同顺序。 <BR><BR>示例1：将两个class文件存档到一个名为 'classes.jar' 的存档文件中：<BR>jar cvf classes.jar Foo.class Bar.class <BR><BR>示例2：用一个存在的清单（manifest）文件 'mymanifest' 将 foo/ 目录下的所有文件存档到一个名为 'classes.jar' 的存档文件中：<BR>jar cvfm classes.jar mymanifest -C foo/ . <BR><BR>来个小例子试试看：<BR>我们只有一个HelloWorld，如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public  class  HelloWorld{
 public static void main(String[] args){
 System.out.println(“Hi, Hello World!”);
}
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>我将这个java文件存到C盘跟目录下，ok，接下来， <BR><BR>在先前打开的命令提示符下(跳转到C盘提示符下)，我们输入javac HelloWorld.java，然后继续输入：jar cvf hello.jar HelloWorld.class，回车后去你的C盘看看，多了什么，没错 hello.jar 。 <BR><BR>基本的步骤我们现在都知道了，你可以自己去尝试一下随着jar后面的参数的不同，结果有什么变化。 <BR><BR>紧接着我们看看如何运行我们的jar包。 <BR><BR>在进入正题之前，你要先打开我们刚刚做好的jar包看看，多了什么呢，META-INF目录？再看看里面是什么，还有一个MANIFEST.MF文件是不是？用文本编辑器(我这里是UltraEdit)打开它看看：<BR>Manifest-Version: 1.0<BR>Created-By: 1.4.2 (Sun Microsystems Inc.) <BR><BR>就是这样。这里我们对它进行修改，加一句：Main-Class: HelloWorld (在第三行)。这个就是我们之前写的那个类，也就是我们的入口类。也即，<BR>Manifest-Version: 1.0<BR>Created-By: 1.4.2 (Sun Microsystems Inc.)<BR>Main-Class: HelloWorld <BR><BR>接下来，我们在命令提示符里执行：<BR>jar umf MANIFEST.MF app.jar <BR><BR>这样我们使用了我们自己的MANIFEST.MF文件对原来默认的进行了更新。你不妨可以再进去看看是不是添上了Main-Class: HelloWorld这一句。 <BR><BR>Ok，这个最后的一步了，来验证我们做的一切，在命令提示符中输入：<BR>java -jar hello.jar(执行) <BR><BR>出现了什么，――Hi, Hello World! <BR><BR>我们再来看看jar文件在tomcat中发布，注意：在tomcat中我们就不能再用jar这种格式，而改war格式，它是专门用于web应用的，其实整个过程下来基本上和jar是类似的： <BR><BR>先准备我们要打包的资源。 <BR><BR>找到存放tomcat的webapps目录，进到其中，新建一个文件夹，这里命名为hello，再进去新建WEB-INF文件夹，再进去新建classes文件夹，此时我们也将我们唯一的servlet，HelloWorld.java放到这里，在与classes目录同级下建立一文件web.xml。Ok，目前我们初步建立了一个简单的web应用。 <BR><BR>这是HelloWorld.java： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
 public void doGet(HttpServletRequest req, HttpServletResponse res)
                              throws ServletException, IOException {
  res.setContentType("text/html");
  PrintWriter out = res.getWriter();
  out.println("&lt;HTML&gt;");
  out.println("&lt;HEAD&gt;&lt;TITLE&gt;Hello, World!&lt;/TITLE&gt;&lt;/HEAD&gt;");
  out.println("&lt;BODY&gt;");
  out.println("Hello, World!");
  out.println("&lt;/BODY&gt;&lt;/HTML&gt;");
 }
}//end here!</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>对它编译。下面是web.xml： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;!DOCTYPE web-app PUBLIC
  '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'
  'http://java.sun.com/j2ee/dtds/web-app_2_3.dtd&amp;#39;&gt;
&lt;web-app&gt;
  &lt;servlet&gt;
    &lt;servlet-name&gt;hello&lt;/servlet-name&gt;
    &lt;servlet-class&gt;HelloWorld&lt;/servlet-class&gt;
  &lt;/servlet&gt;
  &lt;servlet-mapping&gt;
 &lt;servlet-name&gt;hello&lt;/servlet-name&gt;
 &lt;url-pattern&gt;/HelloWorld&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
&lt;/web-app&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>开始压缩，形成war档： <BR><BR>在命令提示符下进到先前创制的hello目录下，执行 jar cvf hello.war * ，我们便得到hello.war。将它拷贝至webapps目录下，ok，来看最后一步，打开tomcat的目录conf中的server.xml，加入： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;Context path="/hello" docBase="hello.war" debug="0"
    reloadable="true"/&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>大功告成！运行它，启动tomcat，后在浏览器中输入http://localhost:8080/hello/HelloWorld，有了吗？ <BR><BR>最后，如果你想用ant来完成以上的打包活动，下面就告诉你： <BR><BR>对于jar来说。在build.xml中， <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;target name="jar"&gt;
  &lt;jar destfile="${app_home}/hello.jar"&gt;
   &lt;fileset dir="${dest}" includes="**"/&gt;
      &lt;!--fileset dir="${dest}" includes="**/action.properties"/--&gt;
    &lt;/jar&gt;
 &lt;/target&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>对于war， <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;war warfile="hello.war" webxml="./WEB-INF/web.xml"&gt;
    &lt;fileset dir="html"/&gt;
    &lt;lib dir="lib/"&gt;
        &lt;exclude name="oracle*.jar"/&gt;
    &lt;/lib&gt;
    &lt;classes dir="build/servlets"&gt;
         &lt;include name="**/*.class"/&gt;
  &lt;/classes&gt;
&lt;/war&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>好了，就这么多，希望对你有点帮助。：） <BR><BR>我上传了上面打过的两个包，hello.jar和hello.war。 <A href="http://www.matrix.org.cn/upload/forum/200441295728.rar" target=_blank><U><FONT color=#0000ff>『 点击下载 』</FONT></U></A> <BR><BR><A href="http://www.matrix.org.cn/upload/forum/200441295831.rar" target=_blank><U><FONT color=#0000ff>『 点击下载 』</FONT></U></A> <BR><BR>第一rar文件对应的是hello.jar,下载后将其名改为hello.jar<BR>第二rar文件对应hello.war，下载后改为hello.war。<BR>这是由于上传不了jar格式和war格式的文件，你只好照我上面说的去做了 ：） <BR><BR><B>补充：</B> <BR><BR>############ <BR><BR>jar基本操作： <BR><BR>############ <BR><BR>1. 创建jar文件 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>jar cf jar-file input-file(s)
c---want to Create a JAR file.
f---want the output to go to a file rather than to stdout.
eg: 1)jar cf myjar.jar query_maintain_insert.htm
    2)jar cvf myjar.jar query_maintain_insert.htm
      v---Produces verbose(详细的) output.
    3)jar cvf myjar.jar query_maintain_insert.htm mydirectory
    4)jar cv0f myjar.jar query_maintain_insert.htm mydirectory
      0---don't want the JAR file to be compressed.
    5)jar cmf MANIFEST.MF myjar.jar yahh.txt
      m---Used to include manifest information from an existing manifest file.
    6)jar cMf MANIFEST.MF myjar.jar yahh.txt
      M---the default manifest file should not be produced.
    7)jar cvf myjar.jar *
      *---create all contents in current directory.</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>2. 察看jar文件 <BR><BR>jar tf jar-file <BR><BR>t---want to view the Table of contents of the JAR file. <BR><BR>eg: 1)jar vft yahh.jar <BR><BR>v---Produces verbose(详细的) output. <BR><BR>3. 提取jar文件 <BR><BR>jar xf jar-file [archived-file(s)] <BR><BR>x---want to extract files from the JAR archive. <BR><BR>eg: 1)jar xf yahh.jar yahh.txt(仅提取文件yahh.txt) <BR><BR>2)jar xf yahh.jar alex/yahhalex.txt(仅提取目录alex下的文件yahhalex.txt) <BR><BR>3)jar xf yahh.jar(提取该jar包中的所有文件或目录) <BR><BR>4. 修改Manifest文件 <BR><BR>jar cmf manifest-addition jar-file input-file(s) <BR><BR>m---Used to include manifest information from an existing manifest file. <BR><BR>5. 更新jar文件 <BR><BR>jar uf jar-file input-file(s) <BR><BR>u---want to update an existing JAR file. <BR></FONT></SPAN><img src ="http://www.blogjava.net/zlsunnan/aggbug/25684.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-28 11:01 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/28/25684.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>