﻿<?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-TSH,SSH开发</title><link>http://www.blogjava.net/yc1354/</link><description /><language>zh-cn</language><lastBuildDate>Wed, 15 Apr 2026 11:58:51 GMT</lastBuildDate><pubDate>Wed, 15 Apr 2026 11:58:51 GMT</pubDate><ttl>60</ttl><item><title>同步synchornized方法与方法块</title><link>http://www.blogjava.net/yc1354/archive/2007/10/16/153179.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Tue, 16 Oct 2007 02:43:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/10/16/153179.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/153179.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/10/16/153179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/153179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/153179.html</trackback:ping><description><![CDATA[<p>打个比方：一个object就像一个大房子，大门永远打开。房子里有很多房间（也就是方法）。这些房间有上锁的（synchronized方法），和不上锁之分（普通方法）。房门口放着一把钥匙（key），这把钥匙可以打开所有上锁的房间。另外我把所有想调用该对象方法的线程比喻成想进入这房子某个房间的人。所有的东西就这么多了，下面我们看看这些东西之间如何作用的。</p>
<p>在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法，否则这个key还有啥意义。当然也就不会有我们的这个主题了。</p>
<p>一个人想进入某间上了锁的房间，他来到房子门口，看见钥匙在那儿（说明暂时还没有其他人要使用上锁的房间）。于是他走上去拿到了钥匙，并且按照自己的计划使用那些房间。注意一点，他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间，中间他也要把钥匙还回去，再取回来。</p>
<p>因此，普通情况下钥匙的使用原则是：&#8220;随用随借，用完即还。&#8221;</p>
<p>这时其他人可以不受限制的使用那些不上锁的房间，一个人用一间可以，两个人用一间也可以，没限制。但是如果当某个人想要进入上锁的房间，他就要跑到大门口去看看了。有钥匙当然拿了就走，没有的话，就只能等了。</p>
<p>要是很多人在等这把钥匙，等钥匙还回来以后，谁会优先得到钥匙？Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙，他中间还钥匙的时候如果还有其他人在等钥匙，那么没有任何保证这家伙能再次拿到。（JAVA规范在很多地方都明确说明不保证，象Thread.sleep()休息后多久会返回运行，相同优先权的线程那个首先被执行，当要访问对象的锁被释放后处于等待池的多个线程哪个会优先得到，等等。我想最终的决定权是在JVM，之所以不保证，就是因为JVM在做出上述决定的时候，绝不是简简单单根据一个条件来做出判断，而是根据很多条。而由于判断条件太多，如果说出来可能会影响JAVA的推广，也可能是因为知识产权保护的原因吧。SUN给了个不保证就混过去了。无可厚非。但我相信这些不确定，并非完全不确定。因为计算机这东西本身就是按指令运行的。即使看起来很随机的现象，其实都是有规律可寻。学过计算机的都知道，计算机里随机数的学名是伪随机数，是人运用一定的方法写出来的，看上去随机罢了。另外，或许是因为要想弄的确定太费事，也没多大意义，所以不确定就不确定了吧。）</p>
<p>再来看看同步代码块。和同步方法有小小的不同。</p>
<p>1.从尺寸上讲，同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。</p>
<p>2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁，你可以用本房的钥匙；你也可以指定用另一个房子的钥匙才能开，这样的话，你要跑到另一栋房子那儿把那个钥匙拿来，并用那个房子的钥匙来打开这个房子的带锁的屏风。</p>
<p>记住你获得的那另一栋房子的钥匙，并不影响其他人进入那栋房子没有锁的房间。</p>
<p>为什么要使用同步代码块呢？我想应该是这样的：首先对程序来讲同步的部分很影响运行效率，而一个方法通常是先创建一些局部变量，再对这些变量做一些操作，如运算，显示等等；而同步所覆盖的代码越多，对效率的影响就越严重。因此我们通常尽量缩小其影响范围。如何做？同步代码块。我们只把一个方法中该同步的地方同步，比如运算。</p>
<p>另外，同步代码块可以指定钥匙这一特点有个额外的好处，是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还，而是在退出同步代码块时才还。</p>
<p>还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后，继续使用另一间呢。用同步代码块吧。先创建另外一个线程，做一个同步代码块，把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙，你就可以一直保留到退出那个代码块。也就是说你甚至可以对本房内所有上锁的房间遍历，甚至再sleep(10*60*1000)，而房门口却还有1000个线程在等这把钥匙呢。很过瘾吧。</p>
<p>在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后，且没有完成同步的内容时，如果被强制sleep()了，那key还一直在它那儿。直到它再次运行，做完所有同步内容，才会归还key。记住，那家伙只是干活干累了，去休息一下，他并没干完他要干的事。为了避免别人进入那个房间把里面搞的一团糟，即使在睡觉的时候他也要把那唯一的钥匙戴在身上。</p>
<p>最后，也许有人会问，为什么要一把钥匙通开，而不是一个钥匙一个门呢？我想这纯粹是因为复杂性问题。一个钥匙一个门当然更安全，但是会牵扯好多问题。钥匙的产生，保管，获得，归还等等。其复杂性有可能随同步方法的增加呈几何级数增加，严重影响效率。</p>
<p>这也算是一个权衡的问题吧。为了增加一点点安全性，导致效率大大降低，是多么不可取啊。</p>
<p>摘自：http://www.54bk.com/more.asp?name=czp&amp;id=2097</p>
<p>一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时，一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。</p>
<p>二、然而，当一个线程访问object的一个synchronized(this)同步代码块时，另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。</p>
<p>三、尤其关键的是，当一个线程访问object的一个synchronized(this)同步代码块时，其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。</p>
<p>四、第三个例子同样适用其它同步代码块。也就是说，当一个线程访问object的一个synchronized(this)同步代码块时，它就获得了这个object的对象锁。结果，其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。</p>
<p>五、以上规则对其它对象锁同样适用.</p>
<p>举例说明：</p>
<p>一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时，一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。</p>
<p>package ths;</p>
<p>public class Thread1 implements Runnable { <br />
public void run() { <br />
synchronized(this) { <br />
for (int i = 0; i &lt; 5; i++) { <br />
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i); <br />
} <br />
} <br />
} <br />
public static void main(String[] args) { <br />
Thread1 t1 = new Thread1(); <br />
Thread ta = new Thread(t1, "A"); <br />
Thread tb = new Thread(t1, "B"); <br />
ta.start(); <br />
tb.start(); <br />
} <br />
}</p>
<p>结果：</p>
<p>A synchronized loop 0 <br />
A synchronized loop 1 <br />
A synchronized loop 2 <br />
A synchronized loop 3 <br />
A synchronized loop 4 <br />
B synchronized loop 0 <br />
B synchronized loop 1 <br />
B synchronized loop 2 <br />
B synchronized loop 3 <br />
B synchronized loop 4</p>
<p>二、然而，当一个线程访问object的一个synchronized(this)同步代码块时，另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。</p>
<p>package ths;</p>
<p>public class Thread2 { <br />
public void m4t1() { <br />
synchronized(this) { <br />
int i = 5; <br />
while( i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : " + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch (InterruptedException ie) { <br />
} <br />
} <br />
} <br />
} <br />
public void m4t2() { <br />
int i = 5; <br />
while( i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : " + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch (InterruptedException ie) { <br />
} <br />
} <br />
} <br />
public static void main(String[] args) { <br />
final Thread2 myt2 = new Thread2(); <br />
Thread t1 = new Thread( <br />
new Runnable() { <br />
public void run() { <br />
myt2.m4t1(); <br />
} <br />
}, "t1" <br />
); <br />
Thread t2 = new Thread( <br />
new Runnable() { <br />
public void run() { <br />
myt2.m4t2(); <br />
} <br />
}, "t2" <br />
); <br />
t1.start(); <br />
t2.start(); <br />
} <br />
}</p>
<p>结果：</p>
<p>t1 : 4 <br />
t2 : 4 <br />
t1 : 3 <br />
t2 : 3 <br />
t1 : 2 <br />
t2 : 2 <br />
t1 : 1 <br />
t2 : 1 <br />
t1 : 0 <br />
t2 : 0</p>
<p>三、尤其关键的是，当一个线程访问object的一个synchronized(this)同步代码块时，其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。</p>
<p>//修改Thread2.m4t2()方法：</p>
<p>public void m4t2() { <br />
synchronized(this) { <br />
int i = 5; <br />
while( i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : " + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch (InterruptedException ie) { <br />
} <br />
} <br />
}</p>
<p>}</p>
<p>结果：</p>
<p>t1 : 4 <br />
t1 : 3 <br />
t1 : 2 <br />
t1 : 1 <br />
t1 : 0 <br />
t2 : 4 <br />
t2 : 3 <br />
t2 : 2 <br />
t2 : 1 <br />
t2 : 0</p>
<p>四、第三个例子同样适用其它同步代码块。也就是说，当一个线程访问object的一个synchronized(this)同步代码块时，它就获得了这个object的对象锁。结果，其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。</p>
<p>//修改Thread2.m4t2()方法如下：</p>
<p>public synchronized void m4t2() { <br />
int i = 5; <br />
while( i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : " + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch (InterruptedException ie) { <br />
} <br />
} <br />
}</p>
<p>结果：</p>
<p>t1 : 4 <br />
t1 : 3 <br />
t1 : 2 <br />
t1 : 1 <br />
t1 : 0 <br />
t2 : 4 <br />
t2 : 3 <br />
t2 : 2 <br />
t2 : 1 <br />
t2 : 0</p>
<p>五、以上规则对其它对象锁同样适用:</p>
<p>package ths;</p>
<p>public class Thread3 { <br />
class Inner { <br />
private void m4t1() { <br />
int i = 5; <br />
while(i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch(InterruptedException ie) { <br />
} <br />
} <br />
} <br />
private void m4t2() { <br />
int i = 5; <br />
while(i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch(InterruptedException ie) { <br />
} <br />
} <br />
} <br />
} <br />
private void m4t1(Inner inner) { <br />
synchronized(inner) { //使用对象锁 <br />
inner.m4t1(); <br />
} <br />
} <br />
private void m4t2(Inner inner) { <br />
inner.m4t2(); <br />
} <br />
public static void main(String[] args) { <br />
final Thread3 myt3 = new Thread3(); <br />
final Inner inner = myt3.new Inner(); <br />
Thread t1 = new Thread( <br />
new Runnable() { <br />
public void run() { <br />
myt3.m4t1(inner); <br />
} <br />
}, "t1" <br />
); <br />
Thread t2 = new Thread( <br />
new Runnable() { <br />
public void run() { <br />
myt3.m4t2(inner); <br />
} <br />
}, "t2" <br />
); <br />
t1.start(); <br />
t2.start(); <br />
} <br />
}</p>
<p>结果：</p>
<p>尽管线程t1获得了对Inner的对象锁，但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。</p>
<p>t1 : Inner.m4t1()=4 <br />
t2 : Inner.m4t2()=4 <br />
t1 : Inner.m4t1()=3 <br />
t2 : Inner.m4t2()=3 <br />
t1 : Inner.m4t1()=2 <br />
t2 : Inner.m4t2()=2 <br />
t1 : Inner.m4t1()=1 <br />
t2 : Inner.m4t2()=1 <br />
t1 : Inner.m4t1()=0 <br />
t2 : Inner.m4t2()=0</p>
<p>现在在Inner.m4t2()前面加上synchronized：</p>
<p>private synchronized void m4t2() { <br />
int i = 5; <br />
while(i-- &gt; 0) { <br />
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); <br />
try { <br />
Thread.sleep(500); <br />
} catch(InterruptedException ie) { <br />
} <br />
} <br />
}</p>
<p>结果：</p>
<p>尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁，所以t2对Inner.m4t2()的访问也被阻塞，因为m4t2()是Inner中的一个同步方法。</p>
<p>t1 : Inner.m4t1()=4 <br />
t1 : Inner.m4t1()=3 <br />
t1 : Inner.m4t1()=2 <br />
t1 : Inner.m4t1()=1 <br />
t1 : Inner.m4t1()=0 <br />
t2 : Inner.m4t2()=4 <br />
t2 : Inner.m4t2()=3 <br />
t2 : Inner.m4t2()=2 <br />
t2 : Inner.m4t2()=1 <br />
t2 : Inner.m4t2()=0</p>
<img src ="http://www.blogjava.net/yc1354/aggbug/153179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-10-16 10:43 <a href="http://www.blogjava.net/yc1354/archive/2007/10/16/153179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux tar 命令</title><link>http://www.blogjava.net/yc1354/archive/2007/09/04/142689.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Tue, 04 Sep 2007 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/09/04/142689.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/142689.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/09/04/142689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/142689.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/142689.html</trackback:ping><description><![CDATA[Windows下最常见的压缩文件只有两种，.zip和.rar。可是Linux就不同了，有.Z、bz2、.gz、.tar、.tar.gz等众多的压 缩文件名，它们分别对应了各种压缩打包命令。要了解这些压缩打包命令的使用，首先要弄清两个概念：打包和压缩。打包是指将一大堆文件或目录什么的变成一个 总的文件，压缩则是将一个大的文件通过一些压缩算法变成一个小文件。Linux中的很多压缩命令只能针对一个文件进行压缩，当要压缩一大堆文件时，就得先 借助打包命令将这一大堆文件先打成一个包，然后再用压缩命令进行压缩。因此打包命令在Linux的应用中具有很重要的作用。
<p>Linux下最常用的打包命令就是tar，使用tar命令打包后，就可以用其它的命令来进行压缩了。tar命令的使用方法如下：</p>
<p>tar [-cxtzjvfpPN] 文件与目录<br />
参数说明：<br />
-c ：建立一个打包文件；<br />
-x ：解开一个打包文件；<br />
-t ：查看 tar包里面的文件；<br />
（特别注意，在选择参数时，c/x/t仅能存在一个，不可同时存在，因为不可能同时压缩与解压缩。）<br />
-z ：打包后用gzip压缩，生成.tar.gz文件；<br />
-j ：打包后用zip2压缩，生成.tar.bz2文件；<br />
-v ：压缩的过程中显示文件；<br />
-f ：使用文件名，请留意，在f之后要立即接文件名，不要再加其它参数；<br />
-p ：保持原文件的属性；<br />
-P ：使用绝对路径来压缩；<br />
-N ：设定日期(yyyy/mm/dd)，比后面接的日期还要新的文件才会被打包进新建的文件中；<br />
--exclude FILE：在打包的过程中，不要将FILE打包。</p>
<p>举几个例子：</p>
<p>例一：将整个/etc目录下的文件全部打包成为/tmp/etc.tar<br />
tar -cvf /tmp/etc.tar /etc&nbsp;&nbsp;#仅打包，不压缩<br />
tar -zcvf /tmp/etc.tar.gz /etc&nbsp;&nbsp;#打包后，以gzip压缩<br />
tar -jcvf /tmp/etc.tar.bz2 /etc&nbsp;&nbsp;#打包后，以bzip2压缩</p>
<p>例二：查阅上述/tmp/etc.tar.gz文件内有哪些文件<br />
tar -ztvf /tmp/etc.tar.gz</p>
<p>例三：将/tmp/etc.tar.gz文件解压缩到/usr/local/src下<br />
cd /usr/local/src&nbsp;&nbsp;&nbsp;#先将工作目录变换到/usr/local/src下<br />
tar -zxvf /tmp/etc.tar.gz<br />
&nbsp; <br />
例四：只将/tmp/etc.tar.gz内的etc/passwd解压到/tmp下<br />
cd /tmp<br />
tar -zxvf /tmp/etc.tar.gz etc/passwd</p>
<p>例五：将/etc内的所有文件备份下来，并且保存其权限！<br />
tar -zxvpf /tmp/etc.tar.gz /etc</p>
<p>例六：在/home当中，比2005/06/01新的文件才备份<br />
tar -N '2005/06/01' -zcvf home.tar.gz /home</p>
<p>例七：备份/home、/etc，但不要/home/dmtsai<br />
tar --exclude /home/dmtsai -zcvf myfile.tar.gz /home/* /etc</p>
<p>例八：将/etc打包后直接解开在/tmp底下，而不产生文件！<br />
cd /tmp<br />
tar -cvf - /etc | tar -xvf -</p>
<br />
<p>&nbsp;</p>
<p>现在有一个需求，不知道该如何才能实现<br />
压缩： tar czvf /data/backup/test.tar.gz /data/a/b/directory<br />
解压：<br />
cd /data/test<br />
tar xzvf /data/backup/test.tar.gz<br />
<br />
问题是，解压后的文件，在/data/test/data/a/b/directory里面<br />
能否压缩时只保留directory以下的所有目录，以directory作为/，而不是/data/a/b/directory？<br />
<br />
问题已经解决，找到了GNU tar的官方资料<br />
http://www.delorie.com/gnu/docs/tar/tar_98.html<br />
<br />
这样写就可以解决了<br />
tar czvf /data/backup/test.tar.gz /data/a/b/directory<br />
改成<br />
tar czvf /data/backup/test.tar.gz -C /data/a/b(空格)directory</p>
<p>&nbsp;<br />
-C是临时切换工作目录，-P是绝对路径，在这里只用到-C参数就行了</p>
<img src ="http://www.blogjava.net/yc1354/aggbug/142689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-09-04 17:17 <a href="http://www.blogjava.net/yc1354/archive/2007/09/04/142689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java反射机制 (转)</title><link>http://www.blogjava.net/yc1354/archive/2007/06/14/124347.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Thu, 14 Jun 2007 08:52:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/06/14/124347.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/124347.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/06/14/124347.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/124347.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/124347.html</trackback:ping><description><![CDATA[<p>摘要<br>Reflection 是Java被视为动态（或准动态）语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息，包括其modifiers（诸如public, static 等等）、superclass（例如Object）、实现之interfaces（例如Cloneable），也包括fields和methods的所有信息，并可于运行时改变fields内容或唤起methods。本文借由实例，大面积示范Reflection APIs。<br>&nbsp;<br>关键词：<br>Introspection（内省、内观）<br>Reflection（反射）<br>有时候我们说某个语言具有很强的动态性，有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定（dynamic binding）、动态链接（dynamic linking）、动态加载（dynamic loading）等。然而&#8220;动态&#8221;一词其实没有绝对而普遍适用的严格定义，有时候甚至像对象导向当初被导入编程领域一样，一人一把号，各吹各的调。<br>&nbsp;<br>一般而言，开发者社群说到动态语言，大致认同的一个定义是：&#8220;程序运行时，允许改变程序结构或变量类型，这种语言称为动态语言&#8221;。从这个观点看，Perl，Python，Ruby是动态语言，C++，Java，C#不是动态语言。<br>&nbsp;<br>尽管在这样的定义与分类下Java不是动态语言，它却有着一个非常突出的动态相关机制：Reflection。这个字的意思是&#8220;反射、映象、倒影&#8221;，用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说，Java程序可以加载一个运行时才得知名称的class，获悉其完整构造（但不包括methods定义），并生成其对象实体、或对其fields设值、或唤起其methods1。这种&#8220;看透class&#8221;的能力（the ability of the program to examine itself）被称为introspection（内省、内观、反省）。Reflection和introspection是常被并提的两个术语。<br>&nbsp;<br>Java如何能够做出上述的动态特性呢？这是一个深远话题，本文对此只简单介绍一些概念。整个篇幅最主要还是介绍Reflection APIs，也就是让读者知道如何探索class的结构、如何对某个&#8220;运行时才获知名称的class&#8221;生成一份实体、为其fields设值、调用其methods。本文将谈到java.lang.Class，以及java.lang.reflect中的Method、Field、Constructor等等classes。<br>&nbsp;<br>&#8220;Class&#8221;class<br>众所周知Java有个Object class，是所有Java classes的继承根源，其内声明了数个应该在所有Java class中被改写的methods：hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。<br>&nbsp;<br>Class class十分特殊。它和一般classes一样继承自Object，其实体用以表达Java程序运行时的classes和interfaces，也用来表达enum、array、primitive Java types（boolean, byte, char, short, int, long, float, double）以及关键词void。当一个class被加载，或当加载器（class loader）的defineClass()被JVM调用，JVM 便自动产生一个Class object。如果您想借由&#8220;修改Java标准库源码&#8221;来观察Class object的实际生成时机（例如在Class的constructor内添加一个println()），不能够！因为Class并没有public constructor（见图1）。本文最后我会拨一小块篇幅顺带谈谈Java标准库源码的改动办法。<br>&nbsp;<br>Class是Reflection故事起源。针对任何您想探勘的class，唯有先为它产生一个Class object，接下来才能经由后者唤起为数十多个的Reflection APIs。这些APIs将在稍后的探险活动中一一亮相。<br>&nbsp;<br>#001 public final<br>#002 class Class&lt;T&gt; implements java.io.Serializable,<br>#003 java.lang.reflect.GenericDeclaration,<br>#004 java.lang.reflect.Type,<br>#005 java.lang.reflect.AnnotatedElement {<br>#006&nbsp;&nbsp;&nbsp; private Class() {}<br>#007&nbsp;&nbsp;&nbsp; public String toString() {<br>#008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ( isInterface() ? "interface " :<br>#009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (isPrimitive() ? "" : "class "))<br>#010&nbsp;&nbsp;&nbsp; + getName();<br>#011 }<br>...<br>图1：Class class片段。注意它的private empty ctor，意指不允许任何人经由编程方式产生Class object。是的，其object 只能由JVM 产生。<br>&nbsp;<br>&#8220;Class&#8221; object的取得途径<br>Java允许我们从多种管道为一个class生成对应的Class object。图2是一份整理。<br>Class object 诞生管道 示例 <br>运用getClass()<br>注：每个class 都有此函数 String str = "abc";<br>Class c1 = str.getClass(); <br>运用<br>Class.getSuperclass()2 Button b = new Button();<br>Class c1 = b.getClass();<br>Class c2 = c1.getSuperclass(); <br>运用static method<br>Class.forName()<br>（最常被使用） Class c1 = Class.forName ("java.lang.String");<br>Class c2 = Class.forName ("java.awt.Button");<br>Class c3 = Class.forName ("java.util.LinkedList$Entry");<br>Class c4 = Class.forName ("I");<br>Class c5 = Class.forName ("[I"); <br>运用<br>.class 语法 Class c1 = String.class;<br>Class c2 = java.awt.Button.class;<br>Class c3 = Main.InnerClass.class;<br>Class c4 = int.class;<br>Class c5 = int[].class; <br>运用<br>primitive wrapper classes<br>的TYPE 语法<br>&nbsp; Class c1 = Boolean.TYPE;<br>Class c2 = Byte.TYPE;<br>Class c3 = Character.TYPE;<br>Class c4 = Short.TYPE;<br>Class c5 = Integer.TYPE;<br>Class c6 = Long.TYPE;<br>Class c7 = Float.TYPE;<br>Class c8 = Double.TYPE;<br>Class c9 = Void.TYPE; </p>
<p><br>图2：Java 允许多种管道生成Class object。<br>&nbsp;<br>Java classes 组成分析<br>首先容我以图3的java.util.LinkedList为例，将Java class的定义大卸八块，每一块分别对应图4所示的Reflection API。图5则是&#8220;获得class各区块信息&#8221;的程序示例及执行结果，它们都取自本文示例程序的对应片段。<br>&nbsp;<br>package java.util;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(1)<br>import java.lang.*;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(2)<br>public class LinkedList&lt;E&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(3)(4)(5)<br>extends AbstractSequentialList&lt;E&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(6)<br>implements List&lt;E&gt;, Queue&lt;E&gt;,<br>Cloneable, java.io.Serializable&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(7)<br>{<br>private static class Entry&lt;E&gt; { &#8230; }//(8)<br>public LinkedList() { &#8230; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(9)<br>public LinkedList(Collection&lt;? extends E&gt; c) { &#8230; }<br>public E getFirst() { &#8230; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //(10)<br>public E getLast() { &#8230; }<br>private transient Entry&lt;E&gt; header = &#8230;; //(11)<br>private transient int size = 0;<br>}<br>图3：将一个Java class 大卸八块，每块相应于一个或一组Reflection APIs（图4）。<br>&nbsp;<br>Java classes 各成份所对应的Reflection APIs<br>图3的各个Java class成份，分别对应于图4的Reflection API，其中出现的Package、Method、Constructor、Field等等classes，都定义于java.lang.reflect。<br>Java class 内部模块（参见图3） Java class 内部模块说明 相应之Reflection API，多半为Class methods。 返回值类型(return type) <br>(1) package class隶属哪个package getPackage() Package <br>(2) import class导入哪些classes 无直接对应之API。<br>解决办法见图5-2。&nbsp;&nbsp; <br>(3) modifier class（或methods, fields）的属性<br>&nbsp; int getModifiers()<br>Modifier.toString(int)<br>Modifier.isInterface(int) int<br>String<br>bool <br>(4) class name or interface name class/interface 名称getName() String <br>(5) type parameters 参数化类型的名称 getTypeParameters()&nbsp; TypeVariable &lt;Class&gt;[] <br>(6) base class base class（只可能一个） getSuperClass() Class <br>(7) implemented interfaces 实现有哪些interfaces getInterfaces() Class[]<br>&nbsp; <br>(8) inner classes 内部classes getDeclaredClasses() Class[] <br>(8') outer class 如果我们观察的class 本身是inner classes，那么相对它就会有个outer class。 getDeclaringClass() Class <br>(9) constructors 构造函数getDeclaredConstructors() 不论 public 或private 或其它access level，皆可获得。另有功能近似之取得函数。 Constructor[] <br>(10) methods 操作函数getDeclaredMethods() 不论 public 或private 或其它access level，皆可获得。另有功能近似之取得函数。 Method[] <br>(11) fields 字段（成员变量） getDeclaredFields()不论 public 或private 或其它access level，皆可获得。另有功能近似之取得函数。 Field[] </p>
<p><br>图4：Java class大卸八块后（如图3），每一块所对应的Reflection API。本表并非<br>Reflection APIs 的全部。<br>&nbsp;<br>Java Reflection API 运用示例<br>图5示范图4提过的每一个Reflection API，及其执行结果。程序中出现的tName()是个辅助函数，可将其第一自变量所代表的&#8220;Java class完整路径字符串&#8221;剥除路径部分，留下class名称，储存到第二自变量所代表的一个hashtable去并返回（如果第二自变量为null，就不储存而只是返回）。<br>&nbsp;<br>#001 Class c = null;<br>#002 c = Class.forName(args[0]);<br>#003<br>#004 Package p;<br>#005 p = c.getPackage();<br>#006<br>#007 if (p != null)<br>#008&nbsp;&nbsp;&nbsp; System.out.println("package "+p.getName()+";");<br>&nbsp;<br>执行结果（例）：<br>package java.util;<br>图5-1：找出class 隶属的package。其中的c将继续沿用于以下各程序片段。<br>&nbsp;<br>#001 ff = c.getDeclaredFields();<br>#002 for (int i = 0; i &lt; ff.length; i++)<br>#003&nbsp;&nbsp;&nbsp; x = tName(ff[i].getType().getName(), classRef);<br>#004<br>#005 cn = c.getDeclaredConstructors();<br>#006 for (int i = 0; i &lt; cn.length; i++) {<br>#007&nbsp;&nbsp;&nbsp; Class cx[] = cn[i].getParameterTypes();<br>#008&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; cx.length; j++)<br>#009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = tName(cx[j].getName(), classRef);<br>#010 }<br>#011<br>#012 mm = c.getDeclaredMethods();<br>#013 for (int i = 0; i &lt; mm.length; i++) {<br>#014&nbsp;&nbsp;&nbsp; x = tName(mm[i].getReturnType().getName(), classRef);<br>#015&nbsp;&nbsp;&nbsp; Class cx[] = mm[i].getParameterTypes();<br>#016&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; cx.length; j++)<br>#017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = tName(cx[j].getName(), classRef);<br>#018 }<br>#019 classRef.remove(c.getName()); //不必记录自己（不需import 自己）<br>&nbsp;<br>执行结果（例）：<br>import java.util.ListIterator;<br>import java.lang.Object;<br>import java.util.LinkedList$Entry;<br>import java.util.Collection;<br>import java.io.ObjectOutputStream;<br>import java.io.ObjectInputStream;<br>图5-2：找出导入的classes，动作细节详见内文说明。<br>&nbsp;<br>#001 int mod = c.getModifiers();<br>#002 System.out.print(Modifier.toString(mod)); //整个modifier<br>#003<br>#004 if (Modifier.isInterface(mod))<br>#005&nbsp;&nbsp;&nbsp; System.out.print(" "); //关键词 "interface" 已含于modifier<br>#006 else<br>#007&nbsp;&nbsp;&nbsp; System.out.print(" class "); //关键词 "class"<br>#008 System.out.print(tName(c.getName(), null)); //class 名称<br>&nbsp;<br>执行结果（例）：<br>public class LinkedList<br>图5-3：找出class或interface 的名称，及其属性（modifiers）。<br>&nbsp;<br>#001 TypeVariable&lt;Class&gt;[] tv;<br>#002 tv = c.getTypeParameters(); //warning: unchecked conversion<br>#003 for (int i = 0; i &lt; tv.length; i++) {<br>#004&nbsp;&nbsp;&nbsp; x = tName(tv[i].getName(), null); //例如 E,K,V...<br>#005&nbsp;&nbsp;&nbsp; if (i == 0) //第一个<br>#006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("&lt;" + x);<br>#007&nbsp;&nbsp;&nbsp; else //非第一个<br>#008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("," + x);<br>#009&nbsp;&nbsp;&nbsp; if (i == tv.length-1) //最后一个<br>#010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("&gt;");<br>#011 }<br>&nbsp;<br>执行结果（例）：<br>public abstract interface Map&lt;K,V&gt;<br>或public class LinkedList&lt;E&gt;<br>图5-4：找出parameterized types 的名称<br>&nbsp;<br>#001 Class supClass;<br>#002 supClass = c.getSuperclass();<br>#003 if (supClass != null) //如果有super class<br>#004&nbsp;&nbsp;&nbsp; System.out.print(" extends" +<br>#005 tName(supClass.getName(),classRef));<br>&nbsp;<br>执行结果（例）：<br>public class LinkedList&lt;E&gt;<br>extends AbstractSequentialList,<br>图5-5：找出base class。执行结果多出一个不该有的逗号于尾端。此非本处重点，为简化计，不多做处理。<br>&nbsp;<br>#001 Class cc[];<br>#002 Class ctmp;<br>#003 //找出所有被实现的interfaces<br>#004 cc = c.getInterfaces();<br>#005 if (cc.length != 0)<br>#006&nbsp;&nbsp;&nbsp; System.out.print(", \r\n" + " implements "); //关键词<br>#007 for (Class cite : cc) //JDK1.5 新式循环写法<br>#008&nbsp;&nbsp;&nbsp; System.out.print(tName(cite.getName(), null)+", ");<br>&nbsp;<br>执行结果（例）：<br>public class LinkedList&lt;E&gt;<br>extends AbstractSequentialList,<br>implements List, Queue, Cloneable, Serializable,<br>图5-6：找出implemented interfaces。执行结果多出一个不该有的逗号于尾端。此非本处重点，为简化计，不多做处理。<br>&nbsp;<br>#001 cc = c.getDeclaredClasses(); //找出inner classes<br>#002 for (Class cite : cc)<br>#003&nbsp;&nbsp;&nbsp; System.out.println(tName(cite.getName(), null));<br>#004<br>#005 ctmp = c.getDeclaringClass(); //找出outer classes<br>#006 if (ctmp != null)<br>#007&nbsp;&nbsp;&nbsp; System.out.println(ctmp.getName());<br>&nbsp;<br>执行结果（例）：<br>LinkedList$Entry<br>LinkedList$ListItr<br>图5-7：找出inner classes 和outer class<br>&nbsp;<br>#001 Constructor cn[];<br>#002 cn = c.getDeclaredConstructors();<br>#003 for (int i = 0; i &lt; cn.length; i++) {<br>#004&nbsp;&nbsp;&nbsp; int md = cn[i].getModifiers();<br>#005&nbsp;&nbsp;&nbsp; System.out.print(" " + Modifier.toString(md) + " " +<br>#006&nbsp;&nbsp;&nbsp; cn[i].getName());<br>#007&nbsp;&nbsp;&nbsp; Class cx[] = cn[i].getParameterTypes();<br>#008&nbsp;&nbsp;&nbsp; System.out.print("(");<br>#009&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; cx.length; j++) {<br>#010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(tName(cx[j].getName(), null));<br>#011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (j &lt; (cx.length - 1)) System.out.print(", ");<br>#012&nbsp;&nbsp;&nbsp; }<br>#013&nbsp;&nbsp;&nbsp; System.out.print(")");<br>#014 }<br>&nbsp;<br>执行结果（例）：<br>public java.util.LinkedList(Collection)<br>public java.util.LinkedList()<br>图5-8a：找出所有constructors<br>&nbsp;<br>#004 System.out.println(cn[i].toGenericString());<br>&nbsp;<br>执行结果（例）：<br>public java.util.LinkedList(java.util.Collection&lt;? extends E&gt;)<br>public java.util.LinkedList()<br>图5-8b：找出所有constructors。本例在for 循环内使用toGenericString()，省事。<br>&nbsp;<br>#001 Method mm[];<br>#002 mm = c.getDeclaredMethods();<br>#003 for (int i = 0; i &lt; mm.length; i++) {<br>#004&nbsp;&nbsp;&nbsp; int md = mm[i].getModifiers();<br>#005&nbsp;&nbsp;&nbsp; System.out.print(" "+Modifier.toString(md)+" "+<br>#006&nbsp;&nbsp;&nbsp; tName(mm[i].getReturnType().getName(), null)+" "+<br>#007&nbsp;&nbsp;&nbsp; mm[i].getName());<br>#008&nbsp;&nbsp;&nbsp; Class cx[] = mm[i].getParameterTypes();<br>#009&nbsp;&nbsp;&nbsp; System.out.print("(");<br>#010&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; cx.length; j++) {<br>#011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(tName(cx[j].getName(), null));<br>#012&nbsp;&nbsp;&nbsp; if (j &lt; (cx.length - 1)) System.out.print(", ");<br>#013&nbsp;&nbsp;&nbsp; }<br>#014&nbsp;&nbsp;&nbsp; System.out.print(")");<br>#015 }<br>&nbsp;<br>执行结果（例）：<br>public Object get(int)<br>public int size()<br>图5-9a：找出所有methods<br>&nbsp;<br>#004 System.out.println(mm[i].toGenericString());<br>&nbsp;<br>public E java.util.LinkedList.get(int)<br>public int java.util.LinkedList.size()<br>图5-9b：找出所有methods。本例在for 循环内使用toGenericString()，省事。<br>&nbsp;<br>#001 Field ff[];<br>#002 ff = c.getDeclaredFields();<br>#003 for (int i = 0; i &lt; ff.length; i++) {<br>#004&nbsp;&nbsp;&nbsp; int md = ff[i].getModifiers();<br>#005&nbsp;&nbsp;&nbsp; System.out.println(" "+Modifier.toString(md)+" "+<br>#006&nbsp;&nbsp;&nbsp; tName(ff[i].getType().getName(), null) +" "+<br>#007&nbsp;&nbsp;&nbsp; ff[i].getName()+";");<br>#008 }<br>&nbsp;<br>执行结果（例）：<br>private transient LinkedList$Entry header;<br>private transient int size;<br>图5-10a：找出所有fields<br>&nbsp;<br>#004 System.out.println("G: " + ff[i].toGenericString());<br>&nbsp;<br>private transient java.util.LinkedList.java.util.LinkedList$Entry&lt;E&gt; ??<br>java.util.LinkedList.header<br>private transient int java.util.LinkedList.size<br>图5-10b：找出所有fields。本例在for 循环内使用toGenericString()，省事。<br>&nbsp;<br>找出class参用（导入）的所有classes<br>没有直接可用的Reflection API可以为我们找出某个class参用的所有其它classes。要获得这项信息，必须做苦工，一步一脚印逐一记录。我们必须观察所有fields的类型、所有methods（包括constructors）的参数类型和回返类型，剔除重复，留下唯一。这正是为什么图5-2程序代码要为tName()指定一个hashtable（而非一个null）做为第二自变量的缘故：hashtable可为我们储存元素（本例为字符串），又保证不重复。<br>&nbsp;<br>本文讨论至此，几乎可以还原一个class的原貌（唯有methods 和ctors的定义无法取得）。接下来讨论Reflection 的另三个动态性质：(1) 运行时生成instances，(2) 执<br>行期唤起methods，(3) 运行时改动fields。<br>&nbsp;<br>运行时生成instances<br>欲生成对象实体，在Reflection 动态机制中有两种作法，一个针对&#8220;无自变量ctor&#8221;，<br>一个针对&#8220;带参数ctor&#8221;。图6是面对&#8220;无自变量ctor&#8221;的例子。如果欲调用的是&#8220;带参数ctor&#8220;就比较麻烦些，图7是个例子，其中不再调用Class的newInstance()，而是调用Constructor 的newInstance()。图7首先准备一个Class[]做为ctor的参数类型（本例指定为一个double和一个int），然后以此为自变量调用getConstructor()，获得一个专属ctor。接下来再准备一个Object[] 做为ctor实参值（本例指定3.14159和125），调用上述专属ctor的newInstance()。<br>&nbsp;<br>#001 Class c = Class.forName("DynTest");<br>#002 Object obj = null;<br>#003 obj = c.newInstance(); //不带自变量<br>#004 System.out.println(obj);<br>图6：动态生成&#8220;Class object 所对应之class&#8221;的对象实体；无自变量。<br>&nbsp;<br>#001 Class c = Class.forName("DynTest");<br>#002 Class[] pTypes = new Class[] { double.class, int.class };<br>#003 Constructor ctor = c.getConstructor(pTypes);<br>#004 //指定parameter list，便可获得特定之ctor<br>#005<br>#006 Object obj = null;<br>#007 Object[] arg = new Object[] {3.14159, 125}; //自变量<br>#008 obj = ctor.newInstance(arg);<br>#009 System.out.println(obj);<br>图7：动态生成&#8220;Class object 对应之class&#8221;的对象实体；自变量以Object[]表示。<br>&nbsp;<br>运行时调用methods<br>这个动作和上述调用&#8220;带参数之ctor&#8221;相当类似。首先准备一个Class[]做为ctor的参数类型（本例指定其中一个是String，另一个是Hashtable），然后以此为自变量调用getMethod()，获得特定的Method object。接下来准备一个Object[]放置自变量，然后调用上述所得之特定Method object的invoke()，如图8。知道为什么索取Method object时不需指定回返类型吗？因为method overloading机制要求signature（署名式）必须唯一，而回返类型并非signature的一个成份。换句话说，只要指定了method名称和参数列，就一定指出了一个独一无二的method。<br>&nbsp;<br>#001 public String func(String s, Hashtable ht)<br>#002 {<br>#003 &#8230;System.out.println("func invoked"); return s;<br>#004 }<br>#005 public static void main(String args[])<br>#006 {<br>#007 Class c = Class.forName("Test");<br>#008 Class ptypes[] = new Class[2];<br>#009 ptypes[0] = Class.forName("java.lang.String");<br>#010 ptypes[1] = Class.forName("java.util.Hashtable");<br>#011 Method m = c.getMethod("func",ptypes);<br>#012 Test obj = new Test();<br>#013 Object args[] = new Object[2];<br>#014 arg[0] = new String("Hello,world");<br>#015 arg[1] = null;<br>#016 Object r = m.invoke(obj, arg);<br>#017 Integer rval = (String)r;<br>#018 System.out.println(rval);<br>#019 }<br>图8：动态唤起method<br>&nbsp;<br>运行时变更fields内容<br>与先前两个动作相比，&#8220;变更field内容&#8221;轻松多了，因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()，如图9。<br>&nbsp;<br>#001 public class Test {<br>#002 public double d;<br>#003<br>#004 public static void main(String args[])<br>#005 {<br>#006 Class c = Class.forName("Test");<br>#007 Field f = c.getField("d"); //指定field 名称<br>#008 Test obj = new Test();<br>#009 System.out.println("d= " + (Double)f.get(obj));<br>#010 f.set(obj, 12.34);<br>#011 System.out.println("d= " + obj.d);<br>#012 }<br>#013 }<br>图9：动态变更field 内容<br>&nbsp;<br>Java 源码改动办法<br>先前我曾提到，原本想借由&#8220;改动Java标准库源码&#8221;来测知Class object的生成，但由于其ctor原始设计为private，也就是说不可能透过这个管道生成Class object（而是由class loader负责生成），因此&#8220;在ctor中打印出某种信息&#8221;的企图也就失去了意义。<br>&nbsp;<br>这里我要谈点题外话：如何修改Java标准库源码并让它反应到我们的应用程序来。假设我想修改java.lang.Class，让它在某些情况下打印某种信息。首先必须找出标准源码！当你下载JDK 套件并安装妥当，你会发现jdk150\src\java\lang 目录（见图10）之中有Class.java，这就是我们此次行动的标准源码。备份后加以修改，编译获得Class.class。接下来准备将.class 搬移到jdk150\jre\lib\endorsed（见图10）。<br>&nbsp;<br>这是一个十分特别的目录，class loader将优先从该处读取内含classes的.jar文件——成功的条件是.jar内的classes压缩路径必须和Java标准库的路径完全相同。为此，我们可以将刚才做出的Class.class先搬到一个为此目的而刻意做出来的\java\lang目录中，压缩为foo.zip（任意命名，唯需夹带路径java\lang），再将这个foo.zip搬到jdk150\jre\lib\endorsed并改名为foo.jar。此后你的应用程序便会优先用上这里的java.lang.Class。整个过程可写成一个批处理文件（batch file），如图11，在DOS Box中使用。<br>&nbsp;<br>&nbsp;<br>图10：JDK1.5 安装后的目录组织。其中的endorsed 是我新建。<br>&nbsp;<br>del e:\java\lang\*.class //清理干净<br>del c:\jdk150\jre\lib\endorsed\foo.jar //清理干净<br>c:<br>cd c:\jdk150\src\java\lang<br>javac -Xlint:unchecked Class.java //编译源码<br>javac -Xlint:unchecked ClassLoader.java //编译另一个源码（如有必要）<br>move *.class e:\java\lang //搬移至刻意制造的目录中<br>e:<br>cd e:\java\lang //以下压缩至适当目录<br>pkzipc -add -path=root c:\jdk150\jre\lib\endorsed\foo.jar *.class<br>cd e:\test //进入测试目录<br>javac -Xlint:unchecked Test.java //编译测试程序<br>java Test //执行测试程序<br>图11：一个可在DOS Box中使用的批处理文件（batch file），用以自动化java.lang.Class<br>的修改动作。Pkzipc(.exe)是个命令列压缩工具，add和path都是其命令。<br>&nbsp;<br>更多信息<br>以下是视野所及与本文主题相关的更多讨论。这些信息可以弥补因文章篇幅限制而带来的不足，或带给您更多视野。<br>&nbsp;<br>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Take an in-depth look at the Java Reflection API -- Learn about the new Java 1.1 tools forfinding out information about classes", by Chuck McManis。此篇文章所附程序代码是本文示例程序的主要依据（本文示例程序示范了更多Reflection APIs，并采用JDK1.5 新式的for-loop 写法）。<br>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br></p>
<img src ="http://www.blogjava.net/yc1354/aggbug/124347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-06-14 16:52 <a href="http://www.blogjava.net/yc1354/archive/2007/06/14/124347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java1.5语言新特性简单总结(转)</title><link>http://www.blogjava.net/yc1354/archive/2007/06/14/124320.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Thu, 14 Jun 2007 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/06/14/124320.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/124320.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/06/14/124320.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/124320.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/124320.html</trackback:ping><description><![CDATA[<div class=postText><font color=#333333><strong>1. 自动装箱与拆箱 对应C#<br></strong>　　<br>　　例1.1<br>　　Integer i = 10;<br>　　int j = i;<br>　　<br>　　<strong>2. 更优化的for循环 对应就C#---foreach循环</strong><br>　　<br>　　例2.1<br>　　String[] names = {"BadBoy","GoodBoy","HappyGirl","sadGirl"};<br>　　for(String option: names) {<br>　　System.out.println(option);<br>　　}<br>　　<br>　　例2.2 加泛型 对应C++模板<br>　　import java.util.*;<br>　　<br>　　ArrayList&lt;String&gt; animals = new ArrayList&lt;String&gt;();<br>　　animals.add("Dog");<br>　　animals.add("Cat");<br>　　animals.add("Chick");<br>　　animals.add("Cow");<br>　　for(String option : animals) {<br>　　System.out.println(option);<br>　　}<br>　　<br>　　<strong>3.参数可变的方法和printf</strong><br>　　<br>　　例3.1<br>　　定义:<br>　　public int sum(int... n) {　//传过来n为一个int型数组<br>　　int tempSum;<br>　　for(int option : n) {<br>　　tempSum+=option;<br>　　}<br>　　/*<br>　　for(int i = 0; i &lt; n.length; i++) {<br>　　tempSum+=n[i];<br>　　}<br>　　*/<br>　　return tempSum;<br>　　}<br>　　调用1: sum(1);<br>　　调用2: sum(1,2);<br>　　调用3: sum(1,2,3,4);<br>　　<br>　　例3.2 printf方法,　对应c语言的printf<br>　　int x = 10;<br>　　int y = 20;<br>　　int sum = x + y;<br>　　System.out.printf("%d + %d = %d",x,y,sum);<br>　　<br>　　<strong>4. 枚举</strong><br>　　<br>　　例4.1<br>　　public enum MyColors {<br>　　red,<br>　　black,<br>　　blue,<br>　　green,<br>　　yellow<br>　　}<br>　　<br>　　MyColors color = MyColors.red;<br>　　for(MyColors option : color.values()) {<br>　　System.out.println(option);<br>　　}<br>　　<br>　　/**不能在switch语句里这样写case MyColors.red:<br>　　*这样编译器不会让你通过*/<br>　　switch(color) {<br>　　case red:<br>　　System.out.println("best color is "+red);<br>　　break;<br>　　case black:<br>　　System.out.println("NO " + black);<br>　　break;<br>　　default:<br>　　System.out.println("What");<br>　　break;<br>　　}<br>　　<br>　　<strong>5.静态引用</strong><br>　　<br>　　例5.1<br>　　1.5版本以前的写法是：<br>　　<br>　　import java.lang.Math; //程序开头处<br>　　<br>　　...<br>　　<br>　　double x = Math.random();<br>　　1.5版本中可以这样写<br>　　import static java.lang.Math.random; //程序开头处<br>　　<br>　　...<br>　　<br>　　double x = random();</font><br></div>
<img src ="http://www.blogjava.net/yc1354/aggbug/124320.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-06-14 14:38 <a href="http://www.blogjava.net/yc1354/archive/2007/06/14/124320.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>showModalDialog和showModelessDialog使用心得(转)</title><link>http://www.blogjava.net/yc1354/archive/2007/03/26/106486.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Mon, 26 Mar 2007 10:19:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/03/26/106486.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/106486.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/03/26/106486.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/106486.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/106486.html</trackback:ping><description><![CDATA[
		<p>一、showModalDialog和showModelessDialog有什么不同？<br />　　showModalDialog：被打开后就会始终保持输入焦点。除非对话框被关闭，否则用户无法切换到主窗口。类似alert的运行效果。<br />　　showModelessDialog：被打开后，用户可以随机切换输入焦点。对主窗口没有任何影响(最多是被挡住一下而以。:P)</p>
		<p>二、怎样才让在showModalDialog和showModelessDialog的超连接不弹出新窗口？<br />　　在被打开的网页里加上&lt;base target="_self"&gt;就可以了。这句话一般是放在&lt;html&gt;和&lt;body&gt;之间的。</p>
		<p>三、怎样才刷新showModalDialog和showModelessDialog里的内容？<br />　　在showModalDialog和showModelessDialog里是不能按F5刷新的，又不能弹出菜单。这个只能依靠javascript了，以下是相关代码：</p>
		<p>&lt;body onkeydown="if (event.keyCode==116){reload.click()}"&gt;<br />&lt;a id="reload" href="filename.htm" style="display:none"&gt;reload...&lt;/a&gt;</p>
		<p>　　将filename.htm替换成网页的名字然后将它放到你打开的网页里，按F5就可以刷新了，注意，这个要配合&lt;base target="_self"&gt;使用，不然你按下F5会弹出新窗口的。</p>
		<p>四、如何用javascript关掉showModalDialog(或showModelessDialog)打开的窗口。<br />　　&lt;input type="button" value="关闭" onclick="window.close()"&gt;<br />　　也要配合&lt;base target="_self"&gt;，不然会打开一个新的IE窗口，然后再关掉的。</p>
		<p>五、showModalDialog和showModelessDialog数据传递技巧。<br />　　(作者语：本来想用一问一答形式来写的，但是我想不出这个怎么问，所以只好这样了。)<br />　　这个东西比较麻烦，我改了好几次了不是没办法说明白(语文水平越来越差了)，只好用个例子说明了。<br />　　例子：<br />　　　　现在需要在一个showModalDialog(或showModelessDialog)里读取或设置一个变量var_name</p>
		<p>　　　　　　一般的传递方式：<br />　　　　　　　　window.showModalDialog("filename.htm",var_name)<br />　　　　　　　　//传递var_name变量<br />　　　　　　在showModalDialog(或showModelessDialog)读取和设置时：<br />　　　　　　　　alert(window.dialogArguments)//读取var_name变量<br />　　　　　　　　window.dialogArguments="oyiboy"//设置var_name变量<br />　　　　这种方式是可以满足的，但是当你想在操作var_name同时再操作第二个变理var_id时呢？就无法再进行操作了。这就是这种传递方式的局限性。<br />　　　　<br />　　　　　　以下是我建议使用的传递方式：<br />　　　　　　　　window.showModalDialog("filename.htm",window)<br />　　　　　　　　//不管要操作什么变量，只直传递主窗口的window对象<br />　　　　　　在showModalDialog(或showModelessDialog)读取和设置时：<br />　　　　　　　　alert(window.dialogArguments.var_name)//读取var_name变量<br />　　　　　　　　window.dialogArguments.var_name="oyiboy"//设置var_name变量</p>
		<p>　　　　　　　　同时我也可以操作var_id变量<br />　　　　　　　　alert(window.dialogArguments.var_id)//读取var_id变量<br />　　　　　　　　window.dialogArguments.var_id="001"//设置var_id变量</p>
		<p>　　　　　　　　同样还可以对主窗口的任何对象进行操作，如form对象里的元素。<br />　　　　　　　　window.dialogArguments.form1.index1.value="这是在设置index1元素的值"</p>
		<p>六、多个showModelessDialog的相互操作。<br />　　因为光说很费劲，我就偷点懒，直接用代码来说了，如果不明白的话那就直接来信(oyiboy#163.net(使用时请将#改成@))问我吧。</p>
		<p>　　以下代码的主要作用是在一个showModelessDialog里移动别一个showModelessDialog的位置。</p>
		<p>　　主文件的部份js代码。<br />　　var s1=showModelessDialog('控制.htm',window,"dialogTop:1px;dialogLeft:1px"） //打开控制窗口<br />　　var s2=showModelessDialog('about:blank',window,"dialogTop:200px;dialogLeft:300px"）　　//打开被控制窗口</p>
		<p>　　控制.htm的部份代码。<br />　　&lt;script&gt;<br />　　　　//操作位置数据，因为窗口的位置数据是"xxxpx"方式的，所以需要这样的一个特殊操作函数。<br /> function countNumber(A_strNumber,A_strWhatdo)<br /> {<br />  A_strNumber=A_strNumber.replace('px','')<br />  A_strNumber-=0<br />  switch(A_strWhatdo)<br />  {<br />  case "-":A_strNumber-=10;break;<br />  case "+":A_strNumber+=10;break;<br />  }<br />  return A_strNumber + "px"<br /> }<br />　　&lt;/script&gt;<br />　　&lt;input type="button" onclick="window.dialogArguments.s2.dialogTop=countNumber(window.dialogArguments.s2.dialogTop,'-')" value="上移"&gt;<br />　　&lt;input type="button" onclick="window.dialogArguments.s2.dialogLeft=countNumber(window.dialogArguments.s2.dialogLeft,'-')" value="左移"&gt;<br />　　&lt;input type="button" onclick="window.dialogArguments.s2.dialogLeft=countNumber(window.dialogArguments.s2.dialogLeft,'+')" value="右移"&gt;<br />　　&lt;input type="button" onclick="window.dialogArguments.s2.dialogTop=countNumber(window.dialogArguments.s2.dialogTop,'+')" value="下移"&gt;</p>
		<p>　　以上关键部份是：<br />　　　　窗口命名方式：var s1=showModelessDialog('控制.htm',window,"dialogTop:1px;dialogLeft:1px"）<br />　　　　变量访问方式：window.dialogArguments.s2.dialogTop</p>
		<p>　　这个例子只是现实showModelessDialog与showModelessDialog之间的位置操作功能，通过这个原理，在showModelessDialog之间相互控制各自的显示页面，传递变量和数据等。这要看各位的发挥了。</p>
		<p> </p>
		<p>showModalDialog()、showModelessDialog()方法使用详解 <br /><br /><br />javascript有许多内建的方法来产生对话框，如：window.alert(), window.confirm(),window.prompt().等。 然而IE提供更多的方法支持对话框。如：</p>
		<p>　　showModalDialog() (IE 4+ 支持)<br />　　 showModelessDialog() (IE 5+ 支持)</p>
		<p>
				<br />window.showModalDialog()方法用来创建一个显示HTML内容的模态对话框，由于是对话框，因此它并没有一般用window.open()打开的窗口的所有属性。<br />window.showModelessDialog()方法用来创建一个显示HTML内容的非模态对话框。</p>
		<p>当我们用showModelessDialog()打开窗口时，不必用window.close()去关闭它，当以非模态方式［IE5］打开时， 打开对话框的窗口仍可以进行其他的操作，即对话框不总是最上面的焦点，当打开它的窗口ＵＲＬ改变时，它自动关闭。而模态［ＩＥ４］方式的对话框始终有焦点（焦点不可移走，直到它关闭）。模态对话框和打开它的窗口相联系，因此我们打开另外的窗口时，他们的链接关系依然保存，并且隐藏在活动窗口的下面。</p>
		<p>使用方法如下：<br />vReturnValue = window.showModalDialog(sURL [, vArguments] [, sFeatures])<br />vReturnValue = window.showModelessDialog(sURL [, vArguments] [, sFeatures])<br />参数说明：<br />sURL<br />必选参数，类型：字符串。用来指定对话框要显示的文档的URL。<br />vArguments<br />可选参数，类型：变体。用来向对话框传递参数。传递的参数类型不限，包括数组等。对话框通过window.dialogArguments来取得传递进来的参数。<br />sFeatures<br />可选参数，类型：字符串。用来描述对话框的外观等信息，可以使用以下的一个或几个，用分号“;”隔开。<br />dialogHeight 对话框高度，不小于１００px，ＩＥ４中dialogHeight 和 dialogWidth 默认的单位是em，而ＩＥ５中是px，为方便其见，在定义modal方式的对话框时，用px做单位。<br />　　 dialogWidth: 对话框宽度。<br />　　 dialogLeft: 距离桌面左的距离。<br />　　 dialogTop: 离桌面上的距离。<br />　　 center: {yes | no | 1 | 0 }：窗口是否居中，默认yes，但仍可以指定高度和宽度。<br />　　 help: {yes | no | 1 | 0 }：是否显示帮助按钮，默认yes。<br />　　 resizable: {yes | no | 1 | 0 } ［ＩＥ５＋］：是否可被改变大小。默认no。<br />　　 status: {yes | no | 1 | 0 } ［IE5+］：是否显示状态栏。默认为yes[ Modeless]或no[Modal]。<br />scroll:{ yes | no | 1 | 0 | on | off }：指明对话框是否显示滚动条。默认为yes。</p>
		<p>还有几个属性是用在HTA中的，在一般的网页中一般不使用。<br />dialogHide:{ yes | no | 1 | 0 | on | off }：在打印或者打印预览时对话框是否隐藏。默认为no。<br />edge:{ sunken | raised }：指明对话框的边框样式。默认为raised。<br />unadorned:{ yes | no | 1 | 0 | on | off }：默认为no。</p>
		<p>传入参数：<br />要想对话框传递参数，是通过vArguments来进行传递的。类型不限制，对于字符串类型，最大为4096个字符。也可以传递对象，例如：</p>
		<p>test1.htm<br />====================<br />&lt;script&gt;<br />var mxh1 = new Array("mxh","net_lover","孟子E章")<br />var mxh2 = window.open("about:blank","window_mxh")<br />// 向对话框传递数组<br />window.showModalDialog("test2.htm",mxh1)<br />// 向对话框传递window对象<br />window.showModalDialog("test3.htm",mxh2)<br />&lt;/script&gt;</p>
		<p>test2.htm<br />====================<br />&lt;script&gt;<br />var a = window.dialogArguments<br />alert("您传递的参数为：" + a)<br />&lt;/script&gt;</p>
		<p>test3.htm<br />====================<br />&lt;script&gt;<br />var a = window.dialogArguments<br />alert("您传递的参数为window对象，名称：" + a.name)<br />&lt;/script&gt;</p>
		<p>可以通过window.returnValue向打开对话框的窗口返回信息，当然也可以是对象。例如：</p>
		<p>test4.htm<br />===================<br />&lt;script&gt;<br />var a = window.showModalDialog("test5.htm")<br />for(i=0;i&lt;a.length;i++) alert(a[i])<br />&lt;/script&gt;</p>
		<p>test5.htm<br />===================<br />&lt;script&gt;<br />function sendTo()<br />{<br />var a=new Array("a","b")<br />window.returnValue = a<br />window.close()<br />}<br />&lt;/script&gt;<br />&lt;body&gt;<br />&lt;form&gt;<br />&lt;input value="返回" type=button onclick="sendTo()"&gt;<br />&lt;/form&gt;</p>
		<p>常见问题：<br />1，如何在模态对话框中进行提交而不新开窗口？<br />如果你 的 浏览器是IE5.5+,可以在对话框中使用带name属性的iframe，提交时可以制定target为该iframe的name。对于IE4+，你可以用高度为0的frame来作：例子，</p>
		<p>test6.htm<br />===================<br />&lt;script&gt;<br />window.showModalDialog("test7.htm")<br />&lt;/script&gt;</p>
		<p>test7.htm<br />===================<br />if(window.location.search) alert(window.location.search)<br />&lt;frameset rows="0,*"&gt;<br />&lt;frame src="about:blank"&gt;<br />&lt;frame src="test8.htm"&gt;<br />&lt;/frameset&gt;</p>
		<p>test8.htm<br />===================<br />&lt;form target="_self" method="get"&gt;<br />&lt;input name=txt value="test"&gt;<br />&lt;input type=submit&gt;<br />&lt;/form&gt;<br />&lt;script&gt;<br />if(window.location.search) alert(window.location.search)<br />&lt;/script&gt;<br />2,可以通过<a href="http://servername/virtualdirname/test.htm?name=mxh">http://servername/virtualdirname/test.htm?name=mxh</a>方式直接向对话框传递参数吗？<br />答案是不能。但在frame里是可以的。</p>
		<p>
				<br />
		</p>
<img src ="http://www.blogjava.net/yc1354/aggbug/106486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-03-26 18:19 <a href="http://www.blogjava.net/yc1354/archive/2007/03/26/106486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>分隔字符串</title><link>http://www.blogjava.net/yc1354/archive/2007/03/26/106391.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Mon, 26 Mar 2007 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/03/26/106391.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/106391.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/03/26/106391.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/106391.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/106391.html</trackback:ping><description><![CDATA[
		<p>import java.util.*;<br />public class TokenTest2{<br /> <br /> public static void main(String[] args){<br />  <br />  StringTokenizer st=new StringTokenizer("this is a test","e",true);<br />  while(st.hasMoreTokens()){<br />   System.out.println(st.nextToken());<br />   <br />  }<br /> }<br />}<br />结果 ：<br />this is a t<br />e<br />st<br /> <br />public class StringTokenizer</p>
		<p>extends Object <br />implements Enumeration&lt;Object&gt; <br />string tokenizer 类允许应用程序将字符串分解为标记。tokenization 方法比 StreamTokenizer 类所使用的方法更简单。StringTokenizer 方法不区分标识符、数和带引号的字符串，它们也不识别并跳过注释。</p>
		<p>可以在创建时指定，也可以根据每个标记来指定分隔符（分隔标记的字符）集合。</p>
		<p>StringTokenizer 的实例有两种行为方式，这取决于它在创建时使用的 returnDelims 标志的值是 true 还是 false：</p>
		<p>如果标志为 false，则分隔符字符用来分隔标记。标记是连续字符（不是分隔符）的最大序列。 <br />如果标志为 true，则认为那些分隔符字符本身即为标记。因此标记要么是一个分隔符字符，要么是那些连续字符（不是分隔符）的最大序列。 <br />StringTokenizer 对象在内部维护字符串中要被标记的当前位置。某些操作将此当前位置移至已处理的字符后。</p>
		<p>通过截取字符串的一个子串来返回标记，该字符串用于创建 StringTokenizer 对象。</p>
		<p>下面是一个使用 tokenizer 的实例。代码如下：</p>
		<p>     StringTokenizer st = new StringTokenizer("this is a test");<br />     while (st.hasMoreTokens()) {<br />         System.out.println(st.nextToken());<br />     }<br /> </p>
		<p>输出以下字符串：</p>
		<p>     this<br />     is<br />     a<br />     test<br /> </p>
		<p>StringTokenizer 是出于兼容性的原因而被保留的遗留类（虽然在新代码中并不鼓励使用它）。建议所有寻求此功能的人使用 String 的 split 方法或 java.util.regex 包。</p>
		<p>下面的示例阐明了如何使用 String.split 方法将字符串分解为基本标记：</p>
		<p>     String[] result = "this is a test".split("<a href="file://\\s">\\s</a>");<br />     for (int x=0; x&lt;result.length; x++)<br />         System.out.println(result[x]);<br /> </p>
		<p>输出以下字符串：</p>
		<p>     this<br />     is<br />     a<br />     test</p>
		<p> </p>
<img src ="http://www.blogjava.net/yc1354/aggbug/106391.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-03-26 14:12 <a href="http://www.blogjava.net/yc1354/archive/2007/03/26/106391.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在线HTML编辑器htmlarea简单使用</title><link>http://www.blogjava.net/yc1354/archive/2007/02/07/98583.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Wed, 07 Feb 2007 05:47:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/02/07/98583.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/98583.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/02/07/98583.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/98583.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/98583.html</trackback:ping><description><![CDATA[
		<p> </p>
		<p>这几步是如果使用htmlarea3.0的说明,是在网上看到的. 在/htmlarea/examples/web-menu这个文件里有更为详细的说明.下边的这个链接的版本是多国语言版本,默认的语言为英语.</p>
		<p>一 </p>
		<p>    下载HTMLarea3.0 <a href="http://www.jhdown.com/soft/8/141/html/2006220010681.html">http://www.jhdown.com/soft/8/141/html/2006220010681.html</a>,解压里面的文件到web站点的/htmlarea目录下(可以是自己建的目录,这里建议直接用htmlarea做为目录)；</p>
		<p>二、定义javascript的全局变量及引入js文件: </p>
		<p> &lt;script type="text/javascript"&gt;<br />   _editor_url = "${pageContext.request.contextPath}/htmlarea/";<br />   _editor_lang = "en";<br />&lt;/script&gt;</p>
		<p>&lt;script type="text/javascript" src="${pageContext.request.contextPath}/htmlarea/htmlarea.js"&gt;&lt;/script&gt;</p>
		<p>&lt;script type="text/javascript"&gt;<br />var editor = null;<br />function initEditor() {<br />  editor = new HTMLArea("content");<br />  editor.generate();<br />}<br />&lt;/script&gt;</p>
		<p>有句editor = new HTMLArea("content");"content"是表单中textarea属性的id，如：</p>
		<p>      &lt;html:form action="/actionBbsreply.do?method=update" onsubmit="this['content'].value=editor.getHTML();"&gt;<br />        &lt;textarea cols="110" rows="15" id="content" name="content"&gt;<br />          &lt;bean:write name="bbsreply" property="content"/&gt;<br />        &lt;/textarea&gt;<br />        &lt;html:submit value=" 提 交 "&gt;<br />        &lt;/html:submit&gt;<br />      &lt;/html:form&gt; </p>
		<p>三、在body的onload事件中加载initEditor(): </p>
		<p>  如：&lt;body bgcolor="#ffffff" onload="initEditor()"&gt;</p>
		<p>四、注意点: </p>
		<p>1、editor = new HTMLArea("content");参数值是表单中textarea属性id;</p>
		<p>2、在body的onload事件中加载initEditor()；</p>
		<p>3、表单onsubmit事件调用this['content'].value=editor.getHTML()</p>
		<p>在使用的过程中发现,因为默认是英文语方,提示信息也是英文的, 这对于飞惯用中文的朋友来说很是不方便 ,如果要改变语言的话<br />   _editor_lang = "en";<br />把上边代码里的._editor_lang="gb"理论上就可以了, 这样,当代码去运行的时候会找/lang文件夹下的gb.js文件,但是作者发布这套源代码的时候jb.js里的中文是用iso8859-1编码的,我用MyEclipse的javascriptEdit查看器(MyEclipse下*.js的默认)打开的时候看到的全是乱码,这就导至了在jsp页面出现错误.解决的办法就是在改变gb.js的文件的属性, 将编码格式改为utf-8的格式. 然后再加上en.js下边的<br /> buttons: {<br />  "ok":           "确定",<br />  "cancel":       "取消"<br /> },</p>
		<p> msg: {<br />  "Path":         "路径",<br />  "TEXT_MODE":    "现在模式是文本模式.  按[&lt;&gt;]键切换到可视化编辑模式.",</p>
		<p>  "IE-sucks-full-screen" :<br />  // translate here<br />  "The full screen mode is known to cause problems with Internet Explorer, " +<br />  "due to browser bugs that we weren't able to workaround.  You might experience garbage " +<br />  "display, lack of editor functions and/or random browser crashes.  If your system is Windows 9x " +<br />  "it's very likely that you'll get a 'General Protection Fault' and need to reboot.\n\n" +<br />  "You have been warned.  Please press OK if you still want to try the full screen editor."<br /> },</p>
		<p> dialogs: {<br />  "Cancel"                                            : "Cancel",<br />  "Insert/Modify Link"                                : "Insert/Modify Link",<br />  "New window (_blank)"                               : "New window (_blank)",<br />  "None (use implicit)"                               : "None (use implicit)",<br />  "OK"                                                : "OK",<br />  "Other"                                             : "Other",<br />  "Same frame (_self)"                                : "Same frame (_self)",<br />  "Target:"                                           : "Target:",<br />  "Title (tooltip):"                                  : "Title (tooltip):",<br />  "Top frame (_top)"                                  : "Top frame (_top)",<br />  "URL:"                                              : "URL:",<br />  "You must enter the URL where this link points to"  : "You must enter the URL where this link points to"<br /> }</p>
		<p>这些代码.如果有需要的话, 可以把右边属性的值翻译成汉语,这里还有一个要注意的地方就是要对照格式,我把我改正过的贴到这里, 方便查看</p>
		<p>// I18N constants -- Chinese GB<br />// by Dave Lo -- <a href="mailto:dlo@interactivetools.com">dlo@interactivetools.com</a><br />HTMLArea.I18N = {</p>
		<p> // the following should be the filename without .js extension<br /> // it will be used for automatically load plugin language.<br /> lang: "gb",</p>
		<p> tooltips: {<br />  bold:           "粗体",<br />  italic:         "斜体",<br />  underline:      "底线",<br />  strikethrough:  "删除线",<br />  subscript:      "下标",<br />  superscript:    "上标",<br />  justifyleft:    "位置靠左",<br />  justifycenter:  "位置居中",<br />  justifyright:   "位置靠右",<br />  justifyfull:    "位置左右平等",<br />  orderedlist:    "位置左右平等",<br />  unorderedlist:  "无序清单",<br />  outdent:        "减小行前空白",<br />  indent:         "加宽行前空白",<br />  forecolor:      "文字颜色",<br />  backcolor:      "背景颜色",<br />  horizontalrule: "水平线",<br />  horizontalrule: "水平标尺",<br />  createlink:     "插入连结",<br />  insertimage:    "插入图形",<br />  inserttable:    "插入表格",<br />  htmlmode:       "切换HTML原始码",<br />  popupeditor:    "放大",<br />  about:          "关於 HTMLArea",<br />  help:           "说明",<br />  textindicator:  "字体例子"<br /> },</p>
		<p>
				<br /> buttons: {<br />  "ok":           "确定",<br />  "cancel":       "取消"<br /> },</p>
		<p> msg: {<br />  "Path":         "路径",<br />  "TEXT_MODE":    "现在模式是文本模式.  按[&lt;&gt;]键切换到可视化编辑模式.",</p>
		<p>  "IE-sucks-full-screen" :<br />  // translate here<br />  "The full screen mode is known to cause problems with Internet Explorer, " +<br />  "due to browser bugs that we weren't able to workaround.  You might experience garbage " +<br />  "display, lack of editor functions and/or random browser crashes.  If your system is Windows 9x " +<br />  "it's very likely that you'll get a 'General Protection Fault' and need to reboot.\n\n" +<br />  "You have been warned.  Please press OK if you still want to try the full screen editor."<br /> },</p>
		<p> dialogs: {<br />  "Cancel"                                            : "Cancel",<br />  "Insert/Modify Link"                                : "Insert/Modify Link",<br />  "New window (_blank)"                               : "New window (_blank)",<br />  "None (use implicit)"                               : "None (use implicit)",<br />  "OK"                                                : "OK",<br />  "Other"                                             : "Other",<br />  "Same frame (_self)"                                : "Same frame (_self)",<br />  "Target:"                                           : "Target:",<br />  "Title (tooltip):"                                  : "Title (tooltip):",<br />  "Top frame (_top)"                                  : "Top frame (_top)",<br />  "URL:"                                              : "URL:",<br />  "You must enter the URL where this link points to"  : "You must enter the URL where this link points to"<br /> }<br />};</p>
		<p>呵呵.文件改好了以后,就把这个文件复制到/plugins/下.(在plugins下有5个子文件夹,每个又文件夹下有一个lang文件夹,把gb.js文件放进去就OK了.)<br />然后启动,成功了...^_^</p>
		<p>
				<br />赵永超原创,转载请注明</p>
		<p> </p>
		<p>
				<br /> </p>
<img src ="http://www.blogjava.net/yc1354/aggbug/98583.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-02-07 13:47 <a href="http://www.blogjava.net/yc1354/archive/2007/02/07/98583.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于struts-config里action元素里的attribute属性用途的理解</title><link>http://www.blogjava.net/yc1354/archive/2007/02/06/98372.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Tue, 06 Feb 2007 08:51:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/02/06/98372.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/98372.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/02/06/98372.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/98372.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/98372.html</trackback:ping><description><![CDATA[action mapping中的attribute 属性到底是干什么用的, 很早以前一直不太了解,今天突然又想到了这个问题,感觉不能再扔一边不管了, 一定要解决它.<br /><br /><br />这是在网上查到的关于attribute的解释.<br />1）应用前提，attribute只有在设置了name后才有意义。<br />2）attribute可以实现对象的重用，即如果设置了attribute属性，在创建actionform是，会先去查找相应的scope中是否有此对象，如果有，则重用，否则创建新的对象。<br />3）当你将创建的acitonForm保存到相应的scope中时，你想用一个更有意义的名字来访问它时，它就有意义了。<br /><br />可是,看到"一个更有意义的名字的时候", 我好像有点理解了<br />&lt;action<br />attribute="newLoginForm"<br />name="loginForm"<br />type="loginAction" <br />scope="request"<br />path="/login"&gt;<br /><br /><br />在struts实例化actionform的时候,struts是根据attribute的值来查找并创建actionform，有两种情况：如果已经存在，那么从内存中取回；如果第一次实例化，那么创建，并放入内存。<br /><p>org.apache.struts.util.RequestUtils中的源代码<br /><br />   public static Actionform createActionform(<br />       HttpServletRequest request,<br />       ActionMapping mapping,<br />       ModuleConfig moduleConfig,<br />       ActionServlet servlet) {<br />       ............<br />      ............<br /><br />       String attribute = mapping.getAttribute();<br />      ......<br />       Actionform instance = null;<br />       HttpSession session = null;<br /><br />       if ("request".equals(mapping.getScope())) {<br />           instance = (Actionform) request.getAttribute(attribute);<br />       } else {<br />           session = request.getSession();<br />           instance = (Actionform) session.getAttribute(attribute);<br />       }<br />      ................<br />        ................<br />       <br />       }<br />       <br />如果没有配置attribute属性的话, struts才会从name属性里读出要创建的formbean 的名字,并创建一下实例,看下边的源代码就知道了, 呵呵.<br /><br /><br />org.apache.struts.config.ActionConfig<br /><br />   protected String attribute = null; 
</p><p>   public String getAttribute() {<br /> //就是这里了. <br />       if (this.attribute == null) {<br />           return (this.name);<br />       } else {<br />           return (this.attribute);<br />       }<br />   } 
</p><p>   public void setAttribute(String attribute) {<br />       if (configured) {<br />           throw new IllegalStateException("Configuration is frozen");<br />       }<br />       this.attribute = attribute;<br />   }<br /><br /></p><img src ="http://www.blogjava.net/yc1354/aggbug/98372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-02-06 16:51 <a href="http://www.blogjava.net/yc1354/archive/2007/02/06/98372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts下运用validator框架进行表单验证的常用方法</title><link>http://www.blogjava.net/yc1354/archive/2007/02/06/98275.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Tue, 06 Feb 2007 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/02/06/98275.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/98275.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/02/06/98275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/98275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/98275.html</trackback:ping><description><![CDATA[第一步,在struts-config文件里添加valudator插件<br />&lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;<br />    &lt;set-property property="pathnames" value="/WEB-INF/validator-rules.xml,  /WEB-INF/validation.xml" /&gt;<br />  &lt;/plug-in&gt;<br /><br />在action配置里添加属性<br />  validate="true"<br /><br /><br />第二步,<br /><br />在validation.xml文件里添加要验证的表单字段的信息<br /><br />&lt;form-validation&gt;<br />    &lt;formset&gt;<br /> &lt;form name="logonForm"&gt;<br />  &lt;field<br />     property="ipcode"<br />     depends="required,minlength"&gt;<br />  &lt;arg0 key="LogonForm.ipcode"/&gt;<br />  &lt;arg1 name="minlength" key="${var:minlength}" resource="false"/&gt;<br />          &lt;var&gt;<br />           &lt;var-name&gt;minlength&lt;/var-name&gt;<br />           &lt;var-value&gt;4&lt;/var-value&gt;<br />         &lt;/var&gt;<br />  &lt;/field&gt;<br />  &lt;field<br />     property="password"<br />     depends="required,mask"&gt;<br />     &lt;arg0 key="LogonForm.password"/&gt;<br />     &lt;var&gt;<br />  &lt;var-name&gt;mask&lt;/var-name&gt;<br />  &lt;var-value&gt;^[0-9a-zA-Z]*$&lt;/var-value&gt;<br />     &lt;/var&gt;<br />  &lt;/field&gt;<br />        &lt;/form&gt;<br />    &lt;/formset&gt;<br />&lt;/form-validation&gt;<br /><br /><br />在资源文件里添加<br /><br /><br />validation.test.title=struts validation test<br />errors.required={0} is required.&lt;p&gt;<br />errors.minlength={0} can not be less than {1} characters.&lt;p&gt;<br />errors.maxlength={0} can not be greater than {1} characters.&lt;p&gt;<br />errors.invalid={0} is invalid.
<p>errors.byte={0} must be an byte.<br />errors.short={0} must be an short.<br />errors.integer={0} must be an integer.<br />errors.long={0} must be an long.<br />errors.float={0} must be an float.<br />errors.double={0} must be an double.</p><p>errors.date={0} is not a date.<br />errors.range={0} is not in the range {1} through {2}.<br />errors.creditcard={0} is not a valid credit card number.<br />errors.email={0} is an invalid e-mail address.&lt;p&gt;</p><p>LogonForm.ipcode=&lt;font color=blue&gt;User name&lt;/font&gt;<br />LogonForm.password=&lt;font color=blue&gt;User password&lt;/font&gt;<br /><br />等等你所想要添加的信息<br /><br /></p><img src ="http://www.blogjava.net/yc1354/aggbug/98275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-02-06 13:34 <a href="http://www.blogjava.net/yc1354/archive/2007/02/06/98275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用javascript实现页面跳转的几种方法</title><link>http://www.blogjava.net/yc1354/archive/2007/02/02/97589.html</link><dc:creator>赵永超</dc:creator><author>赵永超</author><pubDate>Fri, 02 Feb 2007 09:18:00 GMT</pubDate><guid>http://www.blogjava.net/yc1354/archive/2007/02/02/97589.html</guid><wfw:comment>http://www.blogjava.net/yc1354/comments/97589.html</wfw:comment><comments>http://www.blogjava.net/yc1354/archive/2007/02/02/97589.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/yc1354/comments/commentRss/97589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yc1354/services/trackbacks/97589.html</trackback:ping><description><![CDATA[按钮式： 
<p>　　&lt;INPUT name="pclog" type="button" value="GO" onClick="location.href='url'"&gt; <br /><br />直接跳转式： </p><p>　　&lt;script&gt;window.location.href='url';&lt;/script&gt;<br /><br />&lt;SCRIPT LANGUAGE="javascript"&gt; <br />&lt;!-- <br />window.open ('page.html', 'newwindow', 'height=100, width=400, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no') <br />//写成一行 <br />--&gt; <br />&lt;/SCRIPT&gt; <br /><br />参数解释： <br /><br /><br />&lt;SCRIPT LANGUAGE="javascript"&gt; js脚本开始； <br />window.open 弹出新窗口的命令； <br />'page.html' 弹出窗口的文件名； <br />'newwindow' 弹出窗口的名字（不是文件名），非必须，可用空''代替； <br />height=100 窗口高度； <br />width=400 窗口宽度； <br />top=0 窗口距离屏幕上方的象素值； <br />left=0 窗口距离屏幕左侧的象素值； <br />toolbar=no 是否显示工具栏，yes为显示； <br />menubar，scrollbars 表示菜单栏和滚动栏。 <br />resizable=no 是否允许改变窗口大小，yes为允许； <br />location=no 是否显示地址栏，yes为允许； <br />status=no 是否显示状态栏内的信息（通常是文件已经打开），yes为允许； <br />&lt;/SCRIPT&gt; js脚本结束 <br /><br />【3、用函数控制弹出窗口】 <br />下面是一个完整的代码。 <br />&lt;html&gt; <br />&lt;head&gt; <br />&lt;script LANGUAGE="JavaScript"&gt; <br />&lt;!-- <br />function openwin() { <br />window.open ("page.html", "newwindow", "height=100, width=400, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no") <br />//写成一行 <br />} <br />//--&gt; <br />&lt;/script&gt; <br />&lt;/head&gt; <br />&lt;body onload="openwin()"&gt; <br />...任意的页面内容... <br />&lt;/body&gt; <br />&lt;/html&gt; <br /><br />这里定义了一个函数openwin(),函数内容就是打开一个窗口。在调用它之前没有任何用途。 <br />怎么调用呢？ <br /><br /><br />方法一：&lt;body onload="openwin()"&gt; 浏览器读页面时弹出窗口； <br />方法二：&lt;body onunload="openwin()"&gt; 浏览器离开页面时弹出窗口； <br />方法三：用一个连接调用： <br />&lt;a href="#" onclick="openwin()"&gt;打开一个窗口&lt;/a&gt; <br />注意：使用的“#”是虚连接。 <br />方法四：用一个按钮调用： <br />&lt;input type="button" onclick="openwin()" value="打开窗口"&gt; <br /><br />【4、同时弹出2个窗口】 <br /><br /><br />对源代码稍微改动一下： <br />&lt;script LANGUAGE="JavaScript"&gt; <br />&lt;!-- <br />function openwin() { <br />window.open ("page.html", "newwindow", "height=100, width=100, top=0, left=0,toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no") <br />//写成一行 <br />window.open ("page2.html", "newwindow2", "height=100, width=100, top=100, left=100,toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no") <br />//写成一行 <br />} <br />//--&gt; <br />&lt;/script&gt; <br /><br /><br />为避免弹出的2个窗口覆盖，用top和left控制一下弹出的位置不要相互覆盖即可。最后用上面说过的四种方法调用即可。 <br />注意：2个窗口的name(newwindows和newwindow2)不要相同，或者干脆全部为空。OK？ <br /><br />【5、主窗口打开文件1.htm，同时弹出小窗口page.html】 <br /><br />如下代码加入主窗口&lt;head&gt;区： <br /><br />&lt;script language="javascript"&gt; <br />&lt;!-- <br />function openwin() { <br />window.open("page.html","","width=200,height=200") <br />} <br />//--&gt; <br />&lt;/script&gt; <br />加入&lt;body&gt;区： <br />&lt;a href="/blog/1.htm" onclick="openwin()"&gt;open&lt;/a&gt;即可。 <br /><br />【6、弹出的窗口之定时关闭控制】 <br /><br />下面我们再对弹出的窗口进行一些控制，效果就更好了。如果我们再将一小段代码加入弹出的页面(注意是加入到page.html的HTML中，可不是主页面中，否则...)，让它10秒后自动关闭是不是更酷了？ <br /><br /><br />首先，将如下代码加入page.html文件的&lt;head&gt;区： <br />&lt;script language="JavaScript"&gt; <br /><br />function closeit() { <br /><br />setTimeout("self.close()",10000) //毫秒 <br /><br />} <br /><br />&lt;/script&gt; <br />然后，再用&lt;body onload="closeit()"&gt; 这一句话代替page.html中原有的&lt;BODY&gt;这一句就可以了。(这一句话千万不要忘记写啊！这一句的作用是调用关闭窗口的代码，10秒钟后就自行关闭该窗口。) <br /><br />【7、在弹出窗口中加上一个关闭按钮】 <br />&lt;FORM&gt; <br />&lt;INPUT TYPE='BUTTON' VALUE='关闭' onClick='window.close()'&gt; <br />&lt;/FORM&gt; <br />呵呵，现在更加完美了！ <br /><br />【8、内包含的弹出窗口-一个页面两个窗口】 <br /><br />上面的例子都包含两个窗口，一个是主窗口，另一个是弹出的小窗口。 <br />通过下面的例子，你可以在一个页面内完成上面的效果。 <br /><br />&lt;html&gt; <br />&lt;head&gt; <br />&lt;SCRIPT LANGUAGE="JavaScript"&gt; <br />function openwin() <br />{ <br />OpenWindow=window.open("", "newwin", "height=250, width=250,toolbar=no,scrollbars="+scroll+",menubar=no"); <br />//写成一行 <br />OpenWindow.document.write("&lt;TITLE&gt;例子&lt;/TITLE&gt;") <br />OpenWindow.document.write("&lt;BODY BGCOLOR=#ffffff&gt;") <br />OpenWindow.document.write("&lt;h1&gt;Hello!&lt;/h1&gt;") <br />OpenWindow.document.write("New window opened!") <br />OpenWindow.document.write("&lt;/BODY&gt;") <br />OpenWindow.document.write("&lt;/HTML&gt;") <br />OpenWindow.document.close() <br />} <br />&lt;/SCRIPT&gt; <br />&lt;/head&gt; <br />&lt;body&gt; <br />&lt;a href="#" onclick="openwin()"&gt;打开一个窗口&lt;/a&gt; <br />&lt;input type="button" onclick="openwin()" value="打开窗口"&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br /><br />看看 OpenWindow.document.write()里面的代码不就是标准的HTML吗？只要按照格式写更多的行即可。千万注意多一个标签或少一个标签就会出现错误。记得用OpenWindow.document.close()结束啊。 <br /><br /><br />【9、终极应用--弹出的窗口之Cookie控制】 <br /><br />回想一下，上面的弹出窗口虽然酷，但是有一点小毛病(沉浸在喜悦之中，一定没有发现吧？)比如你将上面的脚本放在一个需要频繁经过的页面里(例如首页)，那么每次刷新这个页面，窗口都会弹出一次，是不是非常烦人？:-( <br />有解决的办法吗？Yes! ;-) Follow me. <br />我们使用cookie来控制一下就可以了。 <br />首先，将如下代码加入主页面HTML的&lt;HEAD&gt;区： <br /><br />&lt;script&gt; <br />function openwin(){ <br />window.open("page.html","","width=200,height=200") <br />} <br />function get_cookie(Name) { <br />var search = Name + "=" <br />var returnvalue = ""; <br />if (document.cookie.length &gt; 0) { <br />offset = document.cookie.indexOf(search) <br />if (offset != -1) { <br />offset += search.length <br />end = document.cookie.indexOf(";", offset); <br />if (end == -1) <br />end = document.cookie.length; <br />returnvalue="/blog/unescape(document.cookie.substring(offset," end)) <br />} <br />} <br />return returnvalue; <br />} <br /><br />function loadpopup(){ <br />if (get_cookie('popped')==''){ <br />openwin() <br />document.cookie="popped=yes" <br />} <br />} <br /><br />&lt;/script&gt; <br /><br />然后，用&lt;body onload="loadpopup()"&gt;（注意不是openwin而是loadpop啊！）替换主页面中原有的&lt;BODY&gt;这一句即可。你可以试着刷新一下这个页面或重新进入该页面，窗口再也不会弹出了。真正的Pop-Only-Once！<br /></p><img src ="http://www.blogjava.net/yc1354/aggbug/97589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yc1354/" target="_blank">赵永超</a> 2007-02-02 17:18 <a href="http://www.blogjava.net/yc1354/archive/2007/02/02/97589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>