<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Iciness's space...]]></title> 
<link>http://www.newtyper.com/blog/index.php</link> 
<description><![CDATA[-心静止水-]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Iciness's space...]]></copyright>
<item>
<link>http://www.newtyper.com/blog/post/175/</link>
<title><![CDATA[Java多线程初学者指南]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 06 Jul 2009 10:21:34 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/175/</guid> 
<description>
<![CDATA[ 
	<p>转载至blogjava的<a href="http://www.blogjava.net/nokiaguy/" target="_blank">真的有外星人吗？</a></p>&nbsp;&nbsp;<p><strong>第一节 线程简介</strong></p>&nbsp;&nbsp;<p><strong>一、线程概述</strong></p>&nbsp;&nbsp;<p>线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统，如微软早期的DOS)在执行一个程序时，会在系统中建立一个进程，而在这个进程中，必须至少建立一个线程（这个线程被称为主线程）来作为这个程序运行的入口点。因此，在操作系统中运行的任何程序都至少有一个主线程。</p>&nbsp;&nbsp;<p>进程和线程是现代操作系统中两个必不可少的运行模型。在操作系统中可以有多个进程，这些进程包括系统进程（由操作系统内部建立的进程）和用户进程（由用户程序建立的进程）；一个进程中可以有一个或多个线程。进程和进程之间不共享内存，也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线可以共享系统分派给这个进程的内存空间。</p>&nbsp;&nbsp;<p>线程不仅可以共享进程的内存，而且还拥有一个属于自己的内存空间，这段内存空间也叫做线程栈, 是在建立线程时由系统分配的，主要用来保存线程内部所使用的数据，如线程执行函数中所定义的变量。</p>&nbsp;&nbsp;<p><strong>注意：</strong>任何一个线程在建立时都会执行一个函数，这个函数叫做线程执行函数。也可以将这个函数看做线程的入口点（类似于程序中的main函数）。无论使用什么语言或技术来建立线程，都必须执行这个函数（这个函数的表现形式可能不一样，但都会有一个这样的函数）。如在Windows中用于建立线程的API函数CreateThread的第三个参数就是这个执行函数的指针。</p>&nbsp;&nbsp;<p>在操作系统将进程分成多个线程后，这些线程可以在操作系统的管理下并发执行，从而大大提高了程序的运行效率。虽然线程的执行从宏观上看是多个线程同时执行，但实际上这只是操作系统的障眼法。由于一块CPU同时只能执行一条指令，因此，在拥有一块CPU的计算机上不可能同时执行两个任务。而操作系统为了能提高程序的运行效率，在一个线程空闲时会撤下这个线程，并且会让其他的线程来执行，这种方式叫做线程调度。我们之所以从表面上看是多个线程同时执行，是因为不同线程之间切换的时间非常短，而且在一般情况下切换非常频繁。假设我们有线程A和B。在运行时，可能是A执行了1毫秒后，切换到B后，B又执行了1毫秒，然后又切换到了A，A又执行1毫秒。由于1毫秒的时间对于普通人来说是很难感知的，因此，从表面看上去就象A和B同时执行一样，但实际上A和B是交替执行的。</p>&nbsp;&nbsp;<p><strong>二、线程给我们带来的好处</strong></p>&nbsp;&nbsp;<p>如果能合理地使用线程，将会减少开发和维护成本，甚至可以改善复杂应用程序的性能。如在GUI应用程序中，还以通过线程的异步特性来更好地处理事件；在应用服务器程序中可以通过建立多个线程来处理客户端的请求。线程甚至还可以简化虚拟机的实现，如Java虚拟机(JVM)的垃圾回收器（garbage collector）通常运行在一个或多个线程中。因此，使用线程将会从以下五个方面来改善我们的应用程序：</p>&nbsp;&nbsp;<p><strong>1. </strong><strong>充分利用</strong><strong>CPU</strong><strong>资源</strong></p>&nbsp;&nbsp;<p>现在世界上大多数计算机只有一块CPU。因此，充分利用CPU资源显得尤为重要。当执行单线程程序时，由于在程序发生阻塞时CPU可能会处于空闲状态。这将造成大量的计算资源的浪费。而在程序中使用多线程可以在某一个线程处于休眠或阻塞时，而CPU又恰好处于空闲状态时来运行其他的线程。这样CPU就很难有空闲的时候。因此，CPU资源就得到了充分地利用。</p>&nbsp;&nbsp;<p><strong>2. </strong><strong>简化编程模型</strong></p>&nbsp;&nbsp;<p>如果程序只完成一项任务，那只要写一个单线程的程序，并且按着执行这个任务的步骤编写代码即可。但要完成多项任务，如果还使用单线程的话，那就得在在程序中判断每项任务是否应该执行以及什么时候执行。如显示一个时钟的时、分、秒三个指针。使用单线程就得在循环中逐一判断这三个指针的转动时间和角度。如果使用三个线程分另来处理这三个指针的显示，那么对于每个线程来说就是指行一个单独的任务。这样有助于开发人员对程序的理解和维护。</p>&nbsp;&nbsp;<p><strong>3. </strong><strong>简化异步事件的处理</strong></p>&nbsp;&nbsp;<p>当一个服务器应用程序在接收不同的客户端连接时最简单地处理方法就是为每一个客户端连接建立一个线程。然后监听线程仍然负责监听来自客户端的请求。如果这种应用程序采用单线程来处理，当监听线程接收到一个客户端请求后，开始读取客户端发来的数据，在读完数据后，read方法处于阻塞状态，也就是说，这个线程将无法再监听客户端请求了。而要想在单线程中处理多个客户端请求，就必须使用非阻塞的Socket连接和异步I/O。但使用异步I/O方式比使用同步I/O更难以控制，也更容易出错。因此，使用多线程和同步I/O可以更容易地处理类似于多请求的异步事件。</p>&nbsp;&nbsp;<p><strong>4. </strong><strong>使</strong><strong>GUI</strong><strong>更有效率</strong></p>&nbsp;&nbsp;<p>使用单线程来处理GUI事件时，必须使用循环来对随时可能发生的GUI事件进行扫描，在循环内部除了扫描GUI事件外，还得来执行其他的程序代码。如果这些代码太长，那么GUI事件就会被&ldquo;冻结&rdquo;，直到这些代码被执行完为止。</p>&nbsp;&nbsp;<p>在现代的GUI框架（如SWING、AWT和SWT）中都使用了一个单独的事件分派线程（event dispatch thread，EDT）来对GUI事件进行扫描。当我们按下一个按钮时，按钮的单击事件函数会在这个事件分派线程中被调用。由于EDT的任务只是对GUI事件进行扫描，因此，这种方式对事件的反映是非常快的。</p>&nbsp;&nbsp;<p><strong>5. </strong><strong>节约成本</strong></p>&nbsp;&nbsp;<p>提高程序的执行效率一般有三种方法：</p>&nbsp;&nbsp;<p>（1）增加计算机的CPU个数。</p>&nbsp;&nbsp;<p>（2）为一个程序启动多个进程</p>&nbsp;&nbsp;<p>（3）在程序中使用多进程。</p>&nbsp;&nbsp;<p>第一种方法是最容易做到的，但同时也是最昂贵的。这种方法不需要修改程序，从理论上说，任何程序都可以使用这种方法来提高执行效率。第二种方法虽然不用购买新的硬件，但这种方式不容易共享数据，如果这个程序要完成的任务需要必须要共享数据的话，这种方式就不太方便，而且启动多个线程会消耗大量的系统资源。第三种方法恰好弥补了第一种方法的缺点，而又继承了它们的优点。也就是说，既不需要购买CPU，也不会因为启太多的线程而占用大量的系统资源（在默认情况下，一个线程所占的内存空间要远比一个进程所占的内存空间小得多），并且多线程可以模拟多块CPU的运行方式，因此，使用多线程是提高程序执行效率的最廉价的方式。</p>&nbsp;&nbsp;<p><strong>三、</strong><strong>Java</strong><strong>的线程模型</strong></p>&nbsp;&nbsp;<p>由于Java是纯面向对象语言，因此，Java的线程模型也是面向对象的。Java通过Thread类将线程所必须的功能都封装了起来。要想建立一个线程，必须要有一个线程执行函数，这个线程执行函数对应Thread类的run方法。Thread类还有一个start方法，这个方法负责建立线程，相当于调用Windows的建立线程函数CreateThread。当调用start方法后，如果线程建立成功，并自动调用Thread类的run方法。因此，任何继承Thread的Java类都可以通过Thread类的start方法来建立线程。如果想运行自己的线程执行函数，那就要覆盖Thread类的run方法。</p>&nbsp;&nbsp;<p>在Java的线程模型中除了Thread类，还有一个标识某个Java类是否可作为线程类的接口Runnable，这个接口只有一个抽象方法run，也就是Java线程模型的线程执行函数。因此，一个线程类的唯一标准就是这个类是否实现了Runnable接口的run方法，也就是说，拥有线程执行函数的类就是线程类。</p>&nbsp;&nbsp;<p>从上面可以看出，在Java中建立线程有两种方法，一种是继承Thread类，另一种是实现Runnable接口，并通过Thread和实现Runnable的类来建立线程，其实这两种方法从本质上说是一种方法，即都是通过Thread类来建立线程，并运行run方法的。但它们的大区别是通过继承Thread类来建立线程，虽然在实现起来更容易，但由于Java不支持多继承，因此，这个线程类如果继承了Thread，就不能再继承其他的类了，因此，Java线程模型提供了通过实现Runnable接口的方法来建立线程，这样线程类可以在必要的时候继承和业务有关的类，而不是Thread类。</p>&nbsp;&nbsp;<p><strong>第二节 用Thread类创建线程</strong></p>&nbsp;&nbsp;<p>在Java中创建线程有两种方法：使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例。因此，无论是通过Thread类还是Runnable接口建立线程，都必须建立Thread类或它的子类的实例。Thread类的构造方法被重载了八次，构造方法如下：</p>&nbsp;&nbsp;<p>public Thread( );&nbsp;&nbsp;&nbsp;&nbsp;<br />public Thread(Runnable target);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(Runnable target, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target, String name, long stackSize);</p>&nbsp;&nbsp;<p><strong>Runnable target</strong></p>&nbsp;&nbsp;<p>实现了Runnable接口的类的实例。要注意的是Thread类也实现了Runnable接口，因此，从Thread类继承的类的实例也可以作为target传入这个构造方法。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>String name</strong></p>&nbsp;&nbsp;<p>线程的名子。这个名子可以在建立Thread实例后通过Thread类的setName方法设置。如果不设置线程的名子，线程就使用默认的线程名：Thread-N，N是线程建立的顺序，是一个不重复的正整数。</p>&nbsp;&nbsp;<p><strong>ThreadGroup group</strong></p>&nbsp;&nbsp;<p>当前建立的线程所属的线程组。如果不指定线程组，所有的线程都被加到一个默认的线程组中。关于线程组的细节将在后面的章节详细讨论。</p>&nbsp;&nbsp;<p><strong>long stackSize</strong></p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 线程栈的大小，这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下，默认的线程栈大小是12KB。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>一个普通的Java类只要从Thread类继承，就可以成为一个线程类。并可通过Thread类的start方法来执行线程代码。虽然Thread类的子类可以直接实例化，但在子类中必须要覆盖Thread类的run方法才能真正运行线程的代码。下面的代码给出了一个使用Thread类建立线程的例子：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class Thread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(this.getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />009 public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread1 thread1 = new Thread1();&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread1 thread2 = new Thread1 ();&nbsp;&nbsp;&nbsp;&nbsp; <br />014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017&nbsp; &#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 上面的代码建立了两个线程：thread1和thread2。上述代码中的005至008行是Thread1类的run方法。当在014和015行调用 start方法时，系统会自动调用run方法。在007行使用this.getName()输出了当前线程的名字，由于在建立线程时并未指定线程名，因此，所输出的线程名是系统的默认值，也就是Thread-n的形式。在011行输出了主线程的线程名。&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>main&nbsp;&nbsp;&nbsp;&nbsp;<br />Thread-0&nbsp;&nbsp;&nbsp;&nbsp; <br />Thread-1</p>&nbsp;&nbsp;<p>从上面的输出结果可以看出，第一行输出的main是主线程的名子。后面的Thread-1和Thread-2分别是thread1和thread2的输出结果。</p>&nbsp;&nbsp;<p><strong>注意：</strong>任何一个Java程序都必须有一个主线程。一般这个主线程的名子为main。只有在程序中建立另外的线程，才能算是真正的多线程程序。也就是说，多线程程序必须拥有一个以上的线程。&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; Thread类有一个重载构造方法可以设置线程名。除了使用构造方法在建立线程时设置线程名，还可以使用Thread类的setName方法修改线程名。要想通过Thread类的构造方法来设置线程名，必须在Thread的子类中使用Thread类的public Thread(String name)构造方法，因此，必须在Thread的子类中也添加一个用于传入线程名的构造方法。下面的代码给出了一个设置线程名的例子：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class Thread2 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 private String who;&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp; <br />007 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(who + &quot;:&quot; + this.getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />011 public Thread2(String who)&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />013 super();&nbsp;&nbsp;&nbsp;&nbsp; <br />014 this.who = who;&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016 public Thread2(String who, String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />018 super(name);&nbsp;&nbsp;&nbsp;&nbsp; <br />019 this.who = who;&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021 public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread1 = new Thread2 (&quot;thread1&quot;, &quot;MyThread1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread2 = new Thread2 (&quot;thread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread3 = new Thread2 (&quot;thread3&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.setName(&quot;MyThread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread3.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />031</p>&nbsp;&nbsp;<p>在类中有两个构造方法：</p>&nbsp;&nbsp;<p><strong>第</strong><strong>011</strong><strong>行：</strong><strong>public sample2_2(String who)</strong></p>&nbsp;&nbsp;<p>这个构造方法有一个参数：who。这个参数用来标识当前建立的线程。在这个构造方法中仍然调用Thread的默认构造方法public Thread( )。</p>&nbsp;&nbsp;<p><strong>第</strong><strong>016</strong><strong>行：</strong><strong>public sample2_2(String who, String name)</strong></p>&nbsp;&nbsp;<p>这个构造方法中的who和第一个构造方法的who的含义一样，而name参数就是线程的名名。在这个构造方法中调用了Thread类的public Thread(String name)构造方法，也就是第018行的super(name)。</p>&nbsp;&nbsp;<p>在main方法中建立了三个线程：thread1、thread2和thread3。其中thread1通过构造方法来设置线程名，thread2通过setName方法来修改线程名，thread3未设置线程名。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 运行结果如下：</p>&nbsp;&nbsp;<p>thread1:MyThread1&nbsp;&nbsp;&nbsp;&nbsp;<br />thread2:MyThread2&nbsp;&nbsp;&nbsp;&nbsp; <br />thread3:Thread-1</p>&nbsp;&nbsp;<p>从上面的输出结果可以看出，thread1和thread2的线程名都已经修改了，而thread3的线程名仍然为默认值：Thread-1。thread3的线程名之所以不是Thread-2，而是Thread-1，这是因为在026行已经指定了thread2的Name，因此，启动thread3时就将thread3的线程名设为Thread-1。因此就会得到上面的输出结果。</p>&nbsp;&nbsp;<p><strong>注意：</strong>在调用start方法前后都可以使用setName设置线程名，但在调用start方法后使用setName修改线程名，会产生不确定性，也就是说可能在run方法执行完后才会执行setName。如果在run方法中要使用线程名，就会出现虽然调用了setName方法，但线程名却未修改的现象。</p>&nbsp;&nbsp;<p>Thread类的start方法不能多次调用，如不能调用两次thread1.start()方法。否则会抛出一个IllegalThreadStateException异常。</p>&nbsp;&nbsp;<p>&nbsp;</p>&nbsp;&nbsp;<p><strong>第三节 使用Runnable接口创建线程</strong></p>&nbsp;&nbsp;<p>实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为两步：</p>&nbsp;&nbsp;<p>1. 将实现Runnable接口的类实例化。</p>&nbsp;&nbsp;<p>2. 建立一个Thread对象，并将第一步实例化后的对象作为参数传入Thread类的构造方法。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp; 最后通过Thread类的start方法建立线程。</p>&nbsp;&nbsp;<p>下面的代码演示了如何使用Runnable接口来创建线程：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyRunnable implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyRunnable t1 = new MyRunnable();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyRunnable t2 = new MyRunnable();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread1 = new Thread(t1, &quot;MyThread1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread2 = new Thread(t2);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.setName(&quot;MyThread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>MyThread1&nbsp;&nbsp;&nbsp;&nbsp;<br />MyThread2</p>&nbsp;&nbsp;<p><strong>第四节 线程的生命周期</strong></p>&nbsp;&nbsp;<p>与人有生老病死一样，线程也同样要经历开始（等待）、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。下面给出了Thread类中和这四种状态相关的方法。</p>&nbsp;&nbsp;<p>// 开始线程&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; public void start( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void run( );&nbsp;&nbsp;&nbsp;&nbsp; <br />// 挂起和唤醒线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void resume( );&nbsp;&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void suspend( );&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void sleep(long millis);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static void sleep(long millis, int nanos);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // 终止线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void stop( );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />public void interrupt( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // 得到线程状态&nbsp;&nbsp;&nbsp;&nbsp; <br />public boolean isAlive( );&nbsp;&nbsp;&nbsp;&nbsp; <br />public boolean isInterrupted( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static boolean interrupted( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // join方法&nbsp;&nbsp;&nbsp;&nbsp; <br />public void join( ) throws InterruptedException;</p>&nbsp;&nbsp;<p><strong>一、创建并运行线程</strong></p>&nbsp;&nbsp;<p>线程在建立后并不马上执行run方法中的代码，而是处于等待状态。线程处于等待状态时，可以通过Thread类的方法来设置线程不各种属性，如线程的优先级（setPriority）、线程名(setName)和线程的类型（setDaemon）等。</p>&nbsp;&nbsp;<p>当调用start方法后，线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时，isAlive返回true，当isAlive返回false时，可能线程处于等待状态，也可能处于停止状态。下面的代码演示了线程的创建、运行和停止三个状态之间的切换，并输出了相应的isAlive返回值。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class LifeCycle extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />while ((++n) &lt; 1000);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LifeCycle thread1 = new LifeCycle();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.join();&nbsp; // 等线程thread1结束后再继续执行&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;thread1已经结束!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>要注意一下，在上面的代码中使用了join方法，这个方法的主要功能是保证线程的run方法完成后程序才继续运行，这个方法将在后面的文章中介绍&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 上面代码的运行结果：</p>&nbsp;&nbsp;<p>isAlive: false&nbsp;&nbsp;&nbsp;&nbsp;<br />isAlive: true&nbsp;&nbsp;&nbsp;&nbsp; <br />thread1已经结束!&nbsp;&nbsp;&nbsp;&nbsp; <br />isAlive: false</p>&nbsp;&nbsp;<p><strong>二、挂起和唤醒线程</strong></p>&nbsp;&nbsp;<p>一但线程开始执行run方法，就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中，可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后，可以通过resume方法唤醒线程。而使用sleep使线程休眠后，只能在设定的时间后使线程处于就绪状态（在线程休眠结束后，线程不一定会马上执行，只是进入了就绪状态，等待着系统进行调度）。</p>&nbsp;&nbsp;<p>虽然suspend和resume可以很方便地使线程挂起和唤醒，但由于使用这两个方法可能会造成一些不可预料的事情发生，因此，这两个方法被标识为deprecated(抗议)标记，这表明在以后的jdk版本中这两个方法可能被删除，所以尽量不要使用这两个方法来操作线程。下面的代码演示了sleep、suspend和resume三个方法的使用。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />class SleepThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(2000);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(new java.util.Date().getTime());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread thread = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SleepThread sleepThread = thread.new SleepThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleepThread.start(); // 开始运行线程sleepThread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleepThread.join();&nbsp; // 使线程sleepThread延迟2秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />boolean flag = false;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(5000);&nbsp; // 使主线程延迟5秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flag = !flag;&nbsp;&nbsp;&nbsp;&nbsp; <br />if (flag)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.suspend();&nbsp; <br />else&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.resume();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>从表面上看，使用sleep和suspend所产生的效果类似，但sleep方法并不等同于suspend。它们之间最大的一个区别是可以在一个线程中通过suspend方法来挂起另外一个线程，如上面代码中在主线程中挂起了thread线程。而sleep只对当前正在执行的线程起作用。在上面代码中分别使sleepThread和主线程休眠了2秒和5秒。在使用sleep时要注意，不能在一个线程中来休眠另一个线程。如main方法中使用thread.sleep(2000)方法是无法使thread线程休眠2秒的，而只能使主线程休眠2秒。</p>&nbsp;&nbsp;<p>在使用sleep方法时有两点需要注意：</p>&nbsp;&nbsp;<p>1. sleep方法有两个重载形式，其中一个重载形式不仅可以设毫秒，而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒，因此，如果对sleep设置了纳秒，Java虚拟机将取最接近这个值的毫秒。</p>&nbsp;&nbsp;<p>2. 在使用sleep方法时必须使用throws或try&#123;...&#125;catch&#123;...&#125;。因为run方法无法使用throws，所以只能使用try&#123;...&#125;catch&#123;...&#125;。当在线程休眠的过程中，使用interrupt方法（这个方法将在2.3.3中讨论）中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下：</p>&nbsp;&nbsp;<p>public static void sleep(long millis)&nbsp; throws InterruptedException&nbsp;&nbsp;&nbsp;&nbsp;<br />public static void sleep(long millis,&nbsp; int nanos)&nbsp; throws InterruptedException</p>&nbsp;&nbsp;<p><strong>三、终止线程的三种方法</strong></p>&nbsp;&nbsp;<p>有三种方法可以使终止线程。</p>&nbsp;&nbsp;<p>1.&nbsp; 使用退出标志，使线程正常退出，也就是当run方法完成后线程终止。</p>&nbsp;&nbsp;<p>2.&nbsp; 使用stop方法强行终止线程（这个方法不推荐使用，因为stop和suspend、resume一样，也可能发生不可预料的结果）。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 3.&nbsp; 使用interrupt方法中断线程。<strong> </strong></p>&nbsp;&nbsp;<p><strong>1. </strong><strong>使用退出标志终止线程</strong></p>&nbsp;&nbsp;<p>当run方法执行完后，线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求，或是其他的需要循环处理的任务。在这种情况下，一般是将这些任务放在一个循环中，如while循环。如果想让循环永远运行下去，可以使用while(true)&#123;...&#125;来处理。但要想使while循环在某一特定条件下退出，最直接的方法就是设一个boolean类型的标志，并通过设置这个标志为true或false来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class ThreadFlag extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public volatile boolean exit = false;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (!exit);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadFlag thread = new ThreadFlag();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(5000); // 主线程延迟5秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.exit = true;&nbsp; // 终止线程thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;线程退出!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面代码中定义了一个退出标志exit，当exit为true时，while循环退出，exit的默认值为false。在定义exit时，使用了一个Java关键字volatile，这个关键字的目的是使exit同步，也就是说在同一时刻只能由一个线程来修改exit的值，</p>&nbsp;&nbsp;<p><strong>2. </strong><strong>使用</strong><strong>stop</strong><strong>方法终止线程</strong></p>&nbsp;&nbsp;<p>使用stop方法可以强行终止正在运行或挂起的线程。我们可以使用如下的代码来终止线程：</p>&nbsp;&nbsp;<p>thread.stop();</p>&nbsp;&nbsp;<p>虽然使用上面的代码可以终止线程，但使用stop方法是很危险的，就象突然关闭计算机电源，而不是按正常程序关机一样，可能会产生不可预料的结果，因此，并不推荐使用stop方法来终止线程。</p>&nbsp;&nbsp;<p><strong>3. </strong><strong>使用</strong><strong>interrupt</strong><strong>方法终止线程</strong></p>&nbsp;&nbsp;<p>使用interrupt方法来终端线程可分为两种情况：</p>&nbsp;&nbsp;<p>（1）线程处于阻塞状态，如使用了sleep方法。</p>&nbsp;&nbsp;<p>（2）使用while(!isInterrupted())&#123;...&#125;来判断线程是否被中断。</p>&nbsp;&nbsp;<p>在第一种情况下使用interrupt方法，sleep方法将抛出一个InterruptedException例外，而在第二种情况下线程将直接退出。下面的代码演示了在第一种情况下使用interrupt方法。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class ThreadInterrupt extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(50000);&nbsp; // 延迟50秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (InterruptedException e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e.getMessage());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new ThreadInterrupt();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;在50秒之内按任意键中断线程!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.in.read();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.interrupt();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;线程已经退出!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 在50秒之内按任意键中断线程!&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; sleep interrupted&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; 线程已经退出!</p>&nbsp;&nbsp;<p>在调用interrupt方法后， sleep方法抛出异常，然后输出错误信息：sleep interrupted。</p>&nbsp;&nbsp;<p>注意：在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted()，一个是非静态的方法isInterrupted()，这两个方法的区别是interrupted用来判断当前线是否被中断，而isInterrupted可以用来判断其他线程是否被中断。因此，while (!isInterrupted())也可以换成while (!Thread.interrupted())。</p>&nbsp;&nbsp;<p><strong>第五节 join方法的使用</strong></p>&nbsp;&nbsp;<p>在上面的例子中多次使用到了Thread类的join方法。我想大家可能已经猜出来join方法的功能是什么了。对，join方法的功能就是使异步执行的线程变成同步执行。也就是说，当调用线程实例的start方法后，这个方法会立即返回，如果在调用start方法后后需要使用一个由这个线程计算得到的值，就必须使用join方法。如果不使用join方法，就不能保证当执行到start方法后面的某条语句时，这个线程一定会执行完。而使用join方法后，直到这个线程退出，程序才会往下执行。下面的代码演示了join的用法。</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />static synchronized void inc()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inc();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3);&nbsp; // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp; // 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp; // 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />if (args.length &gt; 0)&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp; // 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在例程2-8中建立了100个线程，每个线程使静态变量n增加10。如果在这100个线程都执行完后输出n，这个n值应该是1000。</p>&nbsp;&nbsp;<p><strong>1.&nbsp; </strong><strong>测试</strong><strong>1</strong></p>&nbsp;&nbsp;<p>使用如下的命令运行上面程序：</p>&nbsp;&nbsp;<p>java mythread.JoinThread</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>n=442</p>&nbsp;&nbsp;<p>这个运行结果可能在不同的运行环境下有一些差异，但一般n不会等于1000。从上面的结果可以肯定，这100个线程并未都执行完就将n输出了。</p>&nbsp;&nbsp;<p><strong>2.&nbsp; 测试</strong><strong>2</strong></p>&nbsp;&nbsp;<p>使用如下的命令运行上面的代码：</p>&nbsp;&nbsp;<p>在上面的命令行中有一个参数join，其实在命令行中可以使用任何参数，只要有一个参数就可以，这里使用join，只是为了表明要使用join方法使这100个线程同步执行。</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>n=1000</p>&nbsp;&nbsp;<p>无论在什么样的运行环境下运行上面的命令，都会得到相同的结果：n=1000。这充分说明了这100个线程肯定是都执行完了，因此，n一定会等于1000。</p>&nbsp;&nbsp;<p><strong>第六节 慎重使用volatile关键字</strong></p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量，如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile，对它们的操作就会变成原子级别的。但这有一定的限制。例如，下面的例子中的n就不是原子级别的：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static volatile int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n = n + 1;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3); // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 如果对n的操作是原子级别的，最后输出的结果应该为n=1000，而在执行上面积代码时，很多时侯输出的n都小于1000，这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关，那么volatile关键字不起作用，也就是说如下的表达式都不是原子操作：</p>&nbsp;&nbsp;<p>n = n + 1;&nbsp;&nbsp;&nbsp;&nbsp;<br />n++;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果要想使这种情况变成原子操作，需要使用synchronized关键字，如上的代码可以改成如下的形式：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void inc()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inc(); // n = n + 1 改成了 inc();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3); // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 上面的代码将n=n+1改成了inc()，其中inc方法使用了synchronized关键字进行方法同步。因此，在使用volatile关键字时要慎重，并不是只要简单类型变量使用volatile修饰，对这个变量的所有操作都是原来操作，当变量的值由自身的上一个决定时，如n=n+1、n++ 等，volatile关键字将失效，只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的，如n = m + 1，这个就是原级别的。所以在使用volatile关键时一定要谨慎，如果自己没有把握，可以使用synchronized来代替volatile。</p>&nbsp;&nbsp;<p><strong>第七节 向线程传递数据的三种方法</strong></p>&nbsp;&nbsp;<p>在传统的同步开发模式下，当我们调用一个函数时，通过这个函数的参数将数据传入，并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下，数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的，因此，在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。本文就以上原因介绍了几种用于向线程传递数据的方法，在下一篇文章中将介绍从线程中返回数据的方法。</p>&nbsp;&nbsp;<p>欲先取之，必先予之。一般在使用线程时都需要有一些初始化数据，然后线程利用这些数据进行加工处理，并返回结果。在这个过程中最先要做的就是向线程中传递数据。</p>&nbsp;&nbsp;<p><strong>一、通过构造方法传递数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>在创建线程时，必须要建立一个Thread类的或其子类的实例。因此，我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来，以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String name;&nbsp;&nbsp;&nbsp;&nbsp; <br />public MyThread1(String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.name = name;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;hello &quot; + name);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new MyThread1(&quot;world&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp; 由于这种方法是在创建线程对象的同时传递数据的，因此，在线程运行之前这些数据就就已经到位了，这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据，可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全，但如果要传递的数据比较多时，就会造成很多不便。由于Java没有默认参数，要想实现类似默认参数的效果，就得使用重载，这样不但使构造方法本身过于复杂，又会使构造方法在数量上大增。因此，要想避免这种情况，就得通过类方法或类变量来传递数据。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>二、通过变量和方法传递数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>向对象中传入数据一般有两次机会，第一次机会是在建立对象时通过构造方法将数据传入，另外一次机会就是在类中定义一系列的public的方法或变量（也可称之为字段）。然后在建立完对象后，通过对象实例逐个赋值。下面的代码是对MyThread1类的改版，使用了一个setName方法来设置name变量：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread2 implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String name;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void setName(String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.name = name;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;hello &quot; + name);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread2 myThread = new MyThread2();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myThread.setName(&quot;world&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new Thread(myThread);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>三、</strong><strong>通过回调函数传递数据</strong></p>&nbsp;&nbsp;<p>上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说，是被动接收这些数据的。然而，在有些应用中需要在线程运行的过程中动态地获取数据，如在下面代码的run方法中产生了3个随机数，然后通过Work类的process方法求这三个随机数的和，并通过Data类的value将结果返回。从这个例子可以看出，在返回value之前，必须要得到三个随机数。也就是说，这个value是无法事先就传入线程类的。</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />class Data&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public int value = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Work&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void process(Data data, Integer&hellip; numbers)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int n : numbers)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data.value += n;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class MyThread3 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Work work;&nbsp;&nbsp;&nbsp;&nbsp; <br />public MyThread3(Work work)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.work = work;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.Random random = new java.util.Random();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Data data = new Data();&nbsp;&nbsp;&nbsp;&nbsp; <br />int n1 = random.nextInt(1000);&nbsp;&nbsp;&nbsp;&nbsp; <br />int n2 = random.nextInt(2000);&nbsp;&nbsp;&nbsp;&nbsp; <br />int n3 = random.nextInt(3000);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; work.process(data, n1, n2, n3);&nbsp;&nbsp; // 使用回调函数&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(String.valueOf(n1) + &quot;+&quot; + String.valueOf(n2) + &quot;+&quot;&nbsp;&nbsp;&nbsp;&nbsp; <br />+ String.valueOf(n3) + &quot;=&quot; + data.value);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new MyThread3(new Work());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面代码中的process方法被称为回调函数。从本质上说，回调函数就是事件函数。在Windows API中常使用回调函数和调用API的程序之间进行数据交互。因此，调用回调函数的过程就是最原始的引发事件的过程。在这个例子中调用了process方法来获得数据也就相当于在run方法中引发了一个事件。</p>&nbsp;&nbsp;<p><strong>第八节 从线程返回数据的两种方法</strong></p>&nbsp;&nbsp;<p>从线程中返回数据和向线程传递数据类似。也可以通过类成员以及回调函数来返回数据。但类成员在返回数据和传递数据时有一些区别，下面让我们来看看它们区别在哪。</p>&nbsp;&nbsp;<p><strong>一、</strong><strong>通过类变量和方法返回数据</strong></p>&nbsp;&nbsp;<p>使用这种方法返回数据需要在调用start方法后才能通过类变量或方法得到数据。让我们先来看看例程2-13会得到什么结果。</p>&nbsp;&nbsp;<p><a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html"></a></p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String value1;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String value2;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value1 = &quot;通过成员变量返回数据&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value2 = &quot;通过成员方法返回数据&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread thread = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;value1:&quot; + thread.value1);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;value2:&quot; + thread.value2);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>运行上面的代码有可能输出如下的结果：</p>&nbsp;&nbsp;<p>value1:null&nbsp;&nbsp;&nbsp;&nbsp;<br />value2:null</p>&nbsp;&nbsp;<p>从上面的运行结果看很不正常。在run方法中已经对value1和value2赋了值，而返回的却是null。发生这种情况的原因是调用start方法后就立刻输出了value1和value2的值，而这里run方法还没有执行到为value1和value2赋值的语句。要避免这种情况的发生，就需要等run方法执行完后才执行输出value1和value2的代码。因此，我们可以想到使用sleep方法将主线程进行延迟，如可以在thread.start()后加一行如下的语句：</p>&nbsp;&nbsp;<p>sleep(1000);</p>&nbsp;&nbsp;<p>这样做可以使主线程延迟1秒后再往下执行，但这样做有一个问题，就是我们怎么知道要延迟多长时间。在这个例子的run方法中只有两条赋值语句，而且只创建了一个线程，因此，延迟1秒已经足够，但如果run方法中的语句很复杂，这个时间就很难预测，因此，这种方法并不稳定。</p>&nbsp;&nbsp;<p>我们的目的就是得到value1和value2的值，因此，只要判断value1和value2是否为null。如果它们都不为null时，就可以输出这两个值了。我们可以使用如下的代码来达到这个目的：</p>&nbsp;&nbsp;<p>while (thread.value1 == null &#124;&#124; thread.value2 == null);</p>&nbsp;&nbsp;<p>使用上面的语句可以很稳定地避免这种情况发生，但这种方法太耗费系统资源。大家可以设想，如果run方法中的代码很复杂，value1和value2需要很长时间才能被赋值，这样while循环就必须一直执行下去，直到value1和value2都不为空为止。因此，我们可以对上面的语句做如下的改进：</p>&nbsp;&nbsp;<p>while (thread.value1 == null &#124;&#124; thread.value2 == null)&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; sleep(100);</p>&nbsp;&nbsp;<p>在while循环中第判断一次value1和value2的值后休眠100毫秒，然后再判断这两个值。这样所占用的系统资源会小一些。</p>&nbsp;&nbsp;<p>上面的方法虽然可以很好地解决，但Java的线程模型为我们提供了更好的解决方案，这就是join方法。在前面已经讨论过，join的功能就是使用线程从异步执行变成同步执行。当线程变成同步执行后，就和从普通的方法中得到返回数据没有什么区别了。因此，可以使用如下的代码更有效地解决这个问题：</p>&nbsp;&nbsp;<p>&hellip;&nbsp;&nbsp;&nbsp;&nbsp;<br />thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 在thread.join()执行完后，线程thread的run方法已经退出了，也就是说线程thread已经结束了。因此，在thread.join()后面可以放心大胆地使用MyThread类的任何资源来得到返回数据。</p>&nbsp;&nbsp;<p><strong>二、</strong><strong>通过回调函数返回数据</strong></p>&nbsp;&nbsp;<p>其实这种方法已经在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html">《向线程传递数据的三种方法》</a>中介绍了。在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html">《向线程传递数据的三种方法》</a>一文的例子中通过Work类的process方法向线程中传递了计算结果，但同时，也通过process方法从线程中得到了三个随机数。因此，这种方法既可以向线程中传递数据，也可以从线程中获得数据。</p>&nbsp;&nbsp;<p><strong>第九节 为什么要进行数据同步</strong></p>&nbsp;&nbsp;<p>Java中的变量分为两类：局部变量和类变量。局部变量是指在方法内定义的变量，如在run方法中定义的变量。对于这些变量来说，并不存在线程之间共享的问题。因此，它们不需要进行数据同步。类变量是在类中定义的变量，作用域是整个类。这类变量可以被多个线程共享。因此，我们需要对这类变量进行数据同步。</p>&nbsp;&nbsp;<p>数据同步就是指在同一时间，只能由一个线程来访问被同步的类变量，当前线程访问完这些变量后，其他线程才能继续访问。这里说的访问是指有写操作的访问，如果所有访问类变量的线程都是读操作，一般是不需要数据同步的。</p>&nbsp;&nbsp;<p>那么如果不对共享的类变量进行数据同步，会发生什么情况呢？让我们先看看下面的代码会发生什么样的事情：</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />int m = n;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yield();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n = m;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread myThread = new MyThread ();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new Thread(myThread);&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n = &quot; + MyThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 在执行上面代码的可能结果如下：</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; n = 59</p>&nbsp;&nbsp;<p>看到这个结果，可能很多读者会感到奇怪。这个程序明明是启动了100个线程，然后每个线程将静态变量n加1。最后使用join方法使这100个线程都运行完后，再输出这个n值。按正常来讲，结果应该是n = 100。可偏偏结果小于100。</p>&nbsp;&nbsp;<p>其实产生这种结果的罪魁祸首就是我们经常提到的&ldquo;脏数据&rdquo;。而run方法中的yield()语句就是产生&ldquo;脏数据&rdquo;的始作俑者（不加yield语句也可能会产生&ldquo;脏数据&rdquo;，但不会这么明显，只有将100改成更大的数，才会经常产生&ldquo;脏数据&rdquo;，在本例中调用yield就是为了放大&ldquo;脏数据&rdquo;的效果）。yield方法的作用是使线程暂停，也就是使调用yield方法的线程暂时放弃CPU资源，使CPU有机会来执行其他的线程。为了说明这个程序如何产生&ldquo;脏数据&rdquo;，我们假设只创建了两个线程：thread1和thread2。由于先调用了thread1的start方法，因此，thread1的run方法一般会先运行。当thread1的run方法运行到第一行（int m = n;）时，将n的值赋给m。当执行到第二行的yield方法后，thread1就会暂时停止执行，而当thread1暂停时，thread2获得了CPU资源后开始运行（之前thread2一直处于就绪状态），当thread2执行到第一行(int m = n;)时，由于thread1在执行到yield时n仍然是0，因此，thread2中的m获得的值也是0。这样就造成了thread1和thread2的m获得的都是0。在它们执行完yield方法后，都是从0开始加1，因此，无论谁先执行完，最后n的值都是1，只是这个n被thread1和thread2各赋了一遍值。这个过程如下图如示：</p>&nbsp;&nbsp;<p><img src="../hznter/upload/blog/200907/zsj.jpg" border="0" /></p>&nbsp;&nbsp;<p>也许有人会问，如果只有n++，会产生&ldquo;脏数据&rdquo;吗？答案是肯定的。那么n++只是一条语句，又如何在执行过程中将CPU交给其他的线程呢？其实这只是表面现象，n++在被Java编译器编译成中间语言（也叫做字节码）后，并不是一条语言。让我们看看下面的Java代码将会被编译成什么样的Java中间语言。</p>&nbsp;&nbsp;<p><strong>Java</strong><strong>源代码</strong></p>&nbsp;&nbsp;<p>public void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>被编译后的中间语言代码</strong></p>&nbsp;&nbsp;<p>001 public void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />003&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aload_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getfield&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iconst_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iadd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; putfield&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />009 return&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp; &#125;</p>&nbsp;&nbsp;<p>大家可以看到在run方法中只有n++一条语句，而在编译后，却有7条中间语言语句。我们并不需要知道这些语句的功能是什么，只看一下第005、007和008行语句。在005行是getfield，根据它的英文含义可知是要得到某个值，因为这里只有一个n，所以毫无疑问，是要得到n的值。而在007行的iadd也不难猜测是将这个得到的n值加1。在008行的putfield的含义我想大家可能已经猜出来了，它负责将这个加1后的n再更新回类变量n。说到这，可能大家还有一个疑惑，执行n++时直接将n加1不就行了，为什么要如此费周折。其实这里涉及到一个Java内存模型的问题。</p>&nbsp;&nbsp;<p>Java的内存模型分为主存储区和工作存储区。主存储区保存了Java中所有的实例。也就是说，在我们使用new来建立一个对象后，这个对象及它内部的方法、变量等都保存在这一区域，在MyThread类中的n就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈，在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量，也就是方法变量。在线程要修改主存储区中的变量时，并不是直接修改这些变量，而是将它们先复制到当前线程的工作存储区，在修改完后，再将这个变量值覆盖主存储区的相应的变量值。</p>&nbsp;&nbsp;<p>在了解了Java的内存模型后，就不难理解为什么n++也不是原子操作了。它必须经过一个拷贝、加1和覆盖的过程。这个过程和在MyThread类中模拟的过程类似。大家可以想象，如果在执行到getfield时，thread1由于某种原因被中断，那么就会发生和MyThread类的执行结果类似的情况。要想彻底解决这个问题，就必须使用某种方法对n进行同步，也就是在同一时间只能有一个线程操作n，这也称为对n的原子操作。</p>&nbsp;&nbsp;<p><strong>第十节 使用Synchronized关键字同步类方法</strong></p>&nbsp;&nbsp;<p>要想解决&ldquo;脏数据&rdquo;的问题，最简单的方法就是使用synchronized关键字来使run方法同步，代码如下：</p>&nbsp;&nbsp;<p>public synchronized void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>从上面的代码可以看出，只要在void和public之间加上synchronized关键字，就可以使run方法同步，也就是说，对于同一个Java类的对象实例，run方法同时只能被一个线程调用，并当前的run执行完后，才能被其他的线程调用。即使当前线程执行到了run方法中的yield方法，也只是暂停了一下。由于其他线程无法执行run方法，因此，最终还是会由当前的线程来继续执行。先看看下面的代码：</p>&nbsp;&nbsp;<p><strong>sychronized</strong><strong>关键字只和一个对象实例绑定</strong></p>&nbsp;&nbsp;<p>class Test&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class Sync implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Test test;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test.method();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public Sync(Test test)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.test = test;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test test1 = new Test();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test test2 = new Test();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sync sync1 = new Sync(test1);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sync sync2 = new Sync(test2);&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(sync1).start();&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(sync2).start();&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>在Test类中的method方法是同步的。但上面的代码建立了两个Test类的实例，因此，test1和test2的method方法是分别执行的。要想让method同步，必须在建立Sync类的实例时向它的构造方法中传入同一个Test类的实例，如下面的代码所示：</p>&nbsp;&nbsp;<p>Sync sync1 = new Sync(test1);</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 不仅可以使用synchronized来同步非静态方法，也可以使用synchronized来同步静态方法。如可以按如下方式来定义method方法：</p>&nbsp;&nbsp;<p>class Test&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static synchronized void method() &#123; &hellip; &hellip; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>建立Test类的对象实例如下：</p>&nbsp;&nbsp;<p>Test test = new Test();</p>&nbsp;&nbsp;<p>对于静态方法来说，只要加上了synchronized关键字，这个方法就是同步的，无论是使用test.method()，还是使用Test.method()来调用method方法，method都是同步的，并不存在非静态方法的多个实例的问题。</p>&nbsp;&nbsp;<p>在23种设计模式中的单件（Singleton）模式如果按传统的方法设计，也是线程不安全的，下面的代码是一个线程不安全的单件模式。</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />// 线程安全的Singleton模式&nbsp;&nbsp;&nbsp;&nbsp; <br />class Singleton&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private static Singleton sample;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Singleton()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static Singleton getInstance()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />if (sample == null)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.yield(); // 为了放大Singleton模式的线程不安全性&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample = new Singleton();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />return sample;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Singleton singleton = Singleton.getInstance();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(singleton.hashCode());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[5];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面的代码调用yield方法是为了使单件模式的线程不安全性表现出来，如果将这行去掉，上面的实现仍然是线程不安全的，只是出现的可能性小得多。</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>25358555&nbsp;&nbsp;&nbsp;&nbsp;<br />26399554&nbsp;&nbsp;&nbsp;&nbsp; <br />7051261&nbsp;&nbsp;&nbsp;&nbsp; <br />29855319&nbsp;&nbsp;&nbsp;&nbsp; <br />5383406</p>&nbsp;&nbsp;<p>上面的运行结果可能在不同的运行环境上有所有同，但一般这五行输出不会完全相同。从这个输出结果可以看出，通过getInstance方法得到的对象实例是五个，而不是我们期望的一个。这是因为当一个线程执行了Thread.yield()后，就将CPU资源交给了另外一个线程。由于在线程之间切换时并未执行到创建Singleton对象实例的语句，因此，这几个线程都通过了if判断，所以，就会产生了建立五个对象实例的情况（可能创建的是四个或三个对象实例，这取决于有多少个线程在创建Singleton对象之前通过了if判断，每次运行时可能结果会不一样）。</p>&nbsp;&nbsp;<p>要想使上面的单件模式变成线程安全的，只要为getInstance加上synchronized关键字即可。代码如下：</p>&nbsp;&nbsp;<p>public static synchronized Singleton getInstance() &#123; &hellip; &hellip; &#125;</p>&nbsp;&nbsp;<p>当然，还有更简单的方法，就是在定义Singleton变量时就建立Singleton对象，代码如下：</p>&nbsp;&nbsp;<p>private static final Singleton sample = new Singleton();</p>&nbsp;&nbsp;<p>然后在getInstance方法中直接将sample返回即可。这种方式虽然简单，但不知在getInstance方法中创建Singleton对象灵活。读者可以根据具体的需求选择使用不同的方法来实现单件模式。</p>&nbsp;&nbsp;<p>在使用synchronized关键字时有以下四点需要注意：<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>1.&nbsp; synchronized</strong><strong>关键字不能继承。</strong></p>&nbsp;&nbsp;<p>虽然可以使用synchronized来定义方法，但synchronized并不属于方法定义的一部分，因此，synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字，而在子类中覆盖了这个方法，在子类中的这个方法默认情况下并不是同步的，而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然，还可以在子类方法中调用父类中相应的方法，这样虽然子类中的方法不是同步的，但子类调用了父类的同步方法，因此，子类的方法也就相当于同步了。这两种方式的例子代码如下：</p>&nbsp;&nbsp;<p><strong>在子类方法中加上</strong><strong>synchronized</strong><strong>关键字&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>class Parent&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Child extends Parent&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>在子类方法中调用父类的同步方法</strong></p>&nbsp;&nbsp;<p>class Parent&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Child extends Parent&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void method() &#123; super.method();&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>2.&nbsp; 在定义接口方法时不能使用</strong><strong>synchronized</strong><strong>关键字。</strong></p>&nbsp;&nbsp;<p><strong>3.&nbsp; 构造方法不能使用</strong><strong>synchronized</strong><strong>关键字，但可以使用下节要讨论的</strong><strong>synchronized</strong><strong>块来进行同步。</strong></p>&nbsp;&nbsp;<p><strong>4.&nbsp; synchronized</strong><strong>可以自由放置。</strong></p>&nbsp;&nbsp;<p>在前面的例子中使用都是将synchronized关键字放在方法的返回类型前面。但这并不是synchronized可放置唯一位置。在非静态方法中，synchronized还可以放在方法定义的最前面，在静态方法中，synchronized可以放在static的前面，代码如下：</p>&nbsp;&nbsp;<p>public synchronized void method();&nbsp;&nbsp;&nbsp;&nbsp;<br />synchronized public void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized static void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized public static void method();</p>&nbsp;&nbsp;<p>但要注意，synchronized不能放在方法返回类型的后面，如下面的代码是错误的：</p>&nbsp;&nbsp;<p>public void synchronized method();&nbsp;&nbsp;&nbsp;&nbsp;<br />public static void synchronized method();</p>&nbsp;&nbsp;<p>synchronized关键字只能用来同步方法，不能用来同步类变量，如下面的代码也是错误的。</p>&nbsp;&nbsp;<p>public synchronized int n = 0;&nbsp;&nbsp;&nbsp;&nbsp;<br />public static synchronized int n = 0;</p>&nbsp;&nbsp;<p>虽然使用synchronized关键字同步方法是最安全的同步方式，但大量使用synchronized关键字会造成不必要的资源消耗以及性能损失。虽然从表面上看synchronized锁定的是一个方法，但实际上synchronized锁定的是一个类。也就是说，如果在非静态方法method1和method2定义时都使用了synchronized，在method1未执行完之前，method2是不能执行的。静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响。看看如下的代码：</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public String methodName;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method(String s)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;非静态的method1方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method2()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;非静态的method2方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method3()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;静态的method3方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method4()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;静态的method4方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getClass().getMethod(methodName).invoke(this);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread1 myThread1 = new MyThread1();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 1; i &lt;= 4; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myThread1.methodName = &quot;method&quot; + String.valueOf(i);&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(myThread1).start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(100);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>运行结果如下：</p>&nbsp;&nbsp;<p>非静态的method1方法&nbsp;&nbsp;&nbsp;&nbsp;<br />静态的method3方法</p>&nbsp;&nbsp;<p>从上面的运行结果可以看出，method2和method4在method1和method3未结束之前不能运行。因此，我们可以得出一个结论，如果在类中使用synchronized关键字来定义非静态方法，那将影响这个中的所有使用synchronized关键字定义的非静态方法。如果定义的是静态方法，那么将影响类中所有使用synchronized关键字定义的静态方法。这有点象数据表中的表锁，当修改一条记录时，系统就将整个表都锁住了，因此，大量使用这种同步方式会使程序的性能大幅度下降。</p>&nbsp;&nbsp;<p><strong>第十一节 使用Synchronized块同步方法</strong></p>&nbsp;&nbsp;<p>synchronized关键字有两种用法。第一种就是在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/nokiaguy/archive/2009/03/20/261020.html">《使用Synchronized关键字同步类方法》</a>一文中所介绍的直接用在方法的定义中。另外一种就是synchronized块。我们不仅可以通过synchronized块来同步一个对象变量。也可以使用synchronized块来同步类中的静态方法和非静态方法。</p>&nbsp;&nbsp;<p>synchronized块的语法如下：</p>&nbsp;&nbsp;<p>public void method()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(表达式)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>一、非静态类方法的同步 </strong></p>&nbsp;&nbsp;<p>从<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/nokiaguy/archive/2009/03/20/261020.html">《使用Synchronized关键字同步类方法》</a>一文中我们知道使用synchronized关键字来定义方法就会锁定类中所有使用synchronzied关键字定义的静态方法或非静态方法，但这并不好理解。而如果使用synchronized块来达到同样的效果，就不难理解为什么会产生这种效果了。如果想使用synchronized块来锁定类中所有的同步非静态方法，需要使用this做为synchronized块的参数传入synchronized块国，代码如下：</p>&nbsp;&nbsp;<p><strong>通过</strong><strong>synchronized</strong><strong>块同步非静态方法&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>001 public class SyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 synchronized(this)&nbsp; // 相当于对method1方法使用synchronized关键字&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />010 public void method2()&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />012 synchronized(this)&nbsp; // 相当于对method2方法使用synchronized关键字&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017 public synchronized void method3()&nbsp;&nbsp; <br />018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021&nbsp; &#125;</p>&nbsp;&nbsp;<p>在上面的代码中的method1和method2方法中使用了synchronized块。而第017行的method3方法仍然使用synchronized关键字来定义方法。在使用同一个SyncBlock类实例时，这三个方法只要有一个正在执行，其他两个方法就会因未获得同步锁而被阻塞。在使用synchronized块时要想达到和synchronized关键字同样的效果，必须将所有的代码都写在synchronized块中，否则，将无法使当前方法中的所有代码和其他的方法同步。</p>&nbsp;&nbsp;<p>除了使用this做为synchronized块的参数外，还可以使用SyncBlock.this作为synchronized块的参数来达到同样的效果。</p>&nbsp;&nbsp;<p>在内类（InnerClass）的方法中使用synchronized块来时，this只表示内类，和外类(OuterClass)没有关系。但内类的非静态方法可以和外类的非静态方法同步。如在内类InnerClass中加一个method4方法，并使method4方法和SyncBlock的三个方法同步，代码如下：</p>&nbsp;&nbsp;<p><strong>使内类的非静态方法和外类的非静态方法同步</strong></p>&nbsp;&nbsp;<p>public class SyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; class InnerClass&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void method4()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(SyncBlock.this)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面SyncBlock类的新版本中，InnerClass类的method4方法和SyncBlock类的其他三个方法同步，因此，method1、method2、method3和method4四个方法在同一时间只能有一个方法执行。</p>&nbsp;&nbsp;<p>Synchronized块不管是正常执行完，还是因为程序出错而异常退出synchronized块，当前的synchronized块所持有的同步锁都会自动释放。因此，在使用synchronized块时不必担心同步锁的释放问题。</p>&nbsp;&nbsp;<p><strong>二、静态类方法的同步</strong></p>&nbsp;&nbsp;<p>由于在调用静态方法时，对象实例不一定被创建。因此，就不能使用this来同步静态方法，而必须使用Class对象来同步静态方法。代码如下：</p>&nbsp;&nbsp;<p><strong>通过</strong><strong>synchronized</strong><strong>块同步静态方法</strong></p>&nbsp;&nbsp;<p>public class StaticSyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(StaticSyncBlock.class)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method2()&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>在同步静态方法时可以使用类的静态字段class来得到Class对象。在上例中method1和method2方法同时只能有一个方法执行。除了使用class字段得到Class对象外，还可以使用实例的getClass方法来得到Class对象。上例中的代码可以修改如下：</p>&nbsp;&nbsp;<p><strong>使用</strong><strong>getClass</strong><strong>方法得到</strong><strong>Class</strong><strong>对象</strong></p>&nbsp;&nbsp;<p>public class StaticSyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static StaticSyncBlock instance;&nbsp; <br />&nbsp;&nbsp;&nbsp; public StaticSyncBlock()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance = this;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(instance.getClass())&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125; </p>&nbsp;&nbsp;<p>在上面代码中通过一个public的静态instance得到一个StaticSyncBlock类的实例，并通过这个实例的getClass方法得到了Class对象（一个类的所有实例通过getClass方法得到的都是同一个Class对象，因此，调用任何一个实例的getClass方法都可以）。我们还可以通过Class对象使不同类的静态方法同步，如Test类的静态方法method和StaticSyncBlock类的两个静态方法同步，代码如下：</p>&nbsp;&nbsp;<p><strong>Test</strong><strong>类的</strong><strong>method</strong><strong>方法和</strong><strong>StaticSyncBlock</strong><strong>类的</strong><strong>method1</strong><strong>、</strong><strong>method2</strong><strong>方法同步</strong></p>&nbsp;&nbsp;<p>public class Test&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(StaticSyncBlock.class)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>注意：在使用synchronized块同步类方法时，非静态方法可以使用this来同步，而静态方法必须使用Class对象来同步。它们互不影响。当然，也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。这一点在使用synchronized块同步类方法时应注意。</p>&nbsp;&nbsp;<p>&nbsp;</p>&nbsp;&nbsp;<p><strong>第十二节 使用Synchronized块同步变量</strong></p>&nbsp;&nbsp;<p>我们可以通过synchronized块来同步特定的静态或非静态方法。要想实现这种需求必须为这些特性的方法定义一个类变量，然后将这些方法的代码用synchronized块括起来，并将这个类变量作为参数传入synchronized块。下面的代码演示了如何同步特定的类方法：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class SyncThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 private static String sync = &quot;&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />006 private String methodType = &quot;&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp; <br />008 private static void method(String s)&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />010 synchronized (sync)&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sync = s;&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp;&nbsp;&nbsp; <br />014 while (true);&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017 public void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;method1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021 public static void staticMethod1()&nbsp;&nbsp;&nbsp;&nbsp; <br />022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;staticMethod1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />025 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />027 if (methodType.equals(&quot;static&quot;))&nbsp;&nbsp;&nbsp;&nbsp; <br />028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; staticMethod1();&nbsp;&nbsp;&nbsp;&nbsp; <br />029 else if (methodType.equals(&quot;nonstatic&quot;))&nbsp;&nbsp;&nbsp;&nbsp; <br />030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method1();&nbsp;&nbsp;&nbsp;&nbsp; <br />031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />032 public SyncThread(String methodType)&nbsp;&nbsp;&nbsp;&nbsp; <br />033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />034 this.methodType = methodType;&nbsp;&nbsp;&nbsp;&nbsp; <br />035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />036 public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />038 SyncThread sample1 = new SyncThread(&quot;nonstatic&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />039 SyncThread sample2 = new SyncThread(&quot;static&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />043&nbsp; &#125;</p>&nbsp;&nbsp;<p>运行结果如下：</p>&nbsp;&nbsp;<p>method1&nbsp;&nbsp;&nbsp;&nbsp;<br />staticMethod1</p>&nbsp;&nbsp;<p>看到上面的运行结果很多读者可能感到惊奇。在上面的代码中method1和staticMethod1方法使用了静态字符串变量sync进行同步。这两个方法只能有一个同时执行，而这两个方法都会执行014行的无限循环语句。因此，输出结果只能是method1和staticMethod1其中之一。但这个程序将这两个字符串都输出了。</p>&nbsp;&nbsp;<p>出现这种结果的愿意很简单，我们看一下012行就知道了。原来在这一行将sync的值改变了。在这里要说一下Java中的String类型。String类型和Java中其他的复杂类型不同。在使用String型变量时，只要给这个变量赋一次值，Java就会创建个新的String类型的实例。如下面的代码所示：</p>&nbsp;&nbsp;<p>String s = &quot;hello&quot;;&nbsp;&nbsp;&nbsp;&nbsp;<br />System.out.println(s.hashCode());&nbsp;&nbsp;&nbsp;&nbsp; <br />s = &quot;world&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />System.out.println(s.hashCode());&nbsp; </p>&nbsp;&nbsp;<p>在上面的代码中。第一个s和再次赋值后的s的hashCode的值是不一样的。由于创建String类的实例并不需要使用new，因此，在同步String类型的变量时要注意不要给这个变量赋值，否则会使变量无法同步。</p>&nbsp;&nbsp;<p>由于在013行已经为sync创建了一个新的实例，假设method1先执行，当method1方法执行了013行的代码后，sync的值就已经不是最初那个值了，而method1方法锁定的仍然是sync变量最初的那个值。而在这时，staticMethod1正好执行到synchronized(sync)，在staticMethod1方法中要锁定的这个sync和method1方法锁定的sync已经不是一个了，因此，这两个方法的同步性已经被破坏了。</p>&nbsp;&nbsp;<p>解决以上问题的方法当然是将013行去掉。在本例中加上这行，只是为了说明使用类变量来同步方法时如果在synchronized块中将同步变量的值改变，就会破坏方法之间的同步。为了彻底避免这种情况发生，在定义同步变量时可以使用final关键字。如将上面的程序中的005行可改成如下形式：</p>&nbsp;&nbsp;<p>private final static String sync = &quot;&quot;;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 使用final关键字后，sync只能在定义时为其赋值，并且以后不能再修改。如果在程序的其他地方给sync赋了值，程序就无法编译通过。在Eclipse等开发工具中，会直接在错误的地方给出提示。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 我们可以从两个角度来理解synchronized块。如果从类方法的角度来理解，可以通过类变量来同步相应的方法。如果从类变量的角度来理解，可以使用synchronized块来保证某个类变量同时只能被一个方法访问。不管从哪个角度来理解，它们的实质都是一样的，就是利用类变量来获得同步锁，通过同步锁的互斥性来实现同步。</p>&nbsp;&nbsp;<p>注意：在使用synchronized块时应注意，synchronized块只能使用对象作为它的参数。如果是简单类型的变量(如int、char、boolean等)，不能使用synchronized来同步。</p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/java/" rel="tag">java</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25BA%25BF%25E7%25A8%258B/" rel="tag">线程</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/168/</link>
<title><![CDATA[Java中的字符集编码入门]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Wed, 24 Dec 2008 07:20:36 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/168/</guid> 
<description>
<![CDATA[ 
	<p><strong style="FONT-SIZE: 24px">（一）Unicode与UCS的历史恩怨<br/></strong> <strong>ASCII及相关标准<br/></strong> 地球人都知道ASCII就是美国标准信息交换码的缩写，也知道ASCII规定用7位二进制数字来表示英文字符，ASCII被定为国际标准之后的代号为 ISO-646。由于ASCII码只使用了7个二进制位，也就是说一个字节可以表示的256个数字中，它仅使用了0~127这128个码位，剩下的128 个码位便可以用来做扩展，用来表示一些特定语言所独有的字符，因此对这多余的128个码位的不同扩展，就形成了一系列ISO-8859-*的标准。例如为英语作了专门扩展的字符集编码标准编号为ISO-8859-1，也叫做Latin-1，为希腊语所作的扩展编号为ISO-8859-7等，完整的列表可以参考《Java Internationalization》一书。<br/><strong>Unicode与UCS<br/></strong> 整个Unicode项目是由多家计算机软件公司，还包括一些出版行业的公司共同发起的，从上世纪八十年代就已经开始。地球人都知道，对于日文，汉字来说，256个码位是远远不够用的（当然，在当时并不是地球人都知道，起码设计计算机的老美们就不知道，甚至直到今天，还有老美以为米国是世界上唯一的国家）。解决方法很直观也很明显，那就是采用码位多到足够包含所需字符数量的编码方案（即俗话说的头痛医头，脚痛医脚嘛）。这也是Unicode的目标之一，能够包含世界上所有语言的字符（包括汉字，日文，数学符号，音乐符号，还包括各种奇奇怪怪看也看不懂的东西比如象形文字，甲骨文，三个代表，科学发展观等等，笑），这个理想，可以说很远大，但很快被发现仅靠Unicode原先的设计无法实现。Unicode的另一个设计目标，对今天影响深远，那就是对所有字符都采用16位编码（即用一个大小不超过2的16次方的整数数字给每个字符编号，注意从这个意义上也可以看出，Unicode 是一种编码字符集，而非字符集编码）。说这个设计目标对现今影响深远，完全不是表扬，因为到后来连Unicode的设计者也发现，16位编码仅有 65536个码位，远远不能容纳世界上所有的字符，但当意识到这个问题的时候，Unicode大部分的规范已经制定完毕，也有相当程度的普及，完全推倒重来是不现实的。这成了一个遗留问题，也是surrogate pair这种蹩脚解决方案的发端。<br/>无独有偶，在1984年，喜欢以繁多的编号糊弄群众的国际标准化组织ISO也开始着手制定解决不同语言字符数量太大问题的解决方案，这一方案被称为 Universal Character Set（UCS），正式的编号是ISO-10646（记得么，ASCII是ISO-646，不知这种安排是否是故意的）。还是ISO高瞻远瞩，一开始就确定了UCS是一个31位的编码字符集（即用一个大小不超过2的31次方的整数数字为每个字符编号），这回真的足以容纳古往今来所有国家，所有语言所包含的字符了（是的，任何国家，任何小语种都包括，也不管这些国家是与台湾建交还是与中国大陆建交，是拥护民主制度还是实行恐怖主义，所以说科学无国界）。虽然后来他们意识到，2的31次方个码位又实在太多了……<br/>天下大势，分久必合。无论Unicode还是UCS，最初的目的都是杜绝各种各样名目繁多形式各异互不兼容老死不相往来的私用扩展编码（好啰嗦的一句话），结果两方确立标准的同时（最初时这两个标准是不兼容的），又形成了割据，这对建设和谐社会是不利的，违反当今世界和平与发展的主旋律，中国政府一向反对任何形式的霸权主义和强权政治，对以米国为首的发达国家……扯远了扯远了。1991年，Unicode联盟与ISO的工作组终于开始讨论 Unicode与UCS的合并问题，虽然其后的合并进行了很多年，Unicode初版规范中的很多编码都需要被改写，UCS也需要对码空间的使用进行必要限制，但成果是喜人的。最终，两者统一了抽象字符集（即任何一个在Unicode中存在的字符，在UCS中也存在），且最靠前的65535个字符也统一了字符的编码。对于码空间，两者同意以一百一十万为限（即两者都认为虽然65536不够，但2的31次方又太大，一百一十万是个双方都可接受的码空间大小，也够用，当然，这里说的一百一十万只是个约数），Unicode将码空间扩展到了一百一十万，而UCS将永久性的不使用一百一十万以后的码位。也就是说，现在再讲Unicode只包含65536个字符是不对的。除了对已经定义的字符进行统一外，Unicode联盟与ISO工作组也同意今后任何的扩展工作两者均保持同步，因此虽然从历史的意义上讲Unicode与UCS不是一回事（甚至细节上说也不是一回事），但现在提起Unicode，指代两者均无不妥。</p><p><strong style="FONT-SIZE: 24px">（二）编码字符集与字符集编码的区别<br/></strong> 需要再一次强调的是，无论历史上的UCS还是现如今的Unicode，两者指的都是<span style="COLOR: #ff0000">编码字符集</span>，而不是<span style="COLOR: #ff0000">字符集编码</span>。花费一点时间来理解好这件事，然后你会发现对所有网页的，系统的，编码标准之间的来回转换等等繁杂事务都会思路清晰，手到擒来。<br/>首先说说最一般意义上的字符集。<br/>一个<span style="COLOR: #0080ff">抽象字符集</span>其实就是指字符的集合，例如所有的英文字母是一个抽象字符集，所有的汉字是一个抽象字符集，当然，把全世界所有语言的符号都放在一起，也可以称为一个抽象字符集，所以这个划分是相当人为的。之所以说"抽象"二字，是因为这里所提及的字符不是任何具体形式的字符，拿汉字中的"汉"这个字符来说，您在这篇文章中看到的这个"汉"其实是这个字符的一种具体表现形式，是它的图像表现形式，而且它是用中文（而非拼音）书写而成，使用宋体外观；而当人们用嘴发出"汉"这个音的时候，他们是在使用"汉"的另一种具体表现形式--声音，但无论如何，两者所指的字符都是"汉"这个字。同一个字符的表现形式可能有无数种（点阵表示，矢量表示，音频表示，楷体，草书等等等等），把每一种表现形式下的同一个字符都纳入到字符集中，会使得集合过于庞大，冗余高，也不好管理。因此抽象字符集中的字符，都是指唯一存在的抽象字符，而忽略它的具体表现形式。<br/>抽象字符集中的诸多字符，没有顺序之分，谁也不能说哪个字符在哪个字符前面，而且这种抽象字符只有人能理解。在给一个抽象字符集合中的每个字符都分配一个整数编号之后（注意这个整数并没有要求大小），这个字符集就有了顺序，就成为了<span style="COLOR: #0080ff">编码字符集</span>。同时，通过这个编号，可以唯一确定到底指的是哪一个字符。当然，对于同一个字符，不同的字符集编码系统所制定的整数编号也不尽相同，例如"儿"这个字，在 Unicode中，它的编号是0x513F，（为方便起见，以十六进制表示，但这个整数编号并不要求必须是以十六进制表示）意思是说它是Unicode这个编码字符集中的第0x513F个字符。而在另一种编码字符集比如Big5中，这个字就是第0xA449个字符了。这种情况的另一面是，许多字符在不同的编码字符集中被分配了相同的整数编号，例如英文字母"A"，在ASCII及Unicode中，均是第0x41个字符。我们常说的Unicode字符集，指的就是这种被分配了整数编号的字符集合，但要澄清的是，编码字符集中字符被分配的整数编号，不一定就是该字符在计算机中存储时所使用的值，计算机中存储的字符到底使用什么二进制整数值来表示，是由下面将要说到的 <span style="COLOR: #0080ff">字符集编码</span>决定的。<br/>字符集编码决定了如何将一个字符的整数编号对应到一个二进制的整数值，有的编码方案简单的将该整数值直接作为其在计算机中的表示而存储，例如英文字符就是这样，几乎所有的字符集编码方案中，英文字母的整数编号与其在计算机内部存储的二进制形式都一致。但有的编码方案，例如适用于Unicode字符集的 UTF-8编码形式，就将很大一部分字符的整数编号作了变换后存储在计算机中。以"汉"字为例，"汉"的Unicode值为0x6C49，但其编码为 UTF-8格式后的值为0xE6B189（注意到变成了三个字节）。这里只是举个例子，关于UTF-8的详细编码规则可以参看《Mapping codepoints to Unicode encoding forms》一文，URL为<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-AppendixA#sec3"><a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-AppendixA#sec3" target="_blank">http://scripts.sil.org/cms...</a></a>。<br/>我们经常听说的另一种编码方案UTF-16,则对Unicode中的前65536个字符编号都不做变换，直接作为计算机存储时使用的值（对65536以后的字符，仍然要做变换），例如"汉"字的Unicode编号为0x6C49，那么经过UTF-16编码后存储在计算机上时，它的表示仍为0x6C49！。我猜，正是因为UTF-16的存在，使得很多人认为Unicode是一种编码（实际上，是一个字符集，再次重申），也因此，很多人说Unicode的时候，他们实际上指的是UTF-16。UTF-16提供了surrogate　pair机制，使得Unicode中码位大于65536的那些字符得以表示。<br/>Surrogate pair机制在目前来说实在不常用，甚至连一些UTF-16的实现都不支持，所以我不打算在这里多加讨论，其基本的思想就是用两个16位的编码表示一个字符（注意，只对码位超过65536的字符这么做）。Unicode如此死抱着16这个数字不放，有历史的原因，也有实用的原因。<br/>当然还有一种最强的编码，UTF-32,他对所有的Unicode字符均不做变换，直接使用编号存储！（俗称的以不变应万变），只是这种编码方案太浪费存储空间（就连1个字节就可以搞定的英文字符，它都必须使用4个字节），因而尽管使用起来方便（不需要任何转换），却没有得到普及。<br/>记得当初Unicode与UCS还没成家之时，UCS也是需要人爱，需要人疼的，没有自己的字符集编码怎么成。UCS-2与UCS-4就扮演了这样的角色。UCS-4与UTF-32除了名字不同以外，思想完全一样。而UCS-2与UTF-16在对前65536个字符的处理上也完全相同，唯一的区别只在于 UCS-2 不支持surrogate pair机制，即是说，UCS-2只能对前65536个字符编码，对其后的字符毫无办法。不过现在再谈起字符编码的时候，UCS-2与UCS-4早已成为计算机史学家才会用到的词汇，就让它们继续留在故纸堆里吧。<br/>下一节我们来说说与中文相关的GB2312和GBK。</p><p><strong style="FONT-SIZE: 24px">（三）GB2312，GBK与中文网页</strong><br/>GB2312是对中国的开发人员来说很重要的一个词汇，它的来龙去脉并不需要我在这里赘述，随便Goolge之便明白无误。我只是想提一句，记得前一节说到编码字符集和字符集编码不是一回事，而有的字符集编码又实际上没有做任何事，GB2312正是这样一种东西！<br/>GB2312最初指的是一个编码字符集，其中包含了ASCII所包含的英文字符，同时加入了6763个简体汉字以及其他一些ASCII之外的符号。与 Unicode有UTF-8和UTF-16一样（当然， UTF-8和UTF-16也没有被限定只能用来对Unicode进行编码，实际上，你用它对视频进行编码都是可以的，只是编出的文件没有播放器支持罢了，哈哈），GB2312也有自己的编码方案，但这个方案直接使用一个字符在GB2312中的编号作为存储值（与UTF-32的做法类似），也因此，这个编码方案甚至没有正式的名称。我们日常说起GB2312的时候，常常即指这个字符集，也指这种编码方案。<br/>GBK是GB2312的后续标准，添加了更多的汉字和特殊符号，类似的是，GBK也是同时指他的字符集和他的编码。<br/>GBK还是现如今中文Windows操作系统的系统默认编码（这正是几乎所有网页上的，文件里的乱码问题的根源）。<br/>我们可以这样来验证，使用以下的Java代码：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,0)">String encoding</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">System.getProperty(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">file.encoding</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">);<br/>System.out.println(encoding);</span></div><p>输出结果为GBK<br/>（什么？你的输出不是这样？怎么可能？完了，我的牌子要砸了，等等，你用的繁体版XP？我说你这同志在这里捣什么乱？去！去！）<br/>说到GB2312和GBK就不得不提中文网页的编码。尽管很多新开发的Web系统和新上线的注重国际化的网站都开始使用UTF-8，仍有相当一部分的中文媒体坚持使用GB2312和GBK，例如新浪的页面。其中有两点很值得注意。<br/>第一，页面中meta标签的部分，常常可以见到<br/>charset=GB2312<br/>这样的写法，很不幸的是，这个"charset"其实是用来指定页面使用的是什么字符集编码，而不是使用什么字符集。例如你见到过有人写 "charset=UTF-8"，见到过有人写"charset=ISO-8859-1"，但你见过有人写"charset=Unicode"么？当然没有，因为Unicode是一个字符集，而不是编码。<br/>然而正是charset这个名称误导了很多程序员，真的以为这里要指定的是字符集，也因而使他们进一步的误以为UTF-8和UTF-16是一种字符集！（万恶啊）好在XML中已经做出了修改，这个位置改成了正确的名称：encoding。<br/>第二，页面中说的GB2312，实际上并不真的是GB2312（惊讶么？）。我们来做个实验，例如找一个GB2312中不存在的汉字"亸"（这个字确实不在GB2312中，你可以到GB2312的码表中去找，保证找不到），这个字在GBK中。然后你把它放到一个html页面中，试着在浏览器中打开它，然后选择浏览器的编码为"GB2312"，看到了什么？它完全正常显示！<br/>结论不用我说你也明白了，浏览器实际上使用的是GBK来显示。<br/>新浪的页面中也有很多这样的例子，到处都写charset=GB2312，却使用了无数个GB2312中并不存在的字符。这种做法对浏览器显示页面并不成问题，但在需要程序抓取页面并保存的时候带来了麻烦，程序将不能依据页面所"声称"的编码进行读取和保存，而只能尽量猜测正确的编码。</p><p><strong style="FONT-SIZE: 24px">（四）网页文件的编码</strong><br/>接着上节的思路说，一个网页要想在浏览器中能够正确显示，需要在三个地方保持编码的一致：网页文件，网页编码声明和浏览器编码设置。<br/>首先是网页文件本身的编码，即网页文件在被创建的时候使用什么编码来保存。这个完全取决于创建该网页的人员使用了什么编码保存，而进一步的取决于该人员使用的操作系统。例如我们使用的中文版WindowsXP系统，当你新建一个文本文件，写入一些内容，并按下ctrl+s进行保存的那一刻，操作系统就替你使用GBK编码将文件进行了保存（没有使用UTF-8，也没有使用UTF-16）。而使用了英文系统的人，系统会使用ISO-8859-1进行保存，这也意味着，在英文系统的文件中如果输入一个汉字，是无法进行保存的（当然，你甚至都无法输入）。<br/>一个在创建XML文件时（创建HTML的时候倒很少有人这么做）常见的误解是以为只要在页面的encoding部分声明了UTF-8，则文件就会被保存为 UTF-8格式。这实在是……怎么说呢，不能埋怨大家。实际上XML文件中encoding部分与HTML文件中的charset中一样，只是告诉"别人 "（这个别人可能是浏览你的页面的人，可能是浏览器，也可能是处理你页面的程序，别人需要知道这个，因为除非你告诉他们，否则谁也猜不出你用了什么编码，仅通过文件的内容判断不出使用了什么编码，这是真的）这个文件使用了什么编码，唯独操作系统不会搭理，它仍然会按自己默认的编码方式保存文件（再一次的，在我们的中文WindowsXP系统中，使用GBK保存）。至于这个文件是不是真的是encoding或者charset所声明的那种编码保存的呢？答案是不一定！<br/>例如新浪的页面就"声称"他是用GB2312编码保存的，但实际上却是GBK，也有无数的二把刀程序员用系统默认的GBK保存了他们的XML文件，却在他们的encoding中信誓旦旦的说是UTF-8的。<br/>这就是我们所说的第二个位置，网页编码声明中的编码应该与网页文件保存时使用的编码一致。<br/>而浏览器的编码设置实际上并不严格，就像我们第三节所说的那样，在浏览器中选择使用GB2312来查看，它实际上仍然会使用GBK进行。而且浏览器还有这样一种好习惯，即它会尽量猜测使用什么编码查看最合适。<br/>我要重申的是，网页文件的编码和网页文件中声明的编码保持一致，这是一个极好的建议（值得遵循，会与人方便，与己方便），但如果不一致，只要网页文件的编码与浏览器的编码设置一致，也是可以正确显示的。<br/>例如有这样一个页面，它使用GBK保存，但声明自己是UTF-8的。这个时候用浏览器打开它，首先会看到乱码，因为这个页面"告诉"浏览器用UTF-8显示，浏览器会很尊重这个提示，于是乱码一片。但当手工把浏览器设为GBK之后，显示正常。<br/>说了以上四节这么多，后面我们就来侃侃Java里的字符编码，你会发现有意思且挠头的事情很多，但一旦弄通，天下无敌（不过不要像东方不败那样才好）。</p><p><span style="FONT-SIZE: 24px"><strong>（五）Java代码中的字符编码转换Part 1</strong></span><br/>如果你是JVM的设计者，让你来决定JVM中所有字符的表示形式，你会不会允许使用各种编码方式的字符并存？<br/>我想你的答案是不会，如果在内存中的Java字符可以以GB2312,UTF-16,BIG5等各种编码形式存在，那么对开发者来说，连进行最基本的字符串打印、连接等操作都会寸步难行。例如一个GB2312的字符串后面连接一个UTF-8的字符串，那么连接后的最终结果应该是什么编码的呢？你选哪一个都没有道理。<br/>因此牢记下面这句话，这也是Java开发者的共同意志：在Java中，字符只以一种形式存在，那就是Unicode（注意到我们没有选择特定的编码，直接使用它们在字符集中的编号，这是统一的唯一方法）。<br/>但"在Java中"到底是指在哪里呢？就是指在JVM中，在内存中，在你的代码里声明的每一个char，String类型的变量中。例如你在程序中这样写</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">;</span></div><p>在内存的相应区域，这个字符就表示为0x6C49。可以用下面的代码证明一下：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">;<br/>System.out.format(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">%x</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">,(</span> <span style="COLOR: rgb(0,0,255)">short</span> <span style="COLOR: rgb(0,0,0)">)han);</span></div><p>输出是：6c49<br/>反过来用Unicode编号来指定一个字符也可以，像这样：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">0x6c49</span> <span style="COLOR: rgb(0,0,0)">;<br/>System.out.println(han);</span></div><p>输出是：汉<br/>这其实也是说，只要你正确的读入了"汉"这个字，那么它在内存中的表示形式一定是0x6C49，没有任何其他的值能代表这个字（当然，如果你读错了，那结果是什么就不知道了，范伟说：读，读错了呀，那还等于好几亿呢；本山大哥说：好几亿你也没答上，请听下一题）。<br/>JVM的这种约定使得一个字符存在的世界分为了两部分：JVM内部和OS的文件系统。在JVM内部，统一使用Unicode表示，当这个字符被从JVM内部移到外部（即保存为文件系统中的一个文件的内容时），就进行了编码转换，使用了具体的编码方案（也有一种很特殊的情况，使得在JVM内部也需要转换，不过这个是后话）。<br/>因此可以说，所有的编码转换就只发生在边界的地方，JVM和OS的交界处，也就是你的各种输入输出流（或者Reader，Writer类）起作用的地方。<br/>话头扯到这里就必须接着说Java的IO系统。<br/>尽管看上去混乱繁杂，但是所有的IO基本上可以分为两大阵营：面向字符的Reader啊Wrtier啊，以及面向字节的输入输出流。<br/>下面我来逐一分解，其实一点也不难。<br/>面向字符和面向字节中的所谓"面向"什么，是指这些类在处理输入输入的时候，在哪个意义上保持一致。如果面向字节，那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容要一致。不能变换任何0和1的顺序。因此这是一种非常"忠实于原著"的做法（偶然间让我想起郭敬明抄袭庄羽的文章，那家伙，太忠实于原著了，笑）。<br/>这种输入输出方式很适合读入视频文件或者音频文件，或者任何不需要做变换的文件内容。<br/>而面向字符的IO是指希望系统中的文件的字符和读入内存的"字符"（注意和字节的区别）要一致。例如我们的中文版WindowsXP系统上有一个GBK的文本文件，其中有一个"汉"字，这个字的GBK编码是0xBABA（而Unicode编号是0x6C49），当我们使用面向字符的IO把它读入内存并保存在一个char型变量中时，我希望IO系统不要傻傻的直接把0xBABA放到这个char型变量中，我甚至都不关心这个char型变量具体的二进制内容到底是多少，我只希望这个字符读进来之后仍然是"汉"这个字。<br/>从这个意义上也可以看出，面向字符的IO类，也就是Reader和Writer类，实际上隐式的为我们做了编码转换，在输出时，将内存中的Unicode 字符使用系统默认的编码方式进行了编码，而在输入时，将文件系统中已经编码过的字符使用默认编码方案进行了还原。我两次提到"默认"，是说Reader和 Writer的聪明也仅此而已了，它们只会使用这个默认的编码来做转换，你不能为一个Reader或者Writer指定转换时使用的编码。这也意味着，如果你使用中文版WindowsXP系统，而上面存放了一个UTF-8编码的文件，当你使用Reader类来读入的时候，它会傻傻的使用GBK来做转换，转换后的内容当然驴唇不对马嘴！<br/>这种笨，有时候其实是一种傻瓜式的功能提供方式，对大多数初级用户（以及不需要跨平台的高级用户）来说反而是件好事。<br/>但我们不一样啦，我们都是国家栋梁，肩负着赶英超美的责任，必须师夷长技以治夷，所以我们总还要和GBK编码以外的文件打交道。<br/>说了上面这些内容，想必聪明的读者已经看出来，所谓编码转换就是一个字符与字节之间的转换，因此Java的IO系统中能够指定转换编码的地方，也就在字符与字节转换的地方，那就是（读者：InputSteamReader和OutputStreamWriter！作者：太强了，都会抢答了！）<br/>这两个类是字节流和字符流之间的适配器类，因此他们肩负着编码转换的任务简直太自然啦！要注意，实际上也只能在这两类实例化的时候指定编码，是不是很好记呢？<br/>下面来写一段小程序，来把"汉"字用我们非常崇拜的UTF-8编码写到文件中！</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">try</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>PrintWriter out</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">PrintWriter(</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">OutputStreamWriter(</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">FileOutputStream(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">c:/utf-8.txt</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">),</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">UTF-8</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">));<br/></span> <span style="COLOR: rgb(0,0,255)">try</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>out.write(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">);<br/>&#125;</span> <span style="COLOR: rgb(0,0,255)">finally</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>out.close();<br/>&#125;<br/>&#125;</span> <span style="COLOR: rgb(0,0,255)">catch</span> <span style="COLOR: rgb(0,0,0)">(IOException e)&#123;<br/></span> <span style="COLOR: rgb(0,0,255)">throw</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">RuntimeException(e);<br/>&#125;</span></div><p>运行之后到c盘下去找utf-8.txt这个文件，用UltraEdit打开，使用16进制查看，看到了什么？它的值是0xE6B189！噢耶！（读者：这，这有什么好高兴的……）<br/>下一节我们来看看实现这种操作的其他方式，读到这里，你已经基本上是字符编码的高手了哦。</p><p><strong style="FONT-SIZE: 24px">（六）Java中的增补字符<br/></strong>Java号称对Unicode提供天然的支持，这话在很久很久以前就已经是假的了（不过曾经是真的），实际上，到JDK5.0为止Java才算刚刚跟上Unicode的脚步，开始提供对<span style="COLOR: rgb(80,157,255)">增补字符</span>的支持。<br/>现在的Unicode码空间为U+0000到U+10FFFF，一共1114112个码位，其中只有1,112,064 个码位是合法的（我来替你做算术，有2048个码位不合法），但并不是说现在的Unicode就有这么多个字符了，实际上其中很多码位还是空闲的，到 Unicode 4.0 规范为止，只有96,382个码位被分配了字符（但无论如何，仍比很多人认为的65536个字符要多得多了）。其中U+0000 到U+FFFF的部分被称为<span style="COLOR: rgb(80,157,255)">基本多语言面</span>（Basic Multilingual Plane，BMP）。U+10000及以上的字符称为补充字符。在Java中（Java1.5之后），补充字符使用两个char型变量来表示，这两个 char型变量就组成了所谓的surrogate pair（在底层实际上是使用一个int进行表示的）。第一个char型变量的范围称为"高代理部分"（high-surrogates range,从"uD800到"uDBFF，共1024个码位）, 第二个char型变量的范围称为low-surrogates range（从"uDC00到"uDFFF，共1024个码位），这样使用surrogate pair可以表示的字符数一共是1024的平方计1048576个，加上BMP的65536个码位，去掉2048个非法的码位，正好是1,112,064 个码位。<br/><br/>关于Unicode的码空间实际上有一些稍不小心就会让人犯错的地方。比如我们都知道从U+0000到U+FFFF的部分被称为基本多语言面（Basic Multilingual Plane，BMP），这个范围内的字符在使用UTF-16编码时，只需要一个char型变量就可以保存。仔细看看这个范围，应该有65536这么大，因此你会说单字节的UTF-16编码能够表示65536个字符，你也会说Unicode的基本多语言面包含65536个字符，但是再想想刚才说过的surrogate pair，一个UTF-16表示的增补字符（再一次的，需要两个char型变量才能表示的字符）怎样才能被正确的识别为增补字符，而不是两个普通的字符呢？答案你也知道，就是通过看它的第一个char是不是在高代理范围内，第二个char是不是在低代理范围内来决定，这也意味着，高代理和低代理所占的共 2048个码位（从0xD800到0xDFFF）是不能分配给其他字符的。<br/>但这是对UTF-16这种编码方法而言，而对Unicode这样的字符集呢？在Unicode的编号中，U+D800到U+DFFF是否有字符分配？答案是也没有！这是典型的字符集为方便编码方法而做的安排（你问他们这么做的目的？当然是希望基本多语言面中的字符和一个char型的UTF-16编码的字符能够一一对应，少些麻烦，从中我们也能看出UTF-16与Unicode间很深的渊源与结合）。也就是说，无论Unicode还是UTF-16编码后的字符，在0x0000至0xFFFF这个范围内，只有63488个字符。这就好比最初的CPU被勉强拿来做多媒体应用，用得多了，CPU就不得不修正自己从硬件上对多媒体应用提供支持了。<br/><br/>尽管不情愿，但说到这里总还得扯扯相关的概念：代码点和代码单元。<br/><span style="COLOR: rgb(80,157,255)">代码点</span>（Code Point）就是指Unicode中为字符分配的编号，一个字符只占一个代码点，例如我们说到字符"汉"，它的代码点是U+6C49。<br/><span style="COLOR: rgb(80,157,255)">代码单元<span style="COLOR: rgb(6,0,0)">（Code Unit）</span></span>则是针对编码方法而言，它指的是编码方法中对一个字符编码以后所占的最小存储单元。例如UTF-8中，代码单元是一个字节，因为一个字符可以被编码为1 个，2个或者3个4个字节；在UTF-16中，代码单元变成了两个字节（就是一个char），因为一个字符可以被编码为1个或2个char（你找不到比一个char还小的UTF-16编码的字符，嘿嘿）。说得再罗嗦一点，一个字符，仅仅对应一个代码点，但却可能有多个代码单元（即可能被编码为2个 char）。<br/>以上概念绝非学术化的绕口令，这意味着当你想以一种统一的方式指定自己使用什么字符的时候，使用代码点（即你告诉你的程序，你要用Unicode中的第几个字符）总是比使用代码单元更好（因为这样做的话你还得区分情况，有时候提供一个16进制数字，有时候要提供两个）。<br/>例如我们有一个增补字符？？？（哈哈，你看到了三个问号对吧？因为我的系统显示不出这个字符），它在Unicode中的编号是U+2F81A，当在程序中需要使用这个字符的时候，就可以这样来写：<br/></p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br /><a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter...</a><br /><br />--><span style="COLOR: rgb(0,0,0)">String s</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">String.valueOf(Character.toChars(</span><span style="COLOR: rgb(0,0,0)">0x2F81A</span><span style="COLOR: rgb(0,0,0)">));<br/></span><span style="COLOR: rgb(0,0,255)">char</span><span style="COLOR: rgb(0,0,0)">[]chars</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">s.toCharArray();<br/></span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">c:chars)&#123;<br/>System.out.format(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">%x</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,(</span><span style="COLOR: rgb(0,0,255)">short</span><span style="COLOR: rgb(0,0,0)">)c);<br/>&#125;</span></div><p>后面的for循环把这个字符的UTF-16编码打印了出来，结果是<br/>d87edc1a<br/>注意到了吗？这个字符变成了两个char型变量，其中0xd87e就是高代理部分的值，0xdc1a就是低代理的值。</p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/126/</link>
<title><![CDATA[最能提升工作效率的200个热键]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Tue, 24 Jul 2007 01:52:59 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/126/</guid> 
<description>
<![CDATA[ 
	<p>新闻来源:SmashingMagazine </p><p>使用计算机和软件的最大理由是可以提高工作效率。提高效率的关键，一是佳软，二是善用。熟练掌握热键，乃是高效工作之道的基础和不二法门。下文针对最经典的软件，列举了最实用的快捷键，涉及：操作系统、浏览器、播放器、交流工具、文件管理工具、文本编辑等。要注意的是，相同的全局热键，只能让一个程序生效。  </p><p><strong>Windows 及相应软件</strong> &nbsp;</p><p>1 &nbsp; &nbsp; 最小化所有窗口（显示桌面）/恢复原状 &nbsp; &nbsp; Win + D<br />2 &nbsp; &nbsp; 打开运行对话框 &nbsp; &nbsp; Win + R<br />3 &nbsp; &nbsp; 打开系统属性 &nbsp; &nbsp; Win + Break/Pause<br />4 &nbsp; &nbsp; 复制某一对象 &nbsp; &nbsp; 按住CTRL拖动<br />5 &nbsp; &nbsp; 选中/高亮文本块 &nbsp; &nbsp; CTRL+SHIFT+方向键<br />6 &nbsp; &nbsp; 按打开的顺序在窗口间切换 &nbsp; &nbsp; Alt + Esc<br />7 &nbsp; &nbsp; 复制文件 &nbsp; &nbsp; CTRL + C<br />8 &nbsp; &nbsp; 粘贴文件 &nbsp; &nbsp; CTRL + V<br />9 &nbsp; &nbsp; 剪切文件 &nbsp; &nbsp; CTRL + X<br />10 &nbsp; &nbsp; 还原 &nbsp; &nbsp; CTRL + Z<br />11 &nbsp; &nbsp; 撤消还原操作（如果可能的话） &nbsp; &nbsp; CTRL + Y<br />12 &nbsp; &nbsp; 打开辅助工具 &nbsp; &nbsp; Win + U<br />13 &nbsp; &nbsp; 打开资源管理器 &nbsp; &nbsp; Win + E<br />14 &nbsp; &nbsp; 打开上下文菜单 &nbsp; &nbsp; Shift + F10<br />15 &nbsp; &nbsp; 多页签时，在页签间切换 &nbsp; &nbsp; Ctrl + Tab &nbsp;</p><p> &nbsp; &nbsp;* 更多Windows全局热键: <a href="http://www.helpwithpcs.com/tipsandtricks/keyboard_shortcuts_windows_xp.htm" target="_blank">Windows keyboard shortcuts</a><br /> &nbsp; &nbsp;* 更多Mac OS全局热键: <a href="http://docs.info.apple.com/article.html?artnum=75459" target="_blank">Mac OS X keyboard shortcuts</a><br /> &nbsp; &nbsp;* 更多Linux全局热键: <a href="http://blog.i64.pl/PiosBlog/200610/29-linux-keyboard-shortcuts-you-should-know-about/" target="_blank">Linux Keyboard Shortcuts You Should Know About</a> &nbsp;</p><p>&nbsp;</p><p><strong>浏览器: Firefox</strong> <br />16 &nbsp; &nbsp; 去除CSS样式 &nbsp; &nbsp; Alt + V + Y + N (或 CTRL + Shift + S + Web Developer&rsquo;s 工具栏)<br />17 &nbsp; &nbsp; 恢复CSS样式 &nbsp; &nbsp; Alt + V + Y + B<br />18 &nbsp; &nbsp; 查看源代码 &nbsp; &nbsp; Ctrl + U<br />19 &nbsp; &nbsp; 查看选中内容的源代码 &nbsp; &nbsp; 选取内容，Shift + F10，点&ldquo;Show source code&rdquo;<br />20 &nbsp; &nbsp; 智能 DOM 侦测 &nbsp; &nbsp; Ctrl + Shift + I<br />21 &nbsp; &nbsp; 启动Firebug &nbsp; &nbsp; F12<br />22 &nbsp; &nbsp; 添加书签 &nbsp; &nbsp; Ctrl + D<br />23 &nbsp; &nbsp; 书签 &nbsp; &nbsp; Ctrl + B<br />24 &nbsp; &nbsp; 历鸣 &nbsp; &nbsp; Ctrl + H<br />25 &nbsp; &nbsp; 找开刚关闭的标签 &nbsp; &nbsp; CTRL+SHIFT+T<br />26 &nbsp; &nbsp; 全部标签加入书签 &nbsp; &nbsp; CTRL+SHIFT+D<br />27 &nbsp; &nbsp; 返回 &nbsp; &nbsp; Alt + Left Arrow<br />28 &nbsp; &nbsp; 前进 &nbsp; &nbsp; Alt + Right Arrow<br />29 &nbsp; &nbsp; 访问历史中后退一步 &nbsp; &nbsp; Backspace<br />30 &nbsp; &nbsp; 为书签添加关键词 &nbsp; &nbsp; 这样做可以更快的访问书签。右击书签，选择属性，输入合适的关键词。保存后，你只要在地址栏输入关键词并回车，就可以访问书签了。<br />31 &nbsp; &nbsp; 跳转到地址栏 &nbsp; &nbsp; Ctrl + L 或 F6<br />32 &nbsp; &nbsp; 回到主页 &nbsp; &nbsp; Alt + Home<br />33 &nbsp; &nbsp; 减小文本字号 &nbsp; &nbsp; Ctrl + -<br />34 &nbsp; &nbsp; 增大文本字号 &nbsp; &nbsp; Ctrl + +<br />35 &nbsp; &nbsp; 回到主页（与32重复） &nbsp; &nbsp; Alt + Home<br />36 &nbsp; &nbsp; 快速搜索 &nbsp; &nbsp; /<br />37 &nbsp; &nbsp; 跳转到搜索条 &nbsp; &nbsp; Ctrl + K<br />38 &nbsp; &nbsp; 在标签历史上跳转 &nbsp; &nbsp; ALT + &larr; (后退), ALT + &rarr; (前进)<br />39 &nbsp; &nbsp; 新建标签 &nbsp; &nbsp; Ctrl + T (键盘), 双击标签条 (鼠标)<br />40 &nbsp; &nbsp; 关闭当前书签 &nbsp; &nbsp; Ctrl + W (键盘), 中键标签 (鼠标)<br />41 &nbsp; &nbsp; 到下一标签 &nbsp; &nbsp; Ctrl + Page up 或 CTRL + Tab<br />42 &nbsp; &nbsp; 到前一标签 &nbsp; &nbsp; Ctrl + Page Dn 或 Ctrl + Shift + Tab<br />43 &nbsp; &nbsp; 新标签中打开链接 &nbsp; &nbsp; Ctrl + 左击<br />43 &nbsp; &nbsp; 选择标签 &nbsp; &nbsp; Ctrl + [1 - 9]<br />45 &nbsp; &nbsp; 到下一链接 &nbsp; &nbsp; Tab<br />46 &nbsp; &nbsp; 到前一链接 &nbsp; &nbsp; Shift + Tab<br />47 &nbsp; &nbsp; 输入框中显示输入过的文字，或下拉菜单中显示可选项 &nbsp; &nbsp; Alt + &darr;</p> <p><br /><strong>浏览器: Internet Explorer 7</strong> <br />48 &nbsp; &nbsp; 在新的后台标签打开链接 &nbsp; &nbsp; CTRL+鼠标左键或中键<br />49 &nbsp; &nbsp; 在新的前台标签打开链接 &nbsp; &nbsp; CTRL+SHIFT+鼠标左键或中键<br />50 &nbsp; &nbsp; 打开快速标签视图 &nbsp; &nbsp; CTRL+Q<br />51 &nbsp; &nbsp; 显示已打开标签列表 &nbsp; &nbsp; CTRL+SHIFT+Q<br />52 &nbsp; &nbsp; 转到地址栏 &nbsp; &nbsp; Alt + D<br />53 &nbsp; &nbsp; 在新标签中打开输入的地址 Address Bar in new tab &nbsp; &nbsp; Alt + Enter<br />54 &nbsp; &nbsp; 转到搜索条 &nbsp; &nbsp; Ctrl + E<br />55 &nbsp; &nbsp; 新建标签 &nbsp; &nbsp; Ctrl + T (keyboard), Double Click on Tab Bar (mouse)<br />56 &nbsp; &nbsp; 关闭当前标签 &nbsp; &nbsp; Ctrl + W (keyboard), Middle Click on Tab (mouse)<br />57 &nbsp; &nbsp; 到下一标签 &nbsp; &nbsp; Ctrl + Tab<br />58 &nbsp; &nbsp; 到前一标签 &nbsp; &nbsp; Ctrl + Shift + Tab<br />59 &nbsp; &nbsp; 到某一标签 &nbsp; &nbsp; Ctrl + [1 - 9]<br />60 &nbsp; &nbsp; 打开feeds &nbsp; &nbsp; CTRL+J<br />61 &nbsp; &nbsp; 到下一标签 &nbsp; &nbsp; Tab<br />62 &nbsp; &nbsp; 到前一标签 &nbsp; &nbsp; Shift + Tab </p> <p> &nbsp; &nbsp;* <a href="http://allhotkeys.com/safari_hotkeys.html" target="_blank">Safari 热键</a><br /> &nbsp; &nbsp;* <a href="http://allhotkeys.com/microsoft-internet-explorer-7-hotkeys.html" target="_blank">更多 MSIE 7热键</a></p> <p>&nbsp;</p> <p><strong>音乐播放: Winamp</strong> &nbsp; </p><p>要使用全局热键，须设置：Main Windows -&gt; Options &gt; Preferences &gt; Global Hotkeys。其热键可自定义。<br />63 &nbsp; &nbsp; 增大音量 &nbsp; &nbsp; CTRL + ALT + &uarr;<br />64 &nbsp; &nbsp; 减小音量 &nbsp; &nbsp; CTRL + ALT + &darr;<br />65 &nbsp; &nbsp; 播放、重新开始或恢复播放 &nbsp; &nbsp; Winamp 窗口: x<br />66 &nbsp; &nbsp; 暂停 &nbsp; &nbsp; CTRL + ALT + Home (Winamp 窗口: c)<br />67 &nbsp; &nbsp; 播放 &nbsp; &nbsp; CTRL + ALT + Insert (Winamp 窗口: x)<br />68 &nbsp; &nbsp; 停止 &nbsp; &nbsp; CTRL + ALT + End (Winamp 窗口: v)<br />69 &nbsp; &nbsp; 前一首 &nbsp; &nbsp; CTRL + ALT + PgUp (Winamp 窗口: z)<br />70 &nbsp; &nbsp; 下一首 &nbsp; &nbsp; CTRL + ALT + PgDn (Winamp 窗口: b)<br />71 &nbsp; &nbsp; 返回5秒 &nbsp; &nbsp; CTRL + ALT + &larr; (Winamp 窗口: &larr;)<br />72 &nbsp; &nbsp; 快进5秒 &nbsp; &nbsp; CTRL + ALT + &rarr; (Winamp 窗口: &rarr;)<br />73 &nbsp; &nbsp; 切换重复 &nbsp; &nbsp; r<br />74 &nbsp; &nbsp; 切换随机 &nbsp; &nbsp; s<br />75 &nbsp; &nbsp; 添加文件 &nbsp; &nbsp; l<br />76 &nbsp; &nbsp; 添加目录 &nbsp; &nbsp; Shift + l<br />77 &nbsp; &nbsp; 随机播放列表 &nbsp; &nbsp; CTRL + Shift + r &nbsp;</p><p> &nbsp; &nbsp;* 更多热键: <a href="http://www.shortcutworld.com/shortcuts.html?id=winamp" target="_blank">Winamp Shortcuts</a><br /> &nbsp; &nbsp; &nbsp;<a href="http://malektips.com/wa50028.html" target="_blank">Winamp Global Hotkeys</a><br /> &nbsp; &nbsp;* iTunes 热键: <a href="http://www.smashingmagazine.com/2007/07/20/developers-alarm-200-hotkeys-to-boost-your-productivity/http%3A%2F%2Flifehacker.com%2Fsoftware%2Ffeatured-windows-download%2Fcontrol-itunes-using-keyboard-shortcuts-with-itunes-hotkey-263925.php" target="_blank">Hotkeys for iTunes</a><br /> &nbsp; &nbsp;* Last.FM 热键: <a href="http://tadhg.com/wp/2007/03/23/autohotkey-script-for-lastfm/" target="_blank">Autohotkey Script for Last.FM</a> &nbsp;</p><p>&nbsp;</p><p><strong>通讯: Thunderbird</strong> &nbsp;</p><p> &nbsp; &nbsp;* 在Skype中可自定义热键: Main Window -&gt; Tools -&gt; Options -&gt; Hotkeys.<br /> &nbsp; &nbsp;* 在Thunderbird中使用热键: <a href="http://www.mozilla.org/support/thunderbird/keyboard" target="_blank">Thunderbird Help: Keyboard Shortcuts</a> 和 <a href="http://allhotkeys.com/mozilla_thunderbird_hotkeys.html" target="_blank">All hotkeys: Mozilla Thunderbird Hotkeys</a><br /> &nbsp; &nbsp;* xbeta提醒：Thunderbird和Firefox很多热键是相通的，并且可用keyconfig扩展更改和自定义更多热键。 &nbsp;</p><p>78 &nbsp; &nbsp; 转到下一封信 &nbsp; &nbsp; F<br />79 &nbsp; &nbsp; 转到下一封未读信件 &nbsp; &nbsp; N<br />80 &nbsp; &nbsp; 转到上一封未读信件 &nbsp; &nbsp; B<br />81 &nbsp; &nbsp; 增大字号 &nbsp; &nbsp; Ctrl + +<br />82 &nbsp; &nbsp; 减小字号 &nbsp; &nbsp; CTRL + -<br />83 &nbsp; &nbsp; 标记信件为已读/未读 &nbsp; &nbsp; M<br />84 &nbsp; &nbsp; 标记为垃圾邮件 &nbsp; &nbsp; J<br />85 &nbsp; &nbsp; 标记为非垃圾邮件 &nbsp; &nbsp; SHIFT + J<br />86 &nbsp; &nbsp; 查看信件源文件 &nbsp; &nbsp; CTRL + U<br />87 &nbsp; &nbsp; 新建信件 &nbsp; &nbsp; CTRL + M, CTRL + N, Cmd + Shift + M (Mac)<br />88 &nbsp; &nbsp; 回复信件 &nbsp; &nbsp; Ctrl + R<br />89 &nbsp; &nbsp; 收取当前账户的信件 &nbsp; &nbsp; CTRL+T<br />90 &nbsp; &nbsp; 收取所有账户的信件 &nbsp; &nbsp; CTRL+SHIFT+T<br />91 &nbsp; &nbsp; 打开已收的信件 &nbsp; &nbsp; CTRL + O<br />92 &nbsp; &nbsp; 发送信件 &nbsp; &nbsp; CTRL + Enter/Return</p> <p><br /><strong>通讯: Google Mail</strong> <br />93 &nbsp; &nbsp; 写邮件 &nbsp; &nbsp; c, + c 在新窗口中写信<br />94 &nbsp; &nbsp; 回信 &nbsp; &nbsp; r, + r 在新窗口中回信<br />95 &nbsp; &nbsp; 转发信件 &nbsp; &nbsp; f, + f 在新窗口中转发信件<br />96 &nbsp; &nbsp; 转到收件箱 &nbsp; &nbsp; g 然后按 i<br />97 &nbsp; &nbsp; 定位到搜索框 &nbsp; &nbsp; /<br />98 &nbsp; &nbsp; 转到下一封信 &nbsp; &nbsp; n<br />99 &nbsp; &nbsp; 转到上一封信 &nbsp; &nbsp; p<br />100 &nbsp; &nbsp; 报告垃圾邮件 &nbsp; &nbsp; ! </p> <p>&nbsp;</p> <p><strong>文件管理: Total Commander</strong> </p> <p>Total Commander是Windows 下键盘操作的典范和极致。用TC不会热键，等于不懂TC。喜欢热键操作而未用TC&mdash;&mdash;xbeta想不出会有这种情况。TC的热键是高度可以自定义的。如下举例仅是管中窥豹，还有更多热键需要你的发现，比如很重要的 alt+F5, alt+F9, alt+enter, &rarr;，space, alt+shift+enter, backspace，+, alt+, alt+F7, 及中国用户广泛自定义的ctrl+1,ctrl+2, ctrl+3等。<br />101 &nbsp; &nbsp; 激活或取消激活左侧菜单 &nbsp; &nbsp; F10<br />102 &nbsp; &nbsp; 比较和同步文件夹 &nbsp; &nbsp; SHIFT+F2<br />103 &nbsp; &nbsp; 新建文本文件，并用编辑器打开 &nbsp; &nbsp; SHIFT + F4<br />104 &nbsp; &nbsp; 在同一目录复制文件并改名 &nbsp; &nbsp; SHIFT + F5<br />105 &nbsp; &nbsp; 重命名文件 &nbsp; &nbsp; SHIFT + F6<br />106 &nbsp; &nbsp; 缩小到系统托盘图标 &nbsp; &nbsp; SHIFT + Esc<br />107 &nbsp; &nbsp; 转到刚访问的前/后一个目录 &nbsp; &nbsp; ALT+left/right<br />108 &nbsp; &nbsp; 显示已访问目录历史清单 &nbsp; &nbsp; ALT + Arrow Down<br />109 &nbsp; &nbsp; 全选 &nbsp; &nbsp; CTRL+NUM +, CTRL + A<br />110 &nbsp; &nbsp; 全不选 &nbsp; &nbsp; CTRL+NUM -<br />111 &nbsp; &nbsp; 返回上级目录 (cd ..) &nbsp; &nbsp; CTRL+PgUp or Backspace<br />112 &nbsp; &nbsp; 返回根目录 (多数欧洲键盘)（xbeta:适用于中国用户） &nbsp; &nbsp; CTRL+<br />113 &nbsp; &nbsp; 返回根目录 (美国键盘布局)（xbeta注:对中国用户是新建目录） &nbsp; &nbsp; F7<br />114 &nbsp; &nbsp; 按文件名排序 &nbsp; &nbsp; CTRL+F3<br />115 &nbsp; &nbsp; 按后缀排序 &nbsp; &nbsp; CTRL+F4<br />116 &nbsp; &nbsp; 按时间/日期排序 &nbsp; &nbsp; CTRL+F5<br />117 &nbsp; &nbsp; 按大小排序 &nbsp; &nbsp; CTRL+F6<br />118 &nbsp; &nbsp; 显示所有文件 &nbsp; &nbsp; CTRL+F10<br />119 &nbsp; &nbsp; 仅显示可执行文件 &nbsp; &nbsp; CTRL + F11<br />120 &nbsp; &nbsp; 显示用户自定义的文件 &nbsp; &nbsp; CTRL + F12<br />121 &nbsp; &nbsp; 显示文件属性 &nbsp; &nbsp; ALT+ENTER<br />122 &nbsp; &nbsp; 并列显示目录及所有子目录下的全部文件（xbeta:这是一个非常实用的功能，不用TC的人体会不到） &nbsp; &nbsp; CTRL + B<br />123 &nbsp; &nbsp; 进入目录收藏列表（书签）（xbeta:TC用户就称之为ctrl+d） &nbsp; &nbsp; CTRL + D<br />124 &nbsp; &nbsp; 连接FTP &nbsp; &nbsp; CTRL + F (断开: CTRL + SHIFT + F)<br />125 &nbsp; &nbsp; 批量改名（xbeta:极其强大） &nbsp; &nbsp; CTRL + M<br />126 &nbsp; &nbsp; 进入命令行对话框并带入当前路径 &nbsp; &nbsp; CTRL + P<br />127 &nbsp; &nbsp; 快速浏览（xbeta:这是TC最常用、最实用的热键。相似功能F3） &nbsp; &nbsp; CTRL + Q<br />128 &nbsp; &nbsp; 新建目录标签并激活它 &nbsp; &nbsp; CTRL + T (CTRL + SHIFT + T是新建标签但不激活)<br />129 &nbsp; &nbsp; 左右窗口交换目录 &nbsp; &nbsp; CTRL + U<br />130 &nbsp; &nbsp; 左右窗口交换目录和标签 &nbsp; &nbsp; CTRL + SHIFT + U<br />131 &nbsp; &nbsp; 把光标处目录在新标签打开 &nbsp; &nbsp; CTRL + Arrow Up<br />132 &nbsp; &nbsp; 转到下一标签 &nbsp; &nbsp; CTRL + TAB<br />133 &nbsp; &nbsp; 转到上一标签 &nbsp; &nbsp; CTRL+SHIFT+TAB<br />134 &nbsp; &nbsp; 定位到某一目录/文件（xbeta强烈建议定位框模式） &nbsp; &nbsp; CTRL+ALT+字母(s)<br /></p> <p><strong>Wordpress</strong> <br />135 &nbsp; &nbsp; 加粗 &nbsp; &nbsp; Alt + Shift + B<br />136 &nbsp; &nbsp; 斜体 &nbsp; &nbsp; Alt + Shift + I<br />137 &nbsp; &nbsp; 引用 &nbsp; &nbsp; Alt + Shift + Q<br />138 &nbsp; &nbsp; 列表 (ul) &nbsp; &nbsp; Alt + Shift + U<br />139 &nbsp; &nbsp; 序号列表 (ol) &nbsp; &nbsp; Alt + Shift + O<br />140 &nbsp; &nbsp; 列表项 (li) &nbsp; &nbsp; Alt + Shift + L<br />141 &nbsp; &nbsp; 代码 &nbsp; &nbsp; Alt + Shift + C<br />142 &nbsp; &nbsp; 插入 &nbsp; &nbsp; Alt + Shift + S<br />143 &nbsp; &nbsp; 删除 &nbsp; &nbsp; Alt + Shift + D<br />144 &nbsp; &nbsp; 链接 &nbsp; &nbsp; Alt + Shift + A<br />145 &nbsp; &nbsp; more (Read More tag) &nbsp; &nbsp; Alt + Shift + T<br />146 &nbsp; &nbsp; 发表文章 &nbsp; &nbsp; Alt + Shift + P</p> <p><br /><strong>通讯: Microsoft Office Outlook</strong> <br />147 &nbsp; &nbsp; 切换到邮件 &nbsp; &nbsp; CTRL + 1<br />148 &nbsp; &nbsp; 切换到日历 &nbsp; &nbsp; CTRL + 2<br />149 &nbsp; &nbsp; 切换到通讯录 &nbsp; &nbsp; Ctrl + 3<br />150 &nbsp; &nbsp; 切换到任务 &nbsp; &nbsp; Ctrl + 4<br />151 &nbsp; &nbsp; 切换到笔记 &nbsp; &nbsp; Ctrl + 5<br />152 &nbsp; &nbsp; 新建约会 &nbsp; &nbsp; CTRL+SHIFT+A<br />153 &nbsp; &nbsp; 新建联系人 &nbsp; &nbsp; CTRL+SHIFT+C<br />154 &nbsp; &nbsp; 新建日志项 &nbsp; &nbsp; CTRL+SHIFT+J<br />155 &nbsp; &nbsp; 新建会议 &nbsp; &nbsp; CTRL+SHIFT+Q<br />156 &nbsp; &nbsp; 新建信件 &nbsp; &nbsp; CTRL+SHIFT+M<br />157 &nbsp; &nbsp; 新建笔记 &nbsp; &nbsp; CTRL+SHIFT+N<br />158 &nbsp; &nbsp; 新建任务 &nbsp; &nbsp; CTRL + SHIFT + K<br />159 &nbsp; &nbsp; 拼写检查 &nbsp; &nbsp; F7<br />160 &nbsp; &nbsp; 转发信件 &nbsp; &nbsp; CTRL + F<br />161 &nbsp; &nbsp; 搜索 &nbsp; &nbsp; F4<br />162 &nbsp; &nbsp; 切换到收件箱 &nbsp; &nbsp; CTRL+SHIFT+I<br />163 &nbsp; &nbsp; 切换到发件箱 &nbsp; &nbsp; CTRL+SHIFT+O<br />164 &nbsp; &nbsp; 发送 &nbsp; &nbsp; Alt + S<br />165 &nbsp; &nbsp; 回信 &nbsp; &nbsp; Ctrl + SHIFT + R<br />166 &nbsp; &nbsp; 收信 &nbsp; &nbsp; CTRL+M or F9<br />167 &nbsp; &nbsp; 写信 &nbsp; &nbsp; CTRL + N<br />168 &nbsp; &nbsp; 打开信件 &nbsp; &nbsp; CTRL + O<br />169 &nbsp; &nbsp; 标为已读 &nbsp; &nbsp; CTRL + Q </p> <p> &nbsp; &nbsp;* Outlook的全局热键: <a href="http://office.microsoft.com/en-gb/outlook/HP030842231033.aspx" target="_blank">Keyboard shortcuts for Outlook</a> &nbsp;</p><p>&nbsp;</p><p><strong>通讯: ICQ</strong> <br />170 &nbsp; &nbsp; 模仿在系统托盘中双击 &nbsp; &nbsp; CTRL + SHIFT + I<br />171 &nbsp; &nbsp; 激活/取消激活用户窗口 &nbsp; &nbsp; CTRL + SHIFT + A<br />172 &nbsp; &nbsp; 关闭 ICQ &nbsp; &nbsp; Control + Shift + I and then Alt + F4<br />173 &nbsp; &nbsp; 发送url给联系人 &nbsp; &nbsp; Control + Shift + F6<br />174 &nbsp; &nbsp; 改变状态 &nbsp; &nbsp; Alt + S + &lt;PgDn&gt;<br />175 &nbsp; &nbsp; 接收信息 &nbsp; &nbsp; Control + Shift + I<br />176 &nbsp; &nbsp; 选择好友 &nbsp; &nbsp; Insert + &lt;Character&gt; &nbsp;</p><p>&nbsp;</p><p><strong>图像管理: ACDSee Viewer</strong> <br />177 &nbsp; &nbsp; 缩小显示比例 &nbsp; &nbsp; - (就是减号)<br />178 &nbsp; &nbsp; 增加显示比例 &nbsp; &nbsp; +<br />179 &nbsp; &nbsp; 复制当前项到某目录 &nbsp; &nbsp; ALT + C<br />180 &nbsp; &nbsp; 移动当前项到某目录 &nbsp; &nbsp; ALT + M<br />181 &nbsp; &nbsp; 改名当前项到某目录 &nbsp; &nbsp; ALT + R<br />182 &nbsp; &nbsp; 打开属性面板 &nbsp; &nbsp; Alt + Enter<br />183 &nbsp; &nbsp; 显/隐状态栏 &nbsp; &nbsp; b<br />184 &nbsp; &nbsp; 显/隐菜单栏 &nbsp; &nbsp; Ctrl + Shift + M<br />185 &nbsp; &nbsp; 用默认程序打开当前图像 &nbsp; &nbsp; CTRL + E<br />186 &nbsp; &nbsp; 进入转换对话框 &nbsp; &nbsp; CTRL + F<br />187 &nbsp; &nbsp; 复制选中部分到剪贴板 &nbsp; &nbsp; Ctrl + Insert<br />188 &nbsp; &nbsp; 打开旋转/镜像对话框 &nbsp; &nbsp; CTRL + J<br />189 &nbsp; &nbsp; 打开当前图像，进入编辑模式，激活调节曝光工具 &nbsp; &nbsp; Ctrl + L<br />190 &nbsp; &nbsp; 打开当前图像，进入编辑模式，激活调节大小工具 &nbsp; &nbsp; CTRL+R<br />191 &nbsp; &nbsp; 清空选中内容 &nbsp; &nbsp; CTRL+Q<br />192 &nbsp; &nbsp; 保存图像 &nbsp; &nbsp; CTRL + S<br />193 &nbsp; &nbsp; 关闭查看窗口 &nbsp; &nbsp; CTRL + W<br />194 &nbsp; &nbsp; 90度顺时针旋转当前图片 &nbsp; &nbsp; Ctrl + Alt + &lt;right arrow&gt;<br />195 &nbsp; &nbsp; 改变图像为黑白两色 &nbsp; &nbsp; Ctrl + Shift + 1<br />196 &nbsp; &nbsp; 进入批量处理菜单 &nbsp; &nbsp; CTRL + Alt + B<br />197 &nbsp; &nbsp; 以默认程序打开当前文件 &nbsp; &nbsp; Shift + E &nbsp;</p><p> &nbsp; &nbsp;* 在小巧但极其强大的IrfanView中使用热键: <a href="http://www.indeavors.com/resources/ivtutorial/iv8.htm" target="_blank">Irfanview</a> &nbsp;</p><p>&nbsp;</p><p><strong>编辑: Ultraedit</strong> &nbsp;</p><p>说到编辑器的快捷键，VIM是无与伦比的。要反对，也得是带脚踏板的Emacs。UE还是有差距的，很大差距。注意：VIM是开源、免费的，而UE则需要注册。UE是Windows下最好的编辑器&mdash;&mdash;如果没有GVIM和Emacs的话。而VIM和Emacs则是任何操作系统下最好的编辑器。<br />198 &nbsp; &nbsp; 自动换行 &nbsp; &nbsp; CTRL + W<br />199 &nbsp; &nbsp; 插入当前日期/时间 &nbsp; &nbsp; F7<br />200 &nbsp; &nbsp; 找到匹配的括号 (,[,&#123; or &#125;,],) &nbsp; &nbsp; CTRL + B<br />201 &nbsp; &nbsp; 段落重新格式化 &nbsp; &nbsp; CTRL + T<br />202 &nbsp; &nbsp; Tag 列表 &nbsp; &nbsp; CTRL + F8<br />203 &nbsp; &nbsp; 转换所选文字为小写 &nbsp; &nbsp; CTRL + F5<br />204 &nbsp; &nbsp; 转换所选文字为大写 &nbsp; &nbsp; Alt + F5<br />205 &nbsp; &nbsp; 激活拼写检查 &nbsp; &nbsp; CTRL + K<br />206 &nbsp; &nbsp; 切换列/块模式 &nbsp; &nbsp; ALT + C<br />207 &nbsp; &nbsp; 设定书签 &nbsp; &nbsp; CTRL + F2<br />208 &nbsp; &nbsp; 转到下一书签 &nbsp; &nbsp; F2<br />209 &nbsp; &nbsp; 插入用户定义的模板 &nbsp; &nbsp; Alt+0-9 or Shift+Alt+0-9<br />210 &nbsp; &nbsp; 上滚一行，光标不变 &nbsp; &nbsp; CTRL + Up<br />211 &nbsp; &nbsp; 下滚一行，光标不变 &nbsp; &nbsp; CTRL + Down<br />212 &nbsp; &nbsp; 显示函数列表 &nbsp; &nbsp; F8<br />213 &nbsp; &nbsp; 到下一段 &nbsp; &nbsp; Alt + Right<br />214 &nbsp; &nbsp; 到上一段 &nbsp; &nbsp; Alt + Left</p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E5%25BF%25AB%25E6%258D%25B7%25E9%2594%25AE/" rel="tag">快捷键</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%258A%2580%25E5%25B7%25A7/" rel="tag">技巧</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/124/</link>
<title><![CDATA[常用开源协议详细解析]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 25 Jun 2007 03:27:07 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/124/</guid> 
<description>
<![CDATA[ 
	原载于：<a href="http://www.cnbeta.com/articles/28880.htm" target="_blank">cnbeta</a><br /><br /><br /><p>开源在今天的软件业已经很普遍,但开源是否意味着使用者可以对开源后的代码为所欲为呢?答案是否定的.开源运动同样有自己的游戏规则和道德准则.不遵行这些规则不但损害开源运动的健康发展,也会对违规者造成名誉和市场上的损失,更可能陷入法律纠纷和赔偿.<br /><span style="font-weight: bold">　　首先,要对几个概念有所了解:</span><br /><br /></p> &nbsp;&nbsp;&nbsp;&nbsp;<span style="font-weight: bold">　　1. Contributors 和 Recipients</span><br /><br />　 　Contributors 指的是对某个开源软件或项目提供了代码(包括最初的或者修改过的)发布的人或者实体(团队、公司、组织等),Contributors 按照参与某个软件开源的时间先后,可以分为 an initial Contributor 和 subsequent Contributors .<br /><br />　　Recipients指的是开源软件或项目的获取者,显然,subsequent Contributors 也属于 Recipients之列.<br /><br /><span style="font-weight: bold">　　2. Source Code 和 Object Code</span><br /><br />　　Source Code 指的是各种语言写成的源代码,通过Source Code,结合文档, 可以了解到整个软件的体系结构及具体到某个功能函数的实现方法等.<br /><br />　　Object Code 指的是Source Code 经过编译之后,生成的类似于&ldquo;类库&rdquo;一样的,提供各种接口供他人使用的目标码,按我的理解,它就是像常见的DLL、ActiveX、OCX控件性质的东西.(不知道这样理解对不对)<br /><br />　　分清楚这两个概念的目的在于,有些开源,只发布Object Code ,当然,大多数发布的是Source Code.很多协议也对 &ldquo;你发布的是哪种Code的时候应该怎样&rdquo;,有着明确的约束.<br /><br /><span style="font-weight: bold">　　3. Derivative Module 和 Separate Module</span><br /><br />　　Derivative Module 指的是,依托或包含&ldquo;最初的&rdquo;或者&ldquo;从别人处获取的&rdquo;开源代码而产生的代码,是原&ldquo;源代码&rdquo;的增强(不等于增加)、改善和延续的模块,意为&ldquo;衍生模块&rdquo;.<br /><br />　　Separate Module 指的是,参考或借助原&ldquo;源代码&rdquo;,开发出的独立的,不包含、不依赖于原&ldquo;源代码模块&rdquo;,意为&ldquo;独立的模块&rdquo;.理解这两个概念的目的在于,很多协议对涉及到商业发布的时候,会有哪些是衍生的,哪些是独立的,有着明确的商业发布规定.<br /><br />　　现今存在的开源协议很多,而经过Open Source Initiative组织通过批准的开源协议目前有58种.我们在常见的开源协议如BSD, GPL, LGPL,MIT等都是OSI批准的协议.如果要开源自己的代码,最好也是选择这些被批准的开源协议.<br />　　这里我们来看四种最常用的开源协议及它们的适用范围,供那些准备开源或者使用开源产品的开发人员/厂家参考.<br /><br /><span style="font-weight: bold">　　BSD开源协议(Berkeley Software Distribution )</span><br /><br />　 　BSD开源协议是一个给予使用者很大自由的协议.基本上使用者可以&ldquo;为所欲为&rdquo;可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件 再发布.但&ldquo;为所欲为&rdquo;的前提当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:<br /><br />　　1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议.<br /><br />　　2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议.<br /><br />　　3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广.<br /><br />　　其实这几个规则约定的目的也只是达到一个目的:是他人的东西,别人以BSD开源了,你就不能不做任何声明而占为己有,更不能用他人的名义来做商业推广.你只对你自己的东西拥有绝对控制权.<br /><br />　 　举个例子,你用开源代码(A)修改或做其他增添之后,产生了产品B,这时候,你对B的控制由你自己决定,你可以用任何协议再开源,也可以闭源商业发布. 但,因为如果B中包含了A或A的一部分(一点都不包含就不叫修改了),那你在B产品的版权声明中,必须有提到你有使用到 A ,并且附带上 A 的开源协议.而且不能做商业推广的时候将B 冠以原开源作者的名义以促进商业推广.<br /><br />　　BSD代码鼓励代码共享,但需要尊重代码作者的著作权.BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上<br />开发商业软件发布和销售,因此是对商业集成很友好的协议.而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发.<br /><br /><span style="font-weight: bold">　&nbsp;&nbsp; Apache Licence 2.0</span><br /><br />　　Apache Licence是著名的非盈利开源组织Apache采用的协议.该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件).需要满足的条件也和BSD类似:<br /><br />　　1. 需要给代码的用户一份Apache Licence<br /><br />　　2. 如果你修改了代码,需要再被修改的文件中说明.<br /><br />　　3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明.<br /><br />　　4. 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence.你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改.<br /><br />　　Apache Licence也是对商业应用友好的许可.使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售.<br /><br /><span style="font-weight: bold">　&nbsp; GPL (Gun General Public License)vesion 2.0 1991</span><br /><br />　 　我们很熟悉的Linux就是采用了GPL.GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样.GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代 码做为闭源的商业软件发布和销售.这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商 业软件公司开发的免费软件了.<br /><br />　　GPL协议的主要内容是只要在一个软件中使用(&ldquo;使用&rdquo;指类库引用,修改后的代码或者衍生代码)GPL 协议的产品,则该软件产品必须也采用 GPL协议,既必须也是开源和免费.这就是所谓的&ldquo;传染性&rdquo;.GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势.<br /><br />　　由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础.<br /><br />　　最常见的开源协议,使用它作为授权协议的有大名鼎鼎的 Linux .GPL最显著的两个特点就是网上称为的&ldquo;病毒性传播&rdquo;和&ldquo;不允许闭源的商业发布&rdquo;.<br /><br />　 　所谓的&ldquo;病毒性传播&rdquo;,指的是,GPL规定,所有从GPL协议授权的源码衍生出来的(即上面提到的Derivative Module),或者要跟GPL授权的源码混着用的Project,都要遵循GPL协议,就像病毒一样,粘上了关系,就&ldquo;中毒&rdquo;了.GPL这样规定的目的 是,保证在GPL协议保护下的产品,不会再受到其他协议或者授权的约束.即让跟GPL有关系的源码都能免费获取.举个例子,如果你的改进的Linux中使 用了GPL授权下的开源模块(也必须使用,你不可能自己重新去做个内核吧,如果做出来了,你也没必要叫Linux了.),那么你整个Linux产品也必须 遵循GPL协议去开源,不能以其他方式去开源发布,更不允许闭源发布.这样一来,就不会出现这样一个Linux--这个功能是GPL协议授权的,可以免费 获取源码,而另外一个功能是其他协议下的,拿不到源码.这点规定对使用或者研究该产品的人来说,是一个极大的便利.<br /><br />　　而&ldquo;不允许闭源商 业发布&rdquo;指的是,在GPL授权下,你的软件产品可以商业发布,拿去卖钱,但是在这同时,你也必须将该产品的源码以GPL协议方式开源发布出去,供他人免费 获取.也许有人会迷惑,拿去卖,又同时开源,那谁来买阿?这个产品怎么赚钱呢??这就涉及到开源产品的商业模式的问题了,想了解相关一些信息的话,可以看 看以上我给出链接的一些文章.至于后面,可能会写一篇关于开源项目的商业模式的随笔.<br /><br />　　GPL协议下的商业发布的一个关键点就像 Java 视线论坛的 Robbin所说的,GPL是针对软件源代码的版权,而不是针对软件编译后二进制版本的版权.你有权免费获得软件的源代码,但是你没有权力免费获得软件的 二进制发行版本.GP对软件发行版本唯一的限制就是:你的发行版本必须把完整的源代码一同提供.<br /><br />　　它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似.<br /><br /><span style="font-weight: bold">　　LGPL</span><br /><br />　 　LGPL是GPL的一个为主要为类库使用设计的开源协议.和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同. LGPL允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码.这使得采用LGPL协议的开源代码可以被商业软件作为类 库引用并发布和销售.<br /><br />　　但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用 LGPL协议.因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发 的商业软件采用.<br /><br />　　GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品.<br /><br /><span style="font-weight: bold">　　CPL(Common Public Liecense) vesion 1.0</span><br /><br />　　CPL是IBM 提出的并通过了OSI(Open Source Initiative)批准的开源协议.主要用于一些IBM或跟IBM相关的开源软件/项目中.如很著名的Java开发环境 Eclipse 、RIA开发平台Open Laszlo等.<br /><br />　　CPL也是一项对商业应用友好的协议.它允许 Recipients 对源码进行任意的使用、复制、分发、传播、展示、修改以及改后做闭源的二次商业发布,这点跟 BSD 很类似,也属于自由度比较高的开源协议.但是,需要遵循:<br /><br />　　1. 当一个Contributors将源码的整体或部分再次开源发布的时候,必须继续遵循 CPL开源协议来发布,而不能改用其他协议发布.除非你得到了原&ldquo;源码&rdquo;Owner 的授权.<br /><br />　　2. CPL协议下,你可以将源码不做任何修改来商业发布.但如果你要将修改后的源码其开源,而且当你再发布的是Object Code的时候,你必须声明它的Source Code 是可以获取的,而且要告知获取方法.<br /><br />　　3. 当你需要将CPL下的源码作为一部分跟其他私有的源码混和着成为一个 Project 发布的时候,你可以将整个Project/Product 以私人的协议发布,但要声明哪一部分代码是CPL下的,而且声明那部分代码继续遵循CPL.<br /><br />　　4. 独立的模块(Separate Module),不需要开源.<br /><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E5%25BC%2580%25E6%25BA%2590/" rel="tag">开源</a> , <a href="http://www.newtyper.com/blog/tags/%25E5%258D%258F%25E8%25AE%25AE/" rel="tag">协议</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25A8%258B%25E5%25BA%258F/" rel="tag">程序</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/123/</link>
<title><![CDATA[对学生朋友的一点建议]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Wed, 13 Jun 2007 09:26:23 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/123/</guid> 
<description>
<![CDATA[ 
	转载于：<a href="http://googlechinablog.com/" target="_blank" title="google黑板报">google黑板报</a><br /><br /><h3><a name="1223076947729616429" title="1223076947729616429"></a>对学生朋友的一点建议</h3> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <p class="byline-timestamp"><span>2007年6月1日 上午 11:09:00</span></p><span class="byline-author">发表者：Google（谷歌）中国工程研究院 工程师 方坤<br /> </span><br />自 去年春天加入谷歌，我曾多次随公司校园招聘团队一起走访各地院校，帮助公司发掘人才。利用这样的出差机会到处走走看看，饱览祖国大好河山，是我一点小小的 私心。但更具挑战性、更激动人心的，还是和我未来的同事们过招，不打不相识嘛。一想到马上就要和这样优秀的人才一起工作，我就兴奋不已，真恨不得现在就买 一张单程机票把他/她拽回北京。 <br /><br />然而，在面试过程中，我也相当惋惜地看到，由于种种主客观原因的限制，很多青春韶华的学子未能充分发挥出他们应有的潜力。这里我不讲成绩，只把我遇到过的一些普遍的问题归纳一下，希望对即将踏入社会的学生朋友们能有些许帮助。<br /><br /><strong>实战！实战！</strong><br /><br />纸 上得来终觉浅，绝知此事要躬行。对于一个未来的软件工程师来说，实际编程经验是相当重要的。我们会要求应聘者在紧凑的时间内编写大量的代码，从中考察应聘 者的分析能力，编码速度，代码可读性，对所用编程语言的掌握程度，对边界条件与异常状况的处理，数据结构与函数接口的定义，程序运行的效率和应聘者查错纠 错的能力等等。一口气列出这么多要求，听上去似乎过于苛刻，其实只要积累了足够的实战经验，每一个应聘者都完全可以满足这些最低限度的要求。 <br /><br />我 记得开复对于青年学子们有一个建议，大学四年，至少要编写 10 万行代码。不是每一个人都期望进入谷歌这样的顶级技术公司，但即使对开复的建议打个三折，也还有 3 万行呢，不努力，能行嘛。前来应聘的学生们在编写代码时暴露出这样那样的问题，大都可以归结到同一个原因：锻炼太少。比如&quot;for (int i = 0; i &lt; strlen(s); ++i)&quot;，没有实战经验的人，怎么可能意识到暗藏其间的效率陷阱。再比如内存泄漏，就和初恋一样，没有亲身经历过的人，不会有刻骨铭心的感受，而一旦经 历，终身难忘，根本用不着你有多聪明。遗憾的是，我看到许多相当聪颖的学生写出来的代码只能用惨不忍睹来形容，真让我怀疑这是不是就是他们的第一次。大家 不要怪谷歌要求高，恕我直言，如果不能持之以恒，下点儿苦功夫，不光谷歌一家，其它公司恐怕也是进不去的。 <br /><br />北京、上海等地的学生，往 往有更多实习、兼职机会，接受锻炼之余，还能攒下一笔小钱作零用之资。其它一些地区的学生也许就没有这么幸运，这就尤其需要积极主动，寻找机会，创造机 会，把握机会。世界正在变平，互联网的发展改变着地球的面貌，让不发达地区的学生也能相对容易地接触到发达地区的资源。前不久我在西安遇到一个学生，他半 年来一直坚持在北京大学的 ACM 网站上参赛、做题，我看他写出来的程序就确实比他大多数同学都要好一些。我相信，只要他能够持之以恒，还会取得更大的进步。我听说浙江大学的 ACM 网站、TopCoder 网站也都是不错的教育资源，感兴趣的同学不妨一看；虽说做竞赛题距离真正的软件开发还有着显著差别，但也不失为一个不错的出发点。 <br /><br />在 积累一定编码量的基础上，我建议大家总结经验教训，不断提高自我。如果几万行代码写下来，学 C++ 的不知道 const 怎么用，学 python 的没听说过 GIL，我看也没什么意思。到了这个阶段，我推荐大家读一些经典的进阶书籍，例如《Effective C++》、《Effective Java》等，即使地处偏远地区，也可通过网上书店买到。（我建议编码量太少的同学就先不要读了，会走火入魔的。我见过有学生连引用和指针都没搞清楚，就 在那里重载操作符的。）交流也很重要，如果能够与网上网下志同道合的朋友互相帮助，共同进步，当收事半功倍之效。在这一方面，各校的 Google Camp 也许能够起到一定作用。 <br /><br /><strong>&quot;我会写代码呀，为什么谷歌不要我？&quot;</strong><br /><br />一家外 包公司或许会满足于雇佣仅能从事简单的、机械性的重复劳动的软件蓝领，而让谷歌苦苦寻觅的，乃是最优秀的软件工程师兼计算机科学家&mdash;&mdash;是的，在谷歌，研究 与开发融为一体，软件工程师与计算机科学家当然也合二为一。计算机这几年算是比较热的，能够得偿所愿挤进计算机科学相关专业的学生都是全中国顶尖的人才， 如果大学四年荒芜学业，未能取到真经，浪费了这一来之不易的学习机会，是不是太可惜了呢。 <br /><br />遍历一个数组或链表的时间复杂度是多少？对 于这样一个不是问题的问题，竟然各地都有相当数量的学生回答说是 O(logN)！有一次我实在忍不住了，提示应聘的学生说：&quot;你是如何理解'遍历'一词的涵义呢？&quot;他立刻做恍然大悟状，回答说：&quot;哦，对，应该是 O(NlogN)&quot;。我当时失望得一句话都说不出来。类似的例子还有很多。坊间一直传说谷歌的面试题有多难，其实大多数学生都卡在最基础的问题上。少数人 费尽气力要收集谷歌曾经用过的面试题，其实我们大多数题目都来自或改编自经典计算机科学教材的习题。 <br /><br />顺便提一句，上面提到的那个答错的学生乃是一流大学中成绩名列前茅的优秀生，门门功课（包括所有计算机专业课）均在 90 分以上。反差何其大也！<br /><br /><strong>诚信为本</strong><br /><br />我曾服务于多家公司，注意到不同的公司之间，乃至同一公司内不同的面试官之间，对应聘者的要求都会有所差别，或看重潜质，或偏好经验，或强调态度。但有一点大家是共同的：如果应聘者在诚信上有疑问，谁也不敢要。 <br /><br />总 体而言，现在的学生其诚信还是相当不错的，大大超出了我们的期望。然而，不和谐音也还是有的。我们曾在某著名高校进行笔试，就发现有学生严重作弊，他们毁 掉自己的机会不说，整个学校的名声也受到拖累。面试中偶尔也能遇到诚信堪忧的学生。有一次我出了一道题，前来应聘的学生明明以前见过这道题，却告诉我说没 见过，自以为得计，可他那一纵即逝的狡黠一笑哪里逃得过我的眼睛。两点之间直线最短，说真话最简单。 <br /><br /><strong>做最好的自己</strong><br /><br />不 是每一个人都会加入某一家具体的公司，但每一个人都可以成功，成为最好的自己。我这里所谈，既不系统亦欠简练，但确实是我在几次校园招聘之旅中发现的一些 具有共性的问题，故不避好为人师之讥，罗列于此。我当然知道，本文的首要目标读者&mdash;&mdash;大一新生是不听劝的，他们更愿意把所有的错误亲自重犯一遍。话虽如 此，只要能够对一个学生有所启迪，我的时间就不算白费。<br /><br /><br /><hr /><span style="color: #0000ff">转载感想：当然这篇文章并不仅仅适合学生朋友。</span><br /><br/>Tags - <a href="http://www.newtyper.com/blog/tags/google/" rel="tag">google</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25BB%258F%25E9%25AA%258C/" rel="tag">经验</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/117/</link>
<title><![CDATA[Java 获得本月一日、本星期星期一、昨天的date对象的方法]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Thu, 09 Nov 2006 02:11:57 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/117/</guid> 
<description>
<![CDATA[ 
	<div class="code">GregorianCalendar cal = new GregorianCalendar();<br/>Date now = new Date();<br/>cal.setTime(now);<br/>cal.setFirstDayOfWeek(GregorianCalendar.MONDAY);&nbsp;&nbsp; // 设置一个星期的第一天为星期1，默认是星期日<br/><br/>SimpleDateFormat dateutil = new SimpleDateFormat("yyyy-MM-dd");<br/>System.out.println("now=" + dateutil.format(cal.getTime()));&nbsp; // 今天<br/><br/>cal.add(GregorianCalendar.DATE,- 1);<br/>System.out.println("now=" + dateutil.format(cal.getTime())); // 昨天&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/><br/>cal.set(GregorianCalendar.DAY_OF_WEEK,GregorianCalendar.MONDAY);<br/>System.out.println("now=" + dateutil.format(cal.getTime()));&nbsp;&nbsp; // 本周1<br/><br/>cal.set(GregorianCalendar.DAY_OF_MONTH,1); <br/>System.out.println("now=" + dateutil.format(cal.getTime())); // 本月1日在此键入内容</div><br/><br/>Tags - <a href="http://www.newtyper.com/blog/tags/java/" rel="tag">java</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%2597%25A5%25E6%259C%259F/" rel="tag">日期</a> , <a href="http://www.newtyper.com/blog/tags/date/" rel="tag">date</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/115/</link>
<title><![CDATA[趣文：你所使用的浏览器反映出了你的个性]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Tue, 29 Aug 2006 16:16:09 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/115/</guid> 
<description>
<![CDATA[ 
	这是国外一篇很火的文章，作者以调侃的语气分析了各种浏览器用户的特点。<br/><br/>　　我经常根据一个人所使用的浏览器来评价某个人。不管你信不信，你对浏览器的选择往往反映出了你的个性。<br/><br/>IE 5.0:<br/>　　你使用电脑仅仅是为了即时聊天，写写电子邮件和博客。你顽固地拒绝升级你那老旧的WIN98，因为你并不需要太多的功能而且认为WIN98已经工作地很好了。你同时可能不使用任何杀毒软件，你只是每个月让你的儿子，侄子或朋友把把病毒清理干净而已。<br/><br/>IE 6.0:<br/>　　你很可能并不知道什么叫做“浏览器”并且认为IE就是因特网。你对技术没有清晰的概念，而且你通常对电脑感到畏惧。同样的，你使用电脑也仅仅是为了即时聊天，写写电子邮件和博客。也许你的朋友曾不断地向你提及“被炒鱿鱼的狐狸”（Fired Fox），但你一直不明白那到底是什么，也不准备在它上面花时间。<br/><br/>IE 7.0:<br/>　　你认为你站在了技术的最前沿，同时认为微软是地球上最伟大的公司。至于那个邪恶的“Lenoux”操作系统(音同Linux)则是由恐怖分子编写出来的。你在卧室的墙上张贴了斯蒂夫·鲍尔默(微软首席执行官)的海报，并希望自己在未来能成为第二个比尔·盖茨。你一想到“Vista”便会激动地浑身颤抖、坐立不安。<br/><br/>Firefox 1.x:<br/>　　你很可爱而且有点傻里傻气的，并为FireFox感到骄傲。你是开源运动的强烈支持者，你认为理乍得·马修·斯托曼才是“真正的男人”。你其实并不关心FireFox是不是比IE更安全，更快速——你会一直使用FireFox哪怕它的效率比IE低上十倍。你只是因为你得到了一个免费、开源并拥有庞大技术支持社区的浏览器而感到高兴。无论任何时候你都会安装至少7个必不可少的扩展。<br/><br/>Firefox 2.0 Beta:<br/>　　在白天你是个程序员，到了晚上你就成了一个开源软件开发者。要不，你就是一个疯狂的Firefox粉丝。你热衷于上报你遇到的每一个Bug，很可能你已经发布了至少一个开源项目的补丁。你喜欢对程序修修补补，而且丝毫不会在意在自己的电脑上运行beta版软件。毕竟，发现新的Bug和修改最新的软件对你来说充满了乐趣。<br/><br/>Mozilla:<br/>　　从一开始你就在使用Mozilla。你认为FireFox宣传地过了火，相对于FireFox你更愿意去使用旧版的Netscape。你并不认为Mozilla套装(Moz Suite)是个负担——事实上你更喜欢一个集成了邮件客户端、IRC聊天客户端和网页编辑器的浏览器。你很不理解为什么有些人宁愿去挑选一个功能很少的浏览器而不是选择Mozilla。在其他的方便你更像一个Firefox用户——你喜欢开源、你喜欢你的浏览器扩展、等等——或许你会说Firefox用户的口味和你非常相似。总之，你们在使用一个令人钦佩的、功能强大的、gecko内核的浏览器，与此同时很多人仍然在他们的IE浏览器里挣扎。<br/><br/>Opera:<br/>　　你并不关心Firefox之流，你所需要的只是一个世界上最好的浏览器——对你来说那就是Opera，而你很可能早在Opera收费时就购买了它。如果有一个Firefox粉丝对你的浏览器评头论足，你就会打开一个ACID2测试，然后以此来驳倒他。你知道什么是你所需要的（一个快速、支持标准的浏览器），你也明白怎样得到它。你对浏览器大战丝毫不感兴趣，虽然你有一点点希望Firefox获胜，因为如果那样的话会有更少的网页开发者制作只兼容IE的页面。<br/><br/>Netscape 8.x:<br/>　　你是一个刚刚得到一台新电脑的老资历网民，虽然你对互联网知道的并不多，但你却清楚地记得你需要Netscape去使用它。你并不明白人们谈到的IE和那个叫Fire什么的东西到底是什么，而且搞不清楚奥普拉·温弗瑞(Oprah,一个脱口秀主持人，音同Opera)和因特网有什么关系，你所知道的就是点开那个大大的“N”，然后变成“在线”。你认为史蒂文的关于网络的演讲很有道理。<br/><br/>Netscape 7 和更老的版本：<br/>　　参见IE 5.0。<br/><br/>AOL Explorer:<br/>　　曾经有一天你安装了最新的AIM客户端，然后这个东西就成了你的默认浏览器。你非常讨厌它，但你却不知道怎样才能把它变回去。你甚至不知道你怎样才能向你的那些电脑高手朋友们描述这个问题，当你想得到帮助时你也许会像这样提问：“你能把这个新的网络，呃，变回原来的那个旧网络吗？”他们只会瞪着你，然后装作不明白你在说什么。他们或许并不像他们自己所说的那样了解电脑。<br/><br/>AOL Suite:<br/>　　你很可能仍然在使用AOL的拨号网络，不然的话，你就是觉得在你使用宽带网络之后仍然需要AOL。有人告诉过你其实你上网是不需要用AOL拨号的，但你无法想象这是怎么一回事。这看起来很难做到，而且似乎是非法的。<br/><br/>Safari:<br/>　　恭喜你！你是一个Mac用户并享受着那个名字给你带来的好处和好心情。你喜欢OSX，并且永远不会使用Windows。Windows对你来说实在是太过丑陋和低效，你更喜欢Mac的简洁和清晰，而Safari就是一个为你工作的浏览器。你从不会烦心去寻找另一个浏览器，因为你对你现在拥有的一切已经非常满意，你也不会因为世界而改变它。<br/><br/>Konqueror:<br/>　　你是一个 linux用户，并且打心底就是个极客(?)。你认为KDE是最好的桌面环境，并且因此而鄙视Gnome。你喜欢一个同时是文件管理器、ftp/scp客户端、smb分享客户端、PDF文档查看器和其它很多东西的浏览器。你喜欢向你的朋友炫耀KDE的网络透明度，你仅仅通过浏览器在你的网页服务器上编辑一个HTML文件，保存它，然后又在浏览器里重新载入修改后的文件。你日常使用的绝大多数软件都以K字大头（Kmail, Kontact, Kdevelop, Koffice 等等）<br/><br/>Lynx:<br/>　　你肯定是个骗子，你真的想让我相信你使用一个文字浏览器来浏览所有网页？尤其是一个不支持javascript, frames, css甚至连tables显示都有问题的浏览器?说真的，我可以相信你一直使用VI（一个编辑器），用Mutt或Pine做你的主要邮件客户端，但你不可能让我相信你使用lynx作你的主浏览器。如果你真的做到了，那么你就是我一生中见到过的最最执着的极客了。向你脱帽致敬！<br/><br/>　　如果你不同意上面的话，请留言好让我知道。如果你被我不幸言中，那么请停止使用那个该死的浏览器并换一个真正的浏览器吧。也请你自由地给我漏掉的浏览器作简短的描述。<br/>　　免责声明：我不清楚是谁制作的那个FireFox图像（就是本文开头的那个），有个人在留言本中曾使用它作头像。我向那个作者致以崇高的敬意，如果我能找到他的话。<br/><br/>　　谢谢你们所有的评论，让我们开公布诚吧——我并没有说Lynx是一个差劲的浏览器，事实上我在很多不同的方面都经常使用它。我只是怀疑是否有人把它作为主浏览器。如果你是的话，向你脱帽致敬！你比我执着多了。<br/>　　现在我补充一些漏掉的比较流行的浏览器：<br/><br/>Flock:<br/>　　他们也许会称你为Web 2.0先生。你所使用浏览器表明一点：你的足迹遍布flickr, del.icio.us, youtube和其它一打的网站。你认为Firefox还不错，但它并不不能让你在弹指间就能完成写博客、照片共享、标签和网络书签等等功能。你希望在你的脑袋里植入一块芯片，这样你就能一直连接到网络，而且能使用24/7移动博客。当一些目光短浅的人告诉你Flock只不过是Firefox的修改版时，你会赶走他们并说他们不能以更宽广的视野看东西。<br/><br/>Epiphany:<br/>　　你是一个Gnome用户并为之自豪。你认为KDE简直是地狱里出来折磨人的东西，并且热衷于向人们解释KDE必须经过几个小时的修改才能使用，至于那些说 KDE马上就能用的人则是可耻的骗子。你希望所有东西能更加简单和直观——那就是你为什么选择了Gnome，这同样也是你使用Epiphany的原因。你试过Mozzila和Firefox，但你发现它们实在是臃肿、丑陋和麻烦。你的桌面就像你的书桌一样整齐有序。<br/><br/>Maxthon and Avant:<br/>　　你也许有些疑惑，虽然你喜欢IE并且不会换用别的浏览器，也不会担心网站会出现渲染不正常的错误，更不会担心它像其它内核浏览器那样不支持 ActiveX控件，但你心底还是羡慕那些使用可以做到标签页浏览和其它很酷的功能的浏览器的朋友。你承认IE有点落后于时代，而你想要一些更加现代的东西，同时也不想放弃正常显示一些网站。Maxhton/Avant让你拥有了世界上最好的两项功能——舒适温暖的IE渲染引擎和其它浏览器里非常酷的功能。当IE7发布正式版时你就会换用IE7。<br/><br/>Sea Monkey:<br/>　　你很喜欢简单的软件套装，对你来说把浏览器和电子邮件客户端分开是不可理喻的。你以前习惯于使用Mozilla，但Sea Monkey发布后你很快便换了口味并不再回头。你认为“Sea Monkey”是浏览器中有史以来最酷的名字。<br/><br/>w3m:<br/>　　你一生的大部分时间都在当系统管理员。你很少看见阳光，因为你一天中大部分时间花在大型服务器的周围。如果周围没有电脑风扇的“嗡嗡”声你便无法入睡。哪怕是在夏天你每天也不得不穿一件暖和的夹克，因为服务器机房里的冷气开的是如此之高，如果你不加以注意的话便很容易感冒。年轻的极客们都向你看其，并试图模仿你——而你一直也不知道这是为什么。<br/><br/>K-Meleon:<br/>　　你对长时间等待浏览器启动感到很不耐烦，甚至IE的启动速度对你来说也太慢了点。这也是为什么你的浏览器会预读取页面，然后仅仅花费十亿分之一秒去载入页面。你的生活节奏非常快，根本没有时间去等待浏览器慢慢启动。你可以花费几个小时去设置Windows注册表来提升程序的响应速度、载入时间，并减少所有程序的超时时间。<br/><br/>Dillo:<br/>　　你从心底就是一个喜欢低资源占用的人，你喜欢让你的程序更加小巧和快速。你最喜欢运行IceWM或Windowmaker，同时嘲笑那些臃肿的桌面环境像KDE或Gnome。你以本地Linux/BSD guru著称。<br/><br/><br/>译文链接：<a href="http://my.opera.com/z8519312/blog/show.dml/423938" target="_blank">http://my.opera.com/z85193...</a><br/>原文链接：<a href="http://www.terminally-incoherent.com/blog/2006/08/19/what-does-your-browser-reveal-about-your-personality/" target="_blank">http://www.terminally-inco...</a><br/>&nbsp;&nbsp;<br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E6%25B5%258F%25E8%25A7%2588%25E5%2599%25A8/" rel="tag">浏览器</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/113/</link>
<title><![CDATA[整理一下Firefox小技术]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Wed, 16 Aug 2006 02:43:59 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/113/</guid> 
<description>
<![CDATA[ 
	本文收集至网上<br/><br/>请问如何同时使用两个版本的Firefox？<br/><br/>你可以用如下方式来启动各自的Firefox，让他们有不同的配置：<br/><br/>firefox.exe -profile "d:&#92;firefox1.0Profile"<br/>firefox.exe -profile "e:&#92;firefox0.9Profile"<br/><br/><br/>Firefox的设置以及扩展和书签是保存在什么地方的？<br/><br/>怎样让Firefox使用多配置文件?<br/><br/><br/>那里能够下载到Firefox1.0的源代码？<br/>Firefox1.0的源代码可以在如下链接下载： <a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/1.0/source/firefox-1.0-source.tar.bz2" target="_blank">http://ftp.mozilla.org/pub...</a><br/><br/>Firefox设置问题<br/>我可以自定义工具栏吗？<br/>当然可以！您可以自己定义工具栏来选择哪些显示哪些按键， 在工具栏上点击右键选择定制或者点击查看 > 工具栏 > 定制...，一个新的窗口会弹出来，其中有所有可用的工具栏的选项。您只要把它们拖到您希望放置到的工具栏的相应位置就行了。如果是删去某个按键，从工具栏上把这个键拖到自定义窗口里就行了。您甚至可以把工具栏的按键放到菜单上的。就是说，您可以把所有的东西放到一行上来节省更多的屏幕空间用来浏览网页。<br/><br/><br/>Firefox的设置以及扩展和书签是保存在什么地方的？<br/><br/>Firefox 的插件是安装在Windows系统用户配置目录里面的，通常这个目录叫：“C:&#92;Documents and Settings&#92;Administrator&#92;Application Data&#92;Mozilla&#92;Firefox&#92;Profiles&#92;bnq1y0aa.默认用户” （这个目录有可能因为你Windows的安装设置不同而不同）如果你重装系统前备份这个目录，重装系统后就不用重新安装插件了，另外也有专门保存扩展和设置的插件可供选择。<br/><br/><br/><br/>如果你看不到Application Data这个目录可以通过设置资源管理器文件夹选项中显示隐藏及系统目录来实现。<br/><br/><br/><br/>如果你要删除所有的配置和设置，那么删除上面那个文件夹即可。<br/><br/>附：书签保存在bookmarks.html这个文件中。<br/><br/><br/>怎样安装XPI扩展文件？<br/><br/>在Firefox 的菜单中选择 文件-打开文件 然后选择你要安装的XPI扩展插件文件。稍后就可以看到Firefox会询问你是否要安装这个插件,点击“是”即可,注意新安装的插件必须在重启FireFox后才能生效（关闭所有的Firefox窗口，包括扩展，主题等窗口）。<br/><br/><br/>如果要对扩展进行设置，你可在菜单——工具——扩展中选择你想设置的扩展后点击下方的“选项”来进行设置（不是所有扩展都提供设置选项）。<br/><br/><br/>怎样安装 Jar 主题文件？<br/>有一些主题下载后是 .jar 包压缩的，如果要安装，先打开 Firefox ，然后打开 Tools - Themes 主题管理窗口，然后再把 .jar 文件直接拖放到这个窗口，就会开始安装。安装完成后，重启 Firefox 即可。同样的，你想要切换主题的话，也可以在 Theme 窗口中选择你需要的主题，然后重启 Firefox 即可。<br/><br/>怎样让Firefox使用多配置文件?<br/>在Firefox启动的时候后面加上 /p 参数，你就可以打开Firefox的配置文件管理器，你能够在这里添加删除并且选择你的配置文件。这样你就可以使用不同的配置来 进行冲浪了。<br/>注意，不同的配置文件里面插件和主题也是独立的。也就是说，你可以在两个不同的配置文件里安装两套不同的插件。<br/>在缺省安装的情况下，在Windows的开始-运行对话框里输入下面的命令就可以了。你可以根据你的Firefox的安装位 置的不同来进行更改：<br/>"C:&#92;Program Files&#92;Mozilla Firefox&#92;firefox.exe " /p<br/><br/>如何自定义Firefox的临时文件存放地址？<br/><br/>打开Firefox地址栏输入about:config 后回车<br/><br/>就会出现Firefox的详细配置页面。用右键新建一个字符串名为browser.cache.disk.parent_directory 然后输入新的临时文件的路径即可。例如： D:&#92;Temp Files<br/><br/><br/>如果使用的是WINDOWS,请使用双斜杠，如D:&#92;&#92;Temp&#92;&#92;Firefox Temp<br/><br/><br/>about:config是什么东西？<br/><br/>如何让Firefox支持Java?<br/>通常，Firefox会自动调用你系统内的Java虚拟机，但是你如果要手动安装的话，Windows用户在这里安装，Linux用户在这里安装。<br/><br/>这里安装<br/>这里安装<br/><br/>怎么隐藏书签菜单里的书签工具栏？<br/>可以这样：首先找到安装目录：firefox&#92;defaults&#92;profile&#92;chrome&#92; ,此文件夹之内有两个css文件，将userChrome-sample.css文件拷贝至c:&#92;documents and settings&#92;用户名&#92;appliactiondata&#92;mozila&#92;firefox&#92;Profiles&#92;de fault&#92;chrome&#92;下，并将 文件名中的-sample去掉,变更为userChrome.css，然后编辑这个文件 ，加入以下内容： .bookmark-item[label="Bookmarks Toolbar Folder"] &#123; display: none !important; &#125; 保存，重启firefox<br/><br/>有破解网页鼠标右键限制的方法吗？<br/><br/>FireFox本身就有这个选项。<br/><br/>在选项->网页特性->启用JavaScript->高级->禁用或者替换上下文菜单取消前面的打勾就可以了。<br/><br/><br/>如何取消整合搜索条里面没用的条目？<br/>在Firefox安装目录的searchplugins目录下面将多余的搜索引擎条目删除后重启Firefox即可。<br/><br/><br/>如何加快Firefox的启动速度？<br/><br/>卸载掉不用的扩展与插件。 取消检测默认浏览器（工具—选项—基本信息——默认浏览器）<br/>取消启动时检测更新（工具—选项—高级—软件更新—Firefox和我的扩展）<br/>也可以参考一下这里<br/><br/><a href="http://www.firefox.net.cn/forum_posts.asp?TID=368&KW=%C6" target="_blank">http://www.firefox.net.cn/...</a> %F4%B6%AF<br/><a href="http://firefox.net.cn/forum_posts.asp?TID=243&PN=1" target="_blank">http://firefox.net.cn/foru...</a><br/><a href="http://www.firefox.net.cn/forum_posts.asp?TID=432&KW=%C6" target="_blank">http://www.firefox.net.cn/...</a> %F4%B6%AF<br/>另外不少地方推出的优化版本能够明显加快启动速度，但是普通用户不推荐安装。<br/><br/>在中文版中怎么修改，才能在使用整合搜索器时直接打开搜索页面呢？<br/>请参考:<br/><a href="http://www.firefox.net.cn/faq/index.php?sid=345?=zh&action=artikel&cat=3&id=3&artlang=zh" target="_blank">http://www.firefox.net.cn/...</a><br/><br/><br/>在中文版中怎么修改，才能在使用整合搜索器时直接打开搜索页面呢？<br/>请参考:<br/><a href="http://www.firefox.net.cn/faq/index.php?sid=345?=zh&action=artikel&cat=3&id=3&artlang=zh" target="_blank">http://www.firefox.net.cn/...</a><br/><br/><br/>about:config是什么东西？<br/><br/>about:config是Firefox的设置页面，Firefox提供了不少高级设置选项在这里以便让你可以更加详细地控制Firefox的运行方式。<br/><br/>但是Firefox官方不推荐用户手工修改about:config的设置。所以，如果你对于你想修改的内容不是非常确定的话，请不要去改变它。<br/><br/>打开Firefox中about:config设置的方法是在Firefox地址栏输入 about:config,然后回车。<br/><br/>这里是中文的about:config说明：<br/><br/><a href="http://www.firefox.net.cn/wiki/pmwiki.php?pagename=Firefox.AboutConfig" target="_blank">http://www.firefox.net.cn/...</a><br/><br/>这里是英文的about:config说明：<a href="http://preferential.mozdev.org/preferences.html" target="_blank">http://preferential.mozdev...</a><br/><br/>怎样定制Firefox的工具栏？<br/>点击Firefox菜单的查看——工具栏——定制，就会在工具栏下方出现一个包含很多定制按钮的窗口，你可以用鼠标拖放这些按钮到你希望它在的位置（你甚至可以把它放到菜单条中），如果你希望隐藏某些按钮，把这些按钮从工具栏上拖到按钮列表窗口中即可。 另外，在工具栏上直接点击右键，也会出现定制选项。<br/><br/><br/>某些扩展安装了怎么一点反应都没有？<br/><br/>并不是所有的扩展都会提供明显的用户界面的改变，有的扩展只是在后台悄悄地做他该做的事情，有的扩展提供工具栏按钮，但是需要你自己将这些按钮放到工具栏上去。有的扩展在使用前需要进行设置（工具——扩展——你要设置的扩展——选项）<br/><br/>打开网页出错时怎样让Firefox显示错误页面而不是弹出对话框？<br/><br/>由于网络或者服务器问题导致网页不能打开的时候，Firefox通常会弹出一个对话框，如果你觉得这样不符合你的浏览习惯的话，你可以在about:config中修改<br/><br/>browser.xul.error_pages.enabled 的值为 True<br/><br/>这样在遇到同样问题的时候，Firefox就会显示错误页面，而不是对话框了。<br/><br/>怎样卸载ActiveX插件?(含详细的步骤)<br/><br/>英文原文如下,大致的意思就是删除下面四个文件即可：<br/><br/>Filefox安装目录&#92;plugins&#92;npmozax.dll<br/><br/>Filefox安装目录&#92;components&#92;nsIMozAxPlugin.xpt<br/><br/>Filefox安装目录&#92;components&#92;nsAxSecurityPolicy.js<br/><br/>Filefox安装目录&#92;defaults&#92;pref&#92;activex.js<br/><br/>以下为原文：<br/><br/>Uninstalling ActiveX<br/><br/>Should you want to uninstall AxtiveX, the process is simple - only four files need to be manually deleted from the Firefox program folder.<br/><br/>Step 1. Go to the Firefox program folder (the location you installed Firefox to). The default location in Windows is 'C:&#92;Program Files&#92;Mozilla Firefox&#92;'<br/><br/>Step 2. Go into the plugins folder and delete the file 'npmozax.dll'<br/><br/>Step 3. Go back to the Firefox program folder, and then go into the components folder, and delete the files 'nsIMozAxPlugin.xpt' & 'nsAxSecurityPolicy.js'.<br/><br/>Step 4. Go back to the Firefox program folder, and then go into the defaults&#92;pref folder, and delete the file 'activex.js'.<br/><br/><br/>Firefox地址栏中的命令的功能。<br/><br/>下面内容仅作为学习目的摘编自WINDOWS SECRETS的文章：Secrets of Firefox 1.0 。<br/><br/>地址栏中的命令的功能：<br/><br/>* about: 显示Firefox的版本号，著作权声明等等。注意，包括冒号部分，否则会跑到<a href="http://www.about.com" target="_blank">www.about.com</a>去。<br/><br/>* about:config 显示Firefox的控制台（面），列出了所有可供定制的设置项，自己动手捣鼓下应当很有意思。<br/><br/>* about:cache 显示内存和文件缓存的摘要，并提供链接指向更详细信息的文件。<br/><br/>* about:buildconfig 列出那些用于编译所用Firefox的设置项。<br/><br/>* aboutlugins 列出安装的插件。<br/><br/>* about:credits 呵呵，一个彩蛋。<br/><br/>* about:mozilla The Book of Mozilla<br/><br/><br/>为什么我已经设了Firefox为默认浏览器，但是总是是有问题？<br/><br/>如果你之前用过maxthon/gb等浏览器并将其设置为默认浏览器。那么要先去maxthon/gb的设置那里改ie为默认浏览器，然后再去firefox里面设置firefox为默认浏览器即可。<br/><br/>虽然设置FireFox为默认浏览器，但是对于HTTP/HTTPS/FTP之类链接，注册表中已经添加/更新了与FireFox关联的open动作但并未设为默认值，如果以前设置了其他浏览器为默认浏览器，比如GreenBrowser原来的默认动作又不是open，则当从外部点击链接时，仍然不会用ff 打开，这种情况设置open为默认动作即可（资源管理 器—工具—文件夹选项—文件类型—URL:超文本传送协议等—高级）。<br/><br/>如何改变Firefox自带整合搜索条的宽度？<br/><br/>你可以通过安装这个扩展来调整整合搜索栏的宽度：Resize Search Box<br/><br/><br/>另外如果你对Firefox比较熟悉的话，你也可以修改userChrome.css 文件来修改搜索栏的长度，添加如下代码到userChrome.css中即可：<br/><br/>/* Make the Search box flex wider */<br/>#search-container &#123;<br/>-moz-box-flex: 400 !important;<br/>&#125;<br/><br/>其中红色的数字便是整合搜索框的宽度值，你可以按照你自己的使用习惯随意进行调整。调整后，重新启动Firefox即可看见更改(默认主题下测试通过)。<br/><br/><br/>Firefox可以定制标签栏的位置吗？<br/><br/>Firefox中缺省标签栏是处于上方的，你可以通过TBE或者TBP扩展来随意定制标签栏的位置。<br/><br/>另外，如果你对Firefox比较熟悉的话，你也可以编辑userChrome.css配置文件来修改标签栏的位置，在该文件中加入如下代码，位置不限：<br/><br/>把标签页放在下面：<br/>/* Display the Tabbar at the bottom */<br/>#content > tabbox &#123;-moz-box-direction: reverse;&#125;<br/><br/><br/>把标签页放在左边：<br/>/* tabs at left */<br/>#content > tabbox &#123;<br/>-moz-box-orient: horizontal;<br/>&#125;<br/>.tabbrowser-strip &#123;<br/>-moz-box-orient: vertical;<br/>/* note: you can set this to -moz-scrollbars-vertical instead,<br/>but then the scrollbar will *always* be visible. this way<br/>there is never a scrollbar, so it behaves like the tab bar<br/>normally does */<br/>overflow: -moz-scrollbars-none;<br/>&#125;<br/>.tabbrowser-tabs &#123;<br/>-moz-box-orient: horizontal;<br/>min-width: 10ex; /* you may want to increase this value */<br/>-mox-box-pack: start;<br/>-moz-box-align: start;<br/>&#125;<br/>.tabbrowser-tabs > hbox &#123;<br/>-moz-box-orient: vertical;<br/>-moz-box-align: stretch;<br/>-moz-box-pack: start;<br/>&#125;<br/>.tabbrowser-tabs > hbox > tab &#123;<br/>-moz-box-align: start;<br/>-moz-box-orient: horizontal;<br/>&#125;<br/>/* remove the close-tab button. trust me, you need to do this. */<br/>.tabbrowser-tabs > stack &#123;<br/>display: none;<br/>&#125;<br/><br/><br/>把标签页放在右边：<br/>上面两段代码一起用。<br/><br/>推荐使用ChromEdit扩展来编辑配置文件。<br/><br/><br/>如何在Firefox中设置多个主页?<br/><br/>你可以先打开你想添加的所有的主页，然后在 工具—选项—基本信息—主页 中，选择"使用当前打开的多个页面"。<br/><br/>你也可以将不同的主页地址用管道符号隔开'&#124;'，输入在主页地址中，<br/>例如:<br/><a href="http://forums.mozillazine.org/&#124;http://www.google.com&#124;http://www.yahoo.com" target="_blank">http://forums.mozillazine....</a><br/><br/>还有一种方法：同样的菜单位置，使用书签。选择一个书签组，这个书签组中的所有标签就会在你启动FF时打开。（你可以为此建立一个启动书签组）<br/><br/><br/>在工具栏定制中添加新工具栏怎么没有变化？<br/><br/>在Firefox中，要在新建的工具条中加入内容才认为这个新加的工具栏有效。要建立一个空白的工具条，你可以放一个可调整空白上去。<br/><br/><br/>换句话说，删除自定义工具栏中所有的内容就等于删除了这个工具栏。<br/><br/><br/>Firefox代理设置排除域的通配符怎么设置？<br/><br/>Firefox设置代理时，需要排除的IP域不需要任何通配符来设置。<br/><br/>只需要在地址之间用逗号分开就可以了。<br/>例如<br/>10.,127.0.0.1.localhost,219.1.1.<br/>就把 10.* 开头的地址、219.1.1开头的地址以及本地全部排除不使用代理了（请注意以上例子的逗号和IP中的点号的差别）。<br/><br/><br/><br/>Firefox使用问题<br/>在Firefox里如何清空地址栏历史?<br/><br/>菜单Tools——Options——Privacy:History--Clear<br/>也可以设定保存多长时间，如果是0，就不保存，退出浏览器就没了。by mugedy<br/><br/><br/>或者点击地址栏左边的下拉按钮，打开地址下拉框，鼠标移到某个地址上，然后一直按住shift+del，马上就over了（应该也不算太麻烦吧）,这个办法也可以选择删除想删除的历史记录。by yuchong11<br/><br/><br/>Firefox怎样实现网址简写的功能？<br/>首先将网址添加到Bookmarks中，然后进入Bookmarks Manager，选中你要实现简写的网址后点击右键，选择Properties，然后在弹出窗口中的Keyword栏目中输入你想要的简写，这样你就可以直接在地址栏里输入你的简写来打开指定的网址了。<br/><br/><br/>为何我在整合搜索栏里进行Google搜索出来的页面却是Google的首页？<br/><br/>打开about:config<br/>清空<br/>browser.search.param.Google.1.custom<br/>browser.search.param.Google.1.default<br/><br/>下的所有设定值。<br/>即可正常使用搜索框。<br/><br/><br/>Firefox打开中文URL的方法<br/>近来很多朋友提出Firefox不能访问中文URL的问题，反映Firefox总是把中文解释成E#z这样的乱码形式。这似乎是Firefox在使用UTF-8编码发送URL的问题，但是我看了看设置，并没有修改这点的选项，于是到about :config里面找了找，发现了这样一个设置项： network.standard-url.escape-utf8 其值缺省为True,将其改为False后，问题解决！<br/><br/>打开某些中文网站全部是乱码怎么办？<br/>刚安装完Firefox后，如果遇到浏览某些中文网站出现乱码，你则可以这样设置：Tools——Options——General——Languages 设置Languages为Chinese/China [zh-cn] 设置Default Character Encoding 为 Chinese Simplified (GB2312) 这样便能正常查看中文网站了<br/><br/>如何让Firefox支持Flash?<br/><br/>当你在浏览一个含有Flash的网页的时候，Firefox会提醒你正常浏览网页需要安装缺少的插件（这个提醒通常在地址栏下方和Flash的占位区上），按照他的提示安装Flash插件即可。<br/><br/>另外你也可以手动安装，在Windows下, 可以在这里安装。 对于Linux用户，你 这里安装 可以在<br/>这里安装<br/>下载这个文件，然后完全关闭Mozilla Firefox（所有的窗口都必须关闭，包括插件主题等），然后再运行安装文件。在出现的对话框时里，点击Other browser...按钮。在这里选择Mozilla Firefox的插件文件夹。您必需知道您安装Mozilla Firefox的文件夹。在那个文件夹里，有个子文件夹名字是"plugins"（Windows下通常是X:&#92;Program Files&#92;Mozilla Firefox&#92;plugins）。 选择这个文件夹，然后点击Select按钮，最后点击Install即可。<br/><br/><br/>为什么我用Firefox对某些网站都不能正常浏览？<br/>非常遗憾，现在的某些不负责任的网站开发人员都没有遵循W3C标准，而是采用了某些IE独家支持的非标准特性，对于这些网站，我们的建议是不去浏览，但是如果你确实需要浏览，那就装一个IEView救救急吧。<br/><br/>Firefox怎样使用FlashGet等下载工具下载？<br/>Firefox 自身提供了强大的下载管理器，支持续传，在管理器中直接打开下载文件等功能，但是有的朋友还是希望能够使用FlashGet等专业下载软件， Firefox有许多扩展插件能够完成这样的功能，我向你推荐FlashGot，安装后会扩展你的右键菜单，你在下载链接上直接点右键便可以选择用FlashGet下载了。你可以到这里寻找下载地址和相关信息。这个扩展还能支持其他很多种下<br/>这里 载工具。<br/><br/>补充Flashgot插件在Windows环境下如果你的用户名为中文的话，可能将不会正常工作，这种情况推荐你使用Launchy或DownloadWith等其他的下载扩展。<br/><br/><br/>某些网页中文字很小而且难看？<br/>在Friefox菜单中选择 工具-选项-基本信息-字体和颜色-语言编码选择简体中文，最小字体设置成12，确定后再看看网页上的文字就会不一样了。如果你只是临时需要改变字体大小，可以用ctrl+"="，"-" 实时放大缩小字体。<br/><br/>Firefox的快捷键列表，以及和IE快捷键的对照<br/><br/>下面这个网址提供了这样一张对照表：<br/><br/><a href="http://people.linux.net.cn/holywen/onlinehelp/firefox/keyboard-cn.html" target="_blank">http://people.linux.net.cn...</a><br/><br/>在网页里点一下怎么会出来一个输入时才会有的光标？<br/>有可能你打开了键盘浏览模式，这个模式允许你使用键盘来选择网页上面的内容，如果你不需要这样的功能的话，可以用F7来关闭<br/><br/>使用Firefox怎样进行网页内容搜索？<br/><br/>编辑-在当前页查找（CTRL+F）<br/><br/>通常在Firefox窗口下方就会出现查找工具条，你可以输入关键字来查找。也可以点击高亮显示，来显示当前网页上所有匹配的关键字。<br/><br/>Firefox能够和IE互相同步书签（收藏夹）吗？<br/><br/>当然可以！<br/><br/>IE——Firefox：选择Firefox菜单—文件—导入即可。会在Firefox书签中新增一个文件夹放置来自IE导入的收藏夹，不会覆盖原Firefox的书签。（在Firefox的书签管理器中也可以做同样的操作。）<br/><br/>Firefox——IE：有这样一个扩展Favorites Converter(Booktofav)他能够同步你在Firefox中对书签的操作到IE中去，你在Firefox中添加的书签，在IE中也能看到了。你可以在<a href="http://www.firefox.net.cn/wiki/pmwiki.php?pagename=Firefox.FirefoxPlugins" target="_blank">http://www.firefox.net.cn/...</a>找到这个扩展。<br/><br/>Firefox如何清除地址栏浏览历史？<br/>你可以在Firefox菜单-工具-选项-安全中清除所有历史纪录。 如果你只想清除某条历史纪录，你可以点击地址栏最右端的下拉箭头，调出历史纪录清单，然后选中你想删除的历史纪录然后用Shift+Delete清除<br/><br/>Firefox可以实现实现地址栏直接搜索的功能吗？<br/>Firefox不需要扩展，利用其提供的书签定制关键字功能就可以实现类似Oprea的地址栏快速搜索。<br/><br/>在Firefox里，可以给每一个书签定义一个定制关键字（书签—书签管理器—书签属性—定制关键字），指定了关键字后你便可以直接在地址栏输入这个关键字来打开书签指向的页面。利用其中的%s参数，我们也可以来定制地址栏快速搜索（以Google举例） 。<br/><br/><br/><br/>1.将Google添加到书签。<br/>2.进入书签管理器，设置google的定制关键字为 g (你可以按照你的使用习惯来设置) 。<br/>3.在书签管理器中将Google的网址设置为如下：<br/><br/><a href="http://www.google.com/search?hl=zh-CN&newwindow=1&q=%s&btnG=%E6%90%9C%E7%B4%A2&lr=" target="_blank">http://www.google.com/sear...</a><br/><br/>4.确定存储后回到浏览器，在地址栏中输入 g “你要搜索的关键字” 后回车，看看出现了什么？<br/><br/>怎么样让Firefox在打开时能够自动载入上次关闭时打开的网页?<br/>有这样一个扩展Session Saver，能够达到这样的功能 这里是扩展作者的下载页面链接： <a href="http://www.pikey.me.uk/mozilla/#ss" target="_blank">http://www.pikey.me.uk/moz...</a><br/><br/><br/><br/>Firefox扩展问题<br/>安装扩展的时候遇到版本错误怎么办？<br/>在地址栏输入：about:config 打开Firefox详细配置页面，找到: app.extentions.version 后面数字就是版本号，改成扩展所支持的版本号,然后装扩展即可。<br/><br/>另外也可以按照如下方法修改扩展的版本要求<br/>用ZIP或者RAR打开XPI扩展安装文件，找到里面的Install.rdf文件。 打开文件找到这样的地方： <em:targetApplication><br/><Description><br/><em:id>&#123;ec8030f7-c20a-464f-9b0e-13a3a9e97384&#125;</em:i d><br/><em:minVersion>0.9</em:minVersion><br/><em:maxVersion>0.10</em:maxVersion><br/></Description><br/></em:targetApplication><br/>上面红色地方就是允许安装的最大版本。改成Firefox当前的版本号。 然后按照原样打包，安装时便不会提示版本错误了。<br/>（这样做，并不保证所有扩展都可以正常运行）<br/><br/><br/>扩展多了会影响浏览速度吗?<br/>占系统资源是一定的，但不会很多的而且不会影响浏览速度，因为大部分扩展不是针对网络的，而是针对浏览器的。<br/><br/>有能够自定义菜单栏的扩展吗?<br/>是的Compact menu这个扩展,可以让你控制菜单栏的显示。你可以去Firefox中文Wiki里面查<br/>Firefox中文Wiki 询这个扩展。<br/><br/>我使用了TBE扩展，请问怎样才能调整Tab的宽度呢？<br/>Tab——Appearance——Tab——Width of Tabs 这里有很多种方法可以让你自定义Tab条的宽度和显示方式。<br/><br/>怎样为Launchy扩展添加支持的程序？<br/>主要是在launchy.xml里面添加这样的声明（如果没有这个文件就要安装上面说的格式来新建一个）:<br/><br/><application><br/><label>WordPad</label> 外部应用的名称<br/><type>7</type> 外部应用的类型<br/><command>%ProgramFiles%&#92;accessories&#92;wordpad.exe</command> 外部应用的地址<br/><arguments></arguments> 需要传递的参数<br/></application><br/>例如，我将我的LeapFTP加入到链接中，使用了这样的描述：<br/>代码:<br/><br/><?xml version="1.0" encoding="UTF-8"?><br/><configurations xmlns="http://launchy.mozdev.org/configurations"><br/><application><br/><label>LeapFtp</label><br/><type>4</type><br/><command>C:&#92;Program Files&#92;LeapFTP&#92;leapftp.exe</command><br/><arguments></arguments><br/></application><br/></configurations><br/><br/><br/><br/><br/><br/>将其命名为launchy.xml保存到C:&#92;Documents and Settings&#92;Administrator&#92;Application Data&#92;Mozilla&#92;Firefox&#92;Profiles&#92;lqc57ddg.default&#92;chrome&#92;<br/><br/>重新启动Firefox就OK了<br/><br/><br/>官方网站这样说的<br/>launchy.xml file usage<br/><br/>Launchy is now (from version 2.5.0 and up) able to use a XML file to add custom applications. So you can add your own applications to Launchy's context menu. At startup Launchy will look for a file called launchy.xml in the chrome directory in your profile. Get help in finding the path of your Mozilla profile.<br/><br/>You can use this page to create a launchy.xml file. The content of the launchy.xml should look like this:<br/>代码:<br/><br/><?xml version="1.0" encoding="UTF-8"?><br/><configurations xmlns="http://launchy.mozdev.org/configurations"><br/><application><br/><label>Mozilla Composer</label><br/><type>7</type><br/><command>c:&#92;program files&#92;mozilla.org&#92;nightly&#92;mozilla&#92;mozilla.exe</command><br/><arguments>-editor</arguments><br/></application><br/><application><br/><label>WordPad</label><br/><type>7</type><br/><command>%ProgramFiles%&#92;accessories&#92;wordpad.exe</command><br/><arguments></arguments><br/></application><br/><application><br/><label>Lynx</label><br/><type>1</type><br/><command>/usr/bin/lynx</command><br/><arguments></arguments><br/></application><br/><application><br/><label>Kget</label><br/><type>5</type><br/><command>/usr/bin/kget</command><br/><arguments></arguments><br/></application><br/></configurations><br/><br/><br/>label: The name of the application required<br/>command: The full path to the executable for the application required<br/>arguments: The arguments for the application optional<br/>type: The type of application required. The following types are supported:<br/><br/>1. Browsers (fx Mozilla Firefox)<br/>2. Mail clients (fx Mozilla Thunderbird)<br/>3. Media clients (fx Windows Media Player)<br/>4. FTP clients (fx WS_FTP)<br/>5. Download Managers (fx FlashGet)<br/>6. File Explorers (fx Windows Explorer)<br/>7. Editors (fx UltraEdit)<br/>8. View Viewers (fx XnView)<br/><br/>You can use both %ProgramFiles% and %SystemRoot% and %HOMEDRIVE% and %HOMEPATH% in the the command and arguments and they are substitutes with their appropriated values.<br/><br/>Adblock的规则支持白名单，支持正则表达式吗？<br/>Adblock可以使用正则表达式，远比其他浏览器自带的拦截工具要强很多。<br/>关于在Adblock中使用正则表达式，你可以参考：<a href="http://www.firefox.net.cn/newforum/viewtopic.php?t=105&start=0&postdays=0&postorder=asc&highlight=" target="_blank">http://www.firefox.net.cn/...</a><br/><br/>对正则表达式感兴趣的可以看看这个地方，介绍得很详细：<br/><a href="http://oo8h.51.net/docs/regular_expression.htm" target="_blank">http://oo8h.51.net/docs/re...</a><br/><br/><br/>注意，在Adblock里面必须给正则表达式前后加上“/”，Adblock才会认为是一条正则表达式<br/><br/><br/>firefox是否支持鼠标拖放打开链接的功能？<br/><br/>你可以安装SuperDragAndGo扩展，这个扩展能够将你拖动的链接在新Tab里面打开，还可以设置拖动一张图片就保存它。<br/><br/>为什么FoxyTunes控制不了foobar?<br/>foobar首先设置成默认界面，然后在Foxytunes菜单里设置Player——Options为Generic Multimedia Player2，这个时候再试试，你就能够在Firefox里面控制foobar了<br/><br/>为什么在另存网页时，存储文件类型仅有HTML方式？<br/>你可以安装一个扩展名字叫做MAF，它可以让你用更多的方式来保存网页。在下列地址可以安装： <a href="http://maf.mozdev.org/installation.html" target="_blank">http://maf.mozdev.org/inst...</a><br/><br/>我可以在Firefox中使用Google工具条吗？<br/><br/>Firefox本身提供了整合搜索功能，但是如果你需要使用更多的Google工具条的功能，你可以安装扩展Googlebar。在下面的地址你可以找到这个扩展：<br/><br/><a href="https://addons.update.mozilla.org/extensions/moreinfo.php?application=firefox&id=33&vid=1185" target="_blank">https://addons.update.mozi...</a><br/><br/>Firefox中有类似网文快捕一样的网页内容摘抄工具吗？<br/><br/>是的，Firefox有不少扩展都能完成类似的功能：<br/><br/>QuickNote：提供一个浮动窗口或者侧边栏，你可以选择网页内容后直接在右键菜单中选择其添加的选项，QuickNote便会记录下你选择的内容和这些内容的网址。<br/><br/>Send To：可以把你所框选的网页内的文字发送到您所指定的文字编辑器比如记事本或者是word里去,只需要在设置中指定你的文字编辑器的路径,就可以使用右键选择了。<br/><br/><br/>ScrapBook:一个类似于网文快捕的扩展，可以保存整个网页、部分网页、选中部分，可以编辑、分类保管已经保存的网页<br/><br/><br/><br/><br/><br/>其他问题<br/>Firefox提供邮件客户端吗？<br/>Firefox仅仅是一个浏览器,并不是完整的web服务包。 但是，Mozilla的开发者开发了一个同样基于Mozilla核心代码的独立的mail/news客户端：Thunderbird（雷鸟）。 它拥有和Firefox相似的用户界面。<br/><br/><br/>Firefox常用下载一览<br/>下面列出的是Firefox在网上的主要资源:<br/><br/>官方原版下载（英文）：<a href="http://www.mozilla.org/products/firefox/index.html" target="_blank">http://www.mozilla.org/pro...</a><br/><br/>官方多语言版本下载（英文）：<a href="http://www.mozilla.org/products/firefox/all.html" target="_blank">http://www.mozilla.org/pro...</a><br/><br/>官方插件下载（英文）：<a href="https://update.mozilla.org/extensions/" target="_blank">https://update.mozilla.org...</a><br/><br/>官方皮肤下载（英文）： <a href="https://update.mozilla.org/themes/" target="_blank">https://update.mozilla.org...</a><br/><br/>插件下载(英文)：<a href="http://texturizer.net/firefox/extensions/" target="_blank">http://texturizer.net/fire...</a><br/><br/>Firefox Extensions(扩展下载 英文)：<a href="http://extensionroom.mozdev.org/" target="_blank">http://extensionroom.mozde...</a><br/><br/>The Extensions Mirror(扩展下载 英文)：<a href="http://www.extensionsmirror.nl/" target="_blank">http://www.extensionsmirro...</a><br/><br/>Firefox中文化扩展：<a href="http://www.evoworks.net/chinesefox/" target="_blank">http://www.evoworks.net/ch...</a><br/><br/><br/>我能为Firefox做什么？<br/><br/>尽量帮助你遇到的人解答问题，让他们能够尽快从IE过渡到Firefox帮忙宣传本FAQ，<a href="http://www.firefox.net.cn/faq/" target="_blank">http://www.firefox.net.cn/...</a><br/><br/>插件网站<br/><a href="http://www.firefox-china.org" target="_blank">http://www.firefox-china.o...</a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/firefox/" rel="tag">firefox</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%2581%25AB%25E7%258B%2590/" rel="tag">火狐</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%258A%2580%25E5%25B7%25A7/" rel="tag">技巧</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/111/</link>
<title><![CDATA[Java 简单的加密解密实现方法]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Fri, 11 Aug 2006 15:50:33 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/111/</guid> 
<description>
<![CDATA[ 
	工作中遇到的课题，要求简单的实现对所取到的手机号码进行加密。<br/>老大只要求简单实现，比如移位……<br/><br/>以servlet实现<br/><div class="code"><br/><br/>import java.io.*;<br/>import java.util.*;<br/>import javax.servlet.*;<br/>import javax.servlet.http.*;<br/>import java.security.*;<br/>import javax.crypto.*;<br/><br/>public class MyPhoneNum<br/> &nbsp; &nbsp;extends HttpServlet &#123;<br/><br/> &nbsp;private static String Algorithm=&quot;DES&quot;; //定义 加密算法,可用 DES,DESede,Blowfish<br/><br/> &nbsp;static boolean debug = false;<br/><br/> &nbsp;static&#123;<br/> &nbsp; &nbsp;Security.addProvider(new com.sun.crypto.provider.SunJCE());<br/> &nbsp;&#125;<br/><br/> &nbsp;//生成密钥, 注意此步骤时间比较长<br/> &nbsp;public static byte&#91;&#93; getKey() throws Exception&#123;<br/> &nbsp; &nbsp;KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);<br/> &nbsp; &nbsp;SecretKey deskey = keygen.generateKey();<br/> &nbsp; &nbsp;if (debug)<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;生成密钥:&quot;+byte2hex(deskey.getEncoded()));<br/> &nbsp; &nbsp;return deskey.getEncoded();<br/> &nbsp;&#125;<br/><br/> &nbsp;//加密<br/> &nbsp;public static byte&#91;&#93; encode(byte&#91;&#93; input,byte&#91;&#93; key) throws Exception&#123;<br/> &nbsp; &nbsp;SecretKey deskey = new javax.crypto.spec.SecretKeySpec(key,Algorithm);<br/> &nbsp; &nbsp;if (debug)&#123;<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;加密前的二进串:&quot;+byte2hex(input));<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;加密前的字符串:&quot;+new String(input));<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;Cipher c1 = Cipher.getInstance(Algorithm);<br/> &nbsp; &nbsp;c1.init(Cipher.ENCRYPT_MODE,deskey);<br/> &nbsp; &nbsp;byte&#91;&#93; cipherByte=c1.doFinal(input);<br/> &nbsp; &nbsp;if (debug)<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;加密后的二进串:&quot;+byte2hex(cipherByte));<br/> &nbsp; &nbsp;return cipherByte;<br/> &nbsp;&#125;<br/><br/> &nbsp;//解密<br/> &nbsp;public static byte&#91;&#93; decode(byte&#91;&#93; input,byte&#91;&#93; key) throws Exception&#123;<br/> &nbsp; &nbsp;SecretKey deskey = new javax.crypto.spec.SecretKeySpec(key,Algorithm);<br/> &nbsp; &nbsp;if (debug)<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;解密前的信息:&quot;+byte2hex(input)); &nbsp; &nbsp;<br/> &nbsp; &nbsp;Cipher c1 = Cipher.getInstance(Algorithm);<br/> &nbsp; &nbsp;c1.init(Cipher.DECRYPT_MODE,deskey);<br/> &nbsp; &nbsp;byte&#91;&#93; clearByte=c1.doFinal(input);<br/> &nbsp; &nbsp;if (debug)&#123;<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;解密后的二进串:&quot;+byte2hex(clearByte));<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;解密后的字符串:&quot;+(new String(clearByte)));<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;return clearByte;<br/> &nbsp;&#125;<br/><br/> &nbsp;//md5()信息摘要, 不可逆<br/> &nbsp;public static byte&#91;&#93; md5(byte&#91;&#93; input) throws Exception&#123;<br/> &nbsp; &nbsp;java.security.MessageDigest alg=java.security.MessageDigest.getInstance(&quot;MD5&quot;); //or &quot;SHA-1&quot;<br/> &nbsp; &nbsp;if (debug)&#123;<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;摘要前的二进串:&quot;+byte2hex(input));<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;摘要前的字符串:&quot;+new String(input));<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;alg.update(input);<br/> &nbsp; &nbsp;byte&#91;&#93; digest = alg.digest();<br/> &nbsp; &nbsp;if (debug)<br/> &nbsp; &nbsp; &nbsp;System.out.println(&quot;摘要后的二进串:&quot;+byte2hex(digest));<br/> &nbsp; &nbsp;return digest;<br/> &nbsp;&#125;<br/><br/><br/> &nbsp;//字节码转换成16进制字符串<br/> &nbsp;public static String byte2hex(byte bytes&#91;&#93;)&#123;<br/> &nbsp; &nbsp;StringBuffer retString = new StringBuffer();<br/> &nbsp; &nbsp;for (int i = 0; i &lt; bytes.length; ++i)<br/> &nbsp; &nbsp;&#123;<br/> &nbsp; &nbsp; &nbsp;retString.append(Integer.toHexString(0x0100 + (bytes&#91;i&#93; &amp; 0x00FF)).substring(1).toUpperCase());<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;return retString.toString();<br/> &nbsp;&#125;<br/><br/> &nbsp;//将16进制字符串转换成字节码<br/> &nbsp;public static byte&#91;&#93; hex2byte(String hex) &#123;<br/> &nbsp; &nbsp;byte&#91;&#93; bts = new byte&#91;hex.length() / 2&#93;;<br/> &nbsp; &nbsp;for (int i = 0; i &lt; bts.length; i++) &#123;<br/> &nbsp; &nbsp; &nbsp; bts&#91;i&#93; = (byte) Integer.parseInt(hex.substring(2*i, 2*i+2), 16);<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;return bts; <br/> &nbsp;&#125; &nbsp;<br/><br/> &nbsp;private void performTask(javax.servlet.http.HttpServletRequest request,javax.servlet.http.HttpServletResponse response) &#123;<br/> &nbsp; &nbsp;String MISC_MSISDN = request.getHeader(&quot;X-Up-Calling-Line-ID&quot;);<br/> &nbsp; &nbsp;if(MISC_MSISDN==null) MISC_MSISDN = &quot;13900000000&quot;; &nbsp; &nbsp;<br/> &nbsp; &nbsp;<br/> &nbsp; &nbsp;<br/> &nbsp; &nbsp;try &#123; &nbsp; &nbsp; &nbsp;<br/> &nbsp; &nbsp; &nbsp;String getCode = this.getParameter(request,&quot;code&quot;,&quot;0&quot;);<br/> &nbsp; &nbsp; &nbsp;String getKey = this.getParameter(request,&quot;key&quot;,&quot;0&quot;);<br/> &nbsp; &nbsp; &nbsp;String getMd5 = this.getParameter(request,&quot;md5&quot;,&quot;0&quot;);<br/> &nbsp; &nbsp; &nbsp;MISC_MSISDN = this.getParameter(request,&quot;MISC_MSISDN&quot;,MISC_MSISDN);*/<br/> &nbsp; &nbsp; &nbsp;/*PrintWriter out1 = response.getWriter();<br/> &nbsp; &nbsp; &nbsp;out1.println(getCode);<br/> &nbsp; &nbsp; &nbsp;out1.println(getKey);<br/> &nbsp; &nbsp; &nbsp;out1.println(getMd5);<br/> &nbsp; &nbsp; &nbsp;out1.close();*/<br/> &nbsp; &nbsp; &nbsp;if(getCode.equals(&quot;0&quot;))&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;byte&#91;&#93; key = getKey();<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (!getKey.equals(&quot;0&quot;))&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;key = hex2byte(getKey);<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;String strCode = byte2hex(encode(MISC_MSISDN.getBytes(),key));<br/> &nbsp; &nbsp; &nbsp; &nbsp;String strKey = byte2hex(key);<br/> &nbsp; &nbsp; &nbsp; &nbsp;String md5Num = byte2hex(md5(MISC_MSISDN.getBytes()));<br/> &nbsp; &nbsp; &nbsp; &nbsp;PrintWriter out = response.getWriter();<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;Code:&quot;+strCode);<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;Key:&quot;+strKey);<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;md5 Phone Number:&quot;+md5Num);<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.close();<br/> &nbsp; &nbsp; &nbsp;&#125;else&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp;byte&#91;&#93; byteCode = hex2byte(getCode);<br/> &nbsp; &nbsp; &nbsp; &nbsp;byte&#91;&#93; byteKey = hex2byte(getKey);<br/> &nbsp; &nbsp; &nbsp; &nbsp;String strPhoneNum = new String(decode(byteCode,byteKey));<br/> &nbsp; &nbsp; &nbsp; &nbsp;PrintWriter out = response.getWriter();<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;Phone Number:&quot;+strPhoneNum);<br/> &nbsp; &nbsp; &nbsp; &nbsp;if (getMd5.equals(&quot;0&quot;))&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;None md5 check!&quot;);<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;else if (getMd5.equals(byte2hex(md5(strPhoneNum.getBytes()))))&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;md5 check Ok!&quot;);<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;else&#123;<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;out.println(&quot;md5 check false!&quot;);<br/> &nbsp; &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp; &nbsp; &nbsp;out.close();<br/> &nbsp; &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;&#125;catch (Exception e) &#123;<br/> &nbsp; &nbsp; &nbsp;System.out.println(e.getMessage());<br/> &nbsp; &nbsp;&#125;<br/> &nbsp; &nbsp;<br/><br/> &nbsp;&#125;<br/><br/> &nbsp;public void doGet(javax.servlet.http.HttpServletRequest request,javax.servlet.http.HttpServletResponse response) &#123;<br/> &nbsp; &nbsp;performTask(request, response);<br/> &nbsp;&#125;<br/><br/> &nbsp;public void doPost(javax.servlet.http.HttpServletRequest request,javax.servlet.http.HttpServletResponse response) &#123;<br/> &nbsp; &nbsp;performTask(request, response);<br/> &nbsp; &nbsp;&#125;<br/><br/> &nbsp;private java.lang.String getParameter(javax.servlet.http.HttpServletRequest request, java.lang.String parameterName, java.lang.String defaultValue) throws java.lang.Exception &#123;<br/> &nbsp; &nbsp;java.lang.String&#91;&#93; parameterValues = null;<br/> &nbsp; &nbsp;java.lang.String paramValue = null;<br/> &nbsp; &nbsp;<br/> &nbsp; &nbsp;parameterValues = request.getParameterValues(parameterName);<br/> &nbsp; &nbsp;if (parameterValues != null)<br/> &nbsp; &nbsp; &nbsp;paramValue = parameterValues&#91;0&#93;;<br/><br/> &nbsp; &nbsp;if (paramValue == null)<br/> &nbsp; &nbsp; &nbsp;paramValue = defaultValue;<br/><br/> &nbsp; &nbsp; &nbsp;return paramValue;<br/> &nbsp;&#125;<br/>&#125;<br/></div><br/>Tags - <a href="http://www.newtyper.com/blog/tags/java/" rel="tag">java</a> , <a href="http://www.newtyper.com/blog/tags/%25E5%258A%25A0%25E5%25AF%2586%25E8%25A7%25A3%25E5%25AF%2586/" rel="tag">加密解密</a> , <a href="http://www.newtyper.com/blog/tags/servlet/" rel="tag">servlet</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/106/</link>
<title><![CDATA[ How to Install VMware Tools on FC5 (Fedora Core 5)]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Thu, 15 Jun 2006 01:03:05 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/106/</guid> 
<description>
<![CDATA[ 
	<div class="quote"><div class="quote-title">引用</div><div class="quote-content">Hello there, I don't know if my post will help some outta there, this is just a resume thread wich I have compute with the various patch I had to apply to fully install VMware Tools on the fresh FC5.<br/><br/>1) make sure you have the corresponding sources of your kernel shown by uname -r (or yum install kernel-devel)<br/><br/>2) if you run ./vmware-config-tools.pl for the first time, the config won't find a suitable kernel path.<br/><br/>3) download <a href="http://ftp.cvut.cz/vmware/vmware-any-any-update99.tar.gz" target="_blank">http://ftp.cvut.cz/vmware/...</a><br/>extract it<br/><br/>4) download <a href="http://ftp.cvut.cz/vmware/vmware-tools-any-update1.tar.gz" target="_blank">http://ftp.cvut.cz/vmware/...</a><br/>extract it in the previously created vmware-any-any-update99 folder to overwrite the file runme.pl<br/><br/>5) after this changes, run the new "vmware-any-any-update99/runme.pl"<br/><br/>6) if you run now again vmware-config-tools, it should now detects fine your kernel source path<br/><br/>7) but after I came across another problem "No X install found", I googled around and found out that was because the vmware-config-tools.pl doenst detects newer X version 7<br/><br/>8) You will need to update manually vmware-config-tools.pl manually, below are the manual code changes to apply or read in this mirrored txt including the changes to do if the forum strip some code parts (patch is from this commun<br/>ity from an ubuntu problem thread):<br/><br/><a href="http://heapoverflow.com/vmware-config-tools.txt" target="_blank">http://heapoverflow.com/vm...</a><br/></div></div><br/><br/>Link: <a href="http://www.vmware.com/community/thread.jspa?threadID=36866&tstart=0" target="_blank">http://www.vmware.com/comm...</a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/fedora/" rel="tag">fedora</a> , <a href="http://www.newtyper.com/blog/tags/vmware/" rel="tag">vmware</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/105/</link>
<title><![CDATA[Win2003下常见问题整理]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Thu, 18 May 2006 23:41:19 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/105/</guid> 
<description>
<![CDATA[ 
	通宵一夜基本搞定我日常用机的windows2003系统，困啊。资料留存！<br/><br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content"><br/>1．打开 DirectX 的 D3D 硬件加速：<br/><br/>看桌面属性，设置 -> 高级 -> 疑难问答 -> 硬件加速 -> 完全。或开始-à运行键入 dxdiag—>回车，打开Display选项卡，可看到 3 项全部启用了。最后，利用Windows updates 在Windows Server 2003中安装DirectX 9.0a。<br/><br/>2. 启用声卡：<br/><br/>系统安装后，声卡是禁止状态，所以要在 控制面板 -> 声音 -> 启用，重启之后再设置它在任务栏显示。现在我们还要启用音频加速。在开始/运行键入Services.msc然后按回车 ，会出现Services 窗口，找到Windows Audio服务，双击打开，把启动类型设置为Automatic，点击Apply，然后点击Start启动该服务。最后我们还要使用DirectX诊断工具，在运 行中输入dxdiag并回车，打开Sound选项卡，把Hardware Sound Acceleration Level的滑块拖动到Full。<br/><br/>3. 如何启用 ASP 支持：<br/><br/>Windows Server 2003 默认安装，是不安装 IIS 6 的，需要另外安装。安装完 IIS 6，还需要单独开启对于 ASP 的支持。方法是：<br/>控制面板 -> 管理工具 -> Web服务扩展 -> Active Server Pages -> 允许。关于安装php服务的方法与之相似，但要注意设置。<br/><br/>4. 如何启用 XP 的主题：<br/><br/>首先我们需要回到服务设置对话框启用Themes服务，在运行中输入Services.msc并按回车，找到并双击Themes这个服务，设置启动类型为Automatic，点击Apply后点Start打开这个 服务。<br/><br/>5. 禁止关机时出现的关机理由选择项：<br/><br/>开始 -> 运行 -> gpedit.msc -> Computer configuration -> Administrative Templates ->System->Display shutdown event tracker -> 设置为 Disable。<br/>如果是中文版，则：gpedit.msc，计算机配置 -> 管理模板 -> 系统 -> 显示关机事件跟踪 -> 禁用。<br/><br/>6. 如何启用摄像机，摄像头或者扫描仪等设备：<br/><br/>在运行中输入Services.msc并回车，找到并双击Windows Image Acquisition (WIA) 服务，设置启动类型为Automatic点击Apply后点击Start然后点击OK。<br/><br/><br/>7. 在控制面板里显示全部组件：<br/><br/>把 Windows&#92;inf 目录中的 sysoc.inf 文件里的 "hide" 替换掉。<br/><br/><br/>8．禁用配置服务器向导<br/><br/>由于不需要服务器设置功能，首先我们先禁止“配置你的服务器”（Manage Your Server）向导的出现，你可以在控制面板（Control Panel） -> 管理员工具（Administrative Tools ）-> 管理你的服务器（Manage Your Server）运行它，然后在窗口的左下角复选“登录时不要显示该页”（Don't display this page at logon）。<br/><br/><br/>9．禁用Internet Explorer Enhanced Security<br/><br/>作为新windows组件出现的IE安全插件－－Internet Explorer Enhanced Security默认把你IE安全设置为最高。这样你将在访问站点弹出询问框并对你浏览网页及文件下载做出阻止的行为。我们其实不一定需要这个组件。我们首先禁止询问框的出现,在弹出的对话框中复选”以后不要显示这个信息“ （In the future, do not show this message）然后，我们可以在IE工具选项中自定义设置IE的安全级别。在”安全“（Security）选项卡上拉动滚动条把Internet区域安全设置为”中 “（Medium），这个级别将适合大多数人，要是 你有特别要求，这个步骤将不适合你。通过对IE安全的设置，你现在安装可以上Sun's Java VM! 当然，你甚至可以在控制面板－－添加程序－－添加或删除Windows组件中卸载 Internet Explorer Enhanced Security<br/><br/><br/>10．USB设备作为Removable Storage服务，默认是没有启动的，得手动打开才可以使用USB硬盘等。<br/><br/><br/>11．禁用开机 CTRL+ALT+DEL和实现自动登陆<br/><br/>管理工具 -> Local Security Settings（本地安全策略） -> 本地策略 -> 安全选项 -> interactive logon: Do not require CTRL+ALT+DEL，启用之。<br/><br/><br/>12．处理IE的最大化设置：<br/><br/>1、运行注册表编辑器，删除HKEY_CURRENT_USER&#92;Software&#92;Microsoft&#92;Internet Explorer&#92;Main分支下的Window_Placement键值，它设置IE窗口最后出现在桌面的位置；再删除 HKEY_CURRENT_USER&#92;Software&#92;Microsoft&#92;Internet Explorer&#92;Desktop&#92;Old WorkAreas分支下OldWorkAreaRects子键；还有HKEY_CURRENT_USER&#92;Software&#92;Microsoft&#92; Internet Explorer&#92;Document Windows分支，它记录着是否最大化信息。<br/>2、右键点击任务栏->平铺窗口。<br/><br/><br/>13. 安装“视频压缩”组件<br/><br/>在2003下动画没有图像，只有声音，提示“视频无法使用，找不到vids:cvid解压缩程序”。这都是Windows server 2003中没有安装“视频压缩”组件造成的。<br/>第一步：找一张Windows XP的安装光盘，在其中的i386目录下找到ir32_32.dl_和iccvid.dl_两个文件，用WinRAR将它们解压缩到C:&#92;Windows &#92;System32下。解压缩后的两个文件 分别为：ir32_32.dll和iccvid.dll。<br/>　第二步：打开“注册表编辑器”，找到 [HKEY_LOCAL_ MACHINE&#92;SOFTWARE&#92;Microsoft&#92;Windows NT&#92;CurrentVersion&#92;Drivers32]，在右侧窗格中新建名为vidc.cvid的字符串值，将其数值设置为：iccvid.dll；再新建一个名为vidc.iv31的字符串值，将其值设置为ir32_32.dll；接着新建名为 vidc.iv32的字符串值，其值为ir32_32.dll。<br/><br/><br/>14. 如何移除并重新安裝NetMeeting？<br/><br/>1、从 %SystemRoot%&#92;Inf 目錄中複製Msnetmtg.inf 到您的桌面<br/>2、依序按一下 [開始]、[執行]，在 [開啟] 方塊中輸入： :<br/>%SystemRoot%&#92;System32&#92;rundll32.exe setupapi,InstallHinfSection NetMtg.Remove 132 msnetmtg.inf<br/>NOTE: 此依命令行字元有大小寫的分別，請務必確實輸入。<br/>3、在步驟1.中複製到桌面的Msnetmtg.inf 檔案，滑鼠按右鍵, 按下安裝. 這時您可能需要放入Windows Server 2003安裝光碟至光碟機.<br/>4、安裝完成後請重新開機。<br/><br/><br/>15. 如何修复系统文件？<br/><br/>要中使用“系统文件检查器”，先要单击“开始→所有程序→附件→命令提示符”，然后在“命令提示符”窗口的光标提示符后键入“Sfc”并按下回车键，“系统文件检查”程序 会给出参数的中文提示。<br/>通过Windows安装盘来修复被损坏了的文件,恢复的具体过程如下：<br/>在Windows 的安装盘中搜索被破坏的文件，需要注意的是，文件名的最后一个字符用底线“_”代替，例如：如果要搜索“Notepad.exe”则需要用 “Notepad.ex_”来进行搜索。搜 索到了之后，打开命令行模式(在“运行”中输入“cmd”)，然后输入：“EXPAND 源文件的完整路径目标文件的完整路径”。例如：EXPAND D:&#92;SETUP&#92;NOTEPAD.EX_ C:&#92;Windows&#92;NOTEPAD.EXE。有一点需要注意的是，如果路径中有空格的话，那么需要把路径用双引号(英文引号)包括起来。<br/>找到当然是最好的，但有时我们在Windows盘中搜索的时候找不到我们需要的文件。产生这种情况的一个原因是要找的文件是在“CAB”文件中。由于 Windows把“CAB”当作一个文 件夹，所以对于Windows系统来说，只需要把“CAB”文件右拖然后复制到相应目录即可。<br/>如果使用的是其他Windows平台，搜索到包含目标文件名的“CAB”文件。然后打开命令行模式，输入：“EXTRACT /L 目标位置 CAB文件的完整路径”，例如：EXTRACT /L C:&#92;Windows D:&#92;I386&#92;Driver.cab Notepad.exe。同前面一样，如同路径中有空格的话，则需要用双引号把路径包括起来。<br/><br/><br/>16. 如何实现ADSL共享？<br/><br/>1、服务器需要双网卡。<br/>2、新建ADSL拨号连接。<br/>3、点击2003安装光盘&#92;SUPPORT&#92;TOOLS&#92;NETSETUP.EXE执行安装，重新启动机器即可。<br/><br/><br/>17. 出现AVI文件无法删除的情况如何处理？<br/><br/>可能是Explorer的预览造成的。如果不需要预览影音文件，把shmedia.dll文件解除注册就可以解决这个问题。解除方法：regsvr32 /u shmedia.dll<br/><br/>18. 如何设置代理？<br/><br/>1、打开IE工具栏的“Internet选项”。<br/>2、在“连接”页面选择“拨号设置”，然后单击“设置”。<br/>3、在“设置”页面中，选中“使用代理服务器”然后在“地址”栏中填上代理服务器地址和“端口”，单击“确定”。<br/>4、可使用的代理地址：<br/><br/>19. 打SP1补丁如何选择不备份？<br/><br/>用WinRAR把下载的SP解开，进入cmd模式，进入i386&#92;update目录，然后运行update /n，就不会备份啦。另：运行update /?就能看到详细的定义安装参数。<br/><br/><br/>20. 清除Thumbs.db文件的方法？<br/><br/>在“文件夹选项”里选中“不缓存缩略图”，这样可以禁止生成新的Thumbs.db文件，至于旧的Thumbs.db文件，直接用系统的搜索功能，找出硬盘上的所有Thumbs.db文件，全部删 除即可。<br/><br/><br/>21. Win2003如何实现自动登录？<br/><br/>开始->运行->输入“rundll32 netplwiz.dll,UsersRunDll”命令打开帐户窗口，先选中某帐户，再去除“要使用本机，用户必须输入用户名密码”复选框中的勾号，输入该帐户的密码即可。<br/><br/><br/>22. 如何取消Winnt/2000/xp/2003每次开机的默认共享？<br/><br/>对于服务器而言 在 HKEY_LOCAL_MACHINE&#92;SYSTEM&#92;CurrentControlSet&#92;Services&#92;lanmanserver&#92;parameters 下，增加一个名为“AutoShareServer”的双字节值，设为“0”。然后去掉共享，重新启动。<br/>对于工作站而言 在 HKEY_LOCAL_MACHINE&#92;SYSTEM&#92;CurrentControlSet&#92;Services&#92;lanmanserver&#92;parameters 下，增加一个名为“AutoShareWks”的双字节值，设为“0”。然后去掉共享，重新启动。<br/><br/><br/>23. 事件查看器里报DCOM出错，如何解决？<br/><br/>根据提示信息，实际上是说NETWORK SERVICE没权限激活CLSID为&#123;BA126AD1-2166-11D1-B1D0-00805FC1270E&#125;的应用程序。可以通过使用组件服务管理工具修改此安全权限。<br/>1、如果按上面的提示去使用组件服务管理工具找CLSID为&#123;BA126AD1-2166-11D1-B1D0-00805FC1270E&#125;的应用程序是找不到的。<br/>2、需要先运行regedit.exe在注册表中查找出&#123;BA126AD1-2166-11D1-B1D0-00805FC1270E&#125;对应的AppID值&#123;27AF75ED-20D9-11D1-B1CE-00805FC1270E&#125;<br/>3、然后再打开组件服务，查看方式为详细信息，找到DCOM 配置里的netman，选中按鼠标右建选属性。<br/>4、在netman属性里的安全 -> “启动和激活权限” -> 自定义编辑，在启动权限里加入NETWORK SERVICE用户，允许本地启动和本地激活，确定后就不会再报这个DCOM错了。 <br/></div></div><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E7%25B3%25BB%25E7%25BB%259F/" rel="tag">系统</a> , <a href="http://www.newtyper.com/blog/tags/windows/" rel="tag">windows</a> , <a href="http://www.newtyper.com/blog/tags/2003/" rel="tag">2003</a> , <a href="http://www.newtyper.com/blog/tags/%25E9%2597%25AE%25E9%25A2%2598/" rel="tag">问题</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/101/</link>
<title><![CDATA[好久没关注我家MPX220的资料了]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Fri, 03 Mar 2006 06:21:37 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/101/</guid> 
<description>
<![CDATA[ 
	小抄小抄<br/><br/>打开mpx220工程/测试模式打开工程/测试模式<br/><br/><br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content">测试模式：*#**372#<br/>工程模式：*#**364#<br/><br/>* # * * 8 3 7 # Version info<br/>* # * * 2 6 6 * 0 # Set as data COM port<br/>* # * * 2 6 6 * 1 # Set as log COM port<br/>* # * * 7 9 7 # Time to failure timer & Power down code<br/>* # * * 2 2 6 3 * 0 # Set to default Band<br/>* # * * 2 2 6 3 * 1 # Disable GSM Band<br/>* # * * 2 2 6 3 * 2 # Disable DCS Band<br/>* # * * 2 2 6 3 * 3 # Disable EGSM and DCS Band<br/>* # * * 2 2 6 3 * 4 # Disable PCS Band<br/></div></div><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E6%2589%258B%25E6%259C%25BA/" rel="tag">手机</a> , <a href="http://www.newtyper.com/blog/tags/%25E4%25BD%25BF%25E7%2594%25A8/" rel="tag">使用</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/13/</link>
<title><![CDATA[IE 十年]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Thu, 11 Aug 2005 03:15:32 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/13/</guid> 
<description>
<![CDATA[ 
	<a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/ie7.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/ie7.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>　　想必看我BLOG的朋友大多是使用IE来浏览的，8月关于微软最热门的新闻应该是Vista beta1的发布以及其捆绑的IE7 Beta1。10年前的这个月IE诞生在这个世界上也带来一场“战争”就让我们来回顾下这段历史。<br/><br/><br/>《<span style="color: #008000;"><span style="font-size: 14px;">小块头大智慧:微软IE网络霸业十年回眸</span></span>》<br/>转自：<a href="http://www.pconline.com.cn/pcedu/soft/wl/brower/0507/662048.html" target="_blank">太平洋电脑网</a><br/><br/><strong>引言</strong>：话说很久很久以前，当这个世界还刚出现Web浏览器的时候，已经有一个叫做Marc Andreessen(安德森)的人开发了一个名号为Mosaic的浏览器，让地球的第一代网虫过足了瘾。后来他又写了一个更了不起的Netscape浏览器，从此笑傲江湖了好几年。不过后来比尔盖茨也将他的魔手伸进了这个领域，从此风云变色，一场惊天地泣鬼神的第一代浏览器大战便正式拉开了帷幕。<br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_marc_andreessen.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_marc_andreessen.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>　　等待的时间总是难熬的，当微软终于下定决心，排除万难，要在2005年这个炎热的夏季推出IE7.0时，我们想借这一个难得的机会，跟大家详细地回顾一下微软IE的前世与今生，它与Netscape 浏览器打的那场瑰丽无伦的战争，以及延绵至今的新一轮网页浏览器争霸，里面还会有关于微软IE的种种故事、传闻、臆测和展望。<br/><strong><br/>IE的前世故事</strong><br/><br/>　　当你打开IE浏览器，点击"帮助"-->"关于Internet Explorer"，就会发现以下的信息:本软件是在 NCSA Mosaic 的基础上完成的。NCSA Mosaic(TM) 由位于 Urbana-Champaign 的伊利诺斯大学的超级计算机应用程序国家中心 (NCSA) 开发。<br/><br/>　　难道IE不是微软开发的吗？你想得没错！Internet Explorer(以下简称IE)浏览器虽然捆绑在Windows系统里，但它并非微软家族原创的作品，这个如今看来了不起的成员其实是伊利诺斯大学的超级计算机应用程序国家中心 (NCSA)于1993年开发的，那时它叫NCSA Mosaic，是个基于X-Windows的浏览器，它就是传说中的微软IE、网景以及众多网页浏览器的鼻祖。既然IE是在它的基础上修改而成的，那就先让我们好好接触一下这个对我们来到说已经遥不可及的Web浏览器开山之作吧。<br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_mosaic.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_mosaic.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>Mosaic标识，不认识NCSA？最著名的Apache Serve的前身httpd其实就是他们开发的。<br/><br/>　　Mosaic发布的最早版本是1993年1月的Alpha版。它支持HTML1.0标准，但当时只能支持X-Windows，93年4月发布正式版。后在同年9月又发行了一个崭新的版本，才真正地支持了X PC、Macintosh和日后大放异彩的Windows操作系统。在这之前所谓的Web浏览器只能浏览Internet上的文本信息，而当年的人们也对这个神奇的能显示图片的新浏览器爱不释手，并且大家根据Mosaic的显示特性设计出第一批真正的WWW服务网站。可以说没有了它就没有现代互联网的辉煌，Mosaic对Internet的贡献将被永载史册。<br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_mosaic2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_mosaic2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>Mosaic浏览器你用过没？<br/><br/><strong>有兴趣感受一下古董的魅力吗？</strong>　[<a href="http://archive.ncsa.uiuc.edu/SDG/Software/mosaic-w/releaseinfo/download.html" target="_blank">NCSA Mosaic下载页面</a>]<br/><br/>　　但Mosaic的出现并未立即让所有人疯狂起来，因为当时操作系统还是很原始，美国甚至全世界的互联网也只是一个简单的雏形，使用的人并不算很多，资源都集中在高级的知识份子手里。<br/><br/>　　现今NCSA Mosaic已经无人再作更新了，并且它的源代码早已公开，每一位有能力的程序员或公司都可以拿它做进一步的开发。<br/><br/>　　这在野心勃勃的微软看来，是个巨大的机会。<br/><br/>　　Marc Andreessen这个Mosaic浏览器的发明者怎么样也想不到，当他开发了Mosaic，又创建了自己的公司Mosaic Communications Corp，并在1994年12月发布了Netscape浏览器的1.0版本后，这个浏览器竟成为日后与微软IE争霸的重要工具。大家看到这里应该明白了， Mosaic Communications Corp其实就是现在被AOL公司收购的Netscap，中译名为网景。<br/><br/>　　<strong>IE的今生：在这匆匆十年间的进步(1995-2005)</strong><br/><br/>　　Marc Andreessen新发布的Netscape浏览器大受当时上网一族的欢迎，因为Netscape 1.0浏览器创造了一个记录，它比上一代的Mosaic浏览速度足足快了十倍，还独创性地使用密钥算法保证网上数据的安全。当时立刻占领了高达70%的市场，人人几乎都是用它上网。而微软适时地抓住了这一波的互联网热潮，跟其它100多间公司一样，成功地从Spyglass, Inc那里取得了Mosaic软件的许可，可以研发基于Mosaic的各种不同的浏览器，战国的混乱时代开始了。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_netscape.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_netscape.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>早期能与IE对抗的网景浏览器标识<br/><br/>　　如果说人类的历史就是一部战争史，用这话来形容微软IE来说也同样合适，当年它为了跟Netscape浏览器互争一日之长短，可谓法宝尽出，无所不用其技。顺便提一下，当年Netscap浏览器其实有机会被Microsoft收编的，不过比尔似乎对它不太感冒，所以婉言拒绝了。现在看来当年的决定是多么的愚蠢，如果不是这样的话，那浏览器的发展史一定会重新改写，也不会有后来的网景与IE之争了。<br/><br/>　　好，那我们来回顾一下微软发布IE 1.0至IE 6.0 SP2之间所发生的故事吧。<br/><br/>　　<strong>IE 1.0诞生的故事</strong> [<a href="http://spot.fho-emden.de/alge/museum/ftp/msie10.exe" target="_blank">点击下载</a>]<br/><br/>　　1995年8月，微软家族中一个日后呼风唤雨的成员呱呱落地，它就是Microsoft Ineternet Explorer 1.0了，但当时微软并未对这个新生儿给予多大的照顾，因为比尔盖茨正沉浸在1995年1月发布的Windows 95所带来的巨大喜悦里，这才是他们的重头戏。<br/><br/>　　这个其貌不扬功能简陋的IE 1.0只是基于NCSA Mosaic的简单"修改版"。没有增加太多的新特性，不支持Java，不支持插件，浏览速度也很缓慢。Netscape浏览器还没有把这个小家伙看在眼里，据说当年给IE1.0做研发工作的只有几个人而已。<br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie10.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie10.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>长得与Mosaic差不多的IE1.0<br/><br/>　　这时IE1.0还是一个单薄的程序，并未引起太大的反响。人们仍然用Mosaic或者Netscape对网页进行浏览。不过面对Netscape如日中天的气势，微软似乎也嗅到了一丝危机。<br/><br/>　　<strong>IE 2.0诞生的故事</strong><br/><br/>　　在完成了IE1.0的研发后，经过仅仅两个月的筹备与开发，95年10月微软就匆匆地推出IE2.0，开发的人员已经有几十个人的规模。这个版本终于有点象样了，增加了News-Group(新闻组)功能，还有对表格和一些新的HTML元素的支持。<br/><br/>　　相对来说，IE2.0比网景浏览器多一些新特性，例如Marquee（滚动文字）功能就是一个例子，但当时IE还未拥有允许浏览器窗口分成不同的部分和表格功能。其时已是HTML 4标准盛行的时代了。<br/><br/>　　企业内部网的发展让Netscape浏览器的"圈地运动"再次加速。这令微软感到前所未有的压力，当年甚至某些金融分析家剔除了股票推荐名单上Microsoft的字样。这次事件终于令骄傲的雷德蒙巨人愤怒了。<br/><br/>　　于是在1996年的珍珠港事件纪念日Microsoft召开了一次大会，在与会的近200名记者和分析家面前，他们高调地宣布调整公司的 Internet战略：要为用户提供更好，更快，并且"永远免费的IE浏览器"，它支持的还包括Mac,WIN31，WinNT操作系统。这个简单的消息打击面却非常地广，内容涵括了Netscape浏览器最主要的产品线，微软与网景的真正较量开始了。<br/><br/>　　96年3月的一个极其普通的日子，美国在线AOL公司却突然宣布了一个令人惊讶的消息，他们将已注册的5000000会员从使用Netscape浏览器转向使用微软IE。这是IE诞生以来获取的单宗最大的胜利。不仅Netscape愕然，业界也一片喧嚷。因为微软给AOL的代价是在Windowds 95里加上AOL的图标。紧接其后IE又继续过关斩将，CompuServe、AT&T、NETCOM等公司也纷纷宣布采用IE。<br/><br/>　　面对如此严重的倒戈，Netscape仍然在97年仍保持了强劲的增长势头，不过其大部分收入只来自企业内部网，在其它领域的市场份额已经给IE狂风骤雨般的手段囊括掉。果然，微软的成功并非浪得虚名。<br/><br/>　　<strong>IE3.0诞生的故事</strong><br/><br/>　　1996年8月IE3.0正式版发布。这个版本提供了对HTML表格定制，框架以及更多HTML元素的支持，同时有了VB、脚本语言和电子邮件阅读器的支持。此时IE的性能已经和Netscape3.0不相上下，而且最可怕的是它真正地实现了微软的承诺：完全免费。<br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie30_win97.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie30_win97.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>IE3.0已有中文版本，但标识却不是现在熟悉的“e”<br/><br/>　　在这一年里IE3.0发生了一个重要的变化，它终于支持1995年出现的Java脚本语言了。微软还为1996年的IE 3.0设计了另一种后来也声名显赫的脚本语言--VBScript语言。<br/><br/>　　在1996年底W3C提出了CSS的建议标准，而 IE 3.0又立刻将它引入到了IE3.0里面。<br/><br/>　　而对于日后深深地影响到计算机安全的ActiveX控件功能，也是在1996年由IE3.0开始支持的，现在比较著名的ActiveX控件有3721上网助手、Flash插件、Realplayer插件、微软Media Player插件等。所以说在新技术应用方面，微软是永远不甘于人后的。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie30_win97_2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie30_win97_2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE3.0界面图，在Winodws 95 OSR2中捆绑<br/><br/>　　微软深知自己的优势在哪里，凭借着自己多年的操作系统开发经验，他们发现很多用户不仅对于给操作系统捆绑浏览器没有什么戒心，反而十分欢迎这样做。人们的这种想法让微软决定调整策略，为了彻底地垄断市场，他们做出了一个日后被人痛骂多年的决定：将IE捆绑进Windows，成为一个重要的组成部分，并且令它们永远也不能分离。<br/><br/>　　当年微软发布的Windows 95的OSR2版操作系统，这个被昵称为Windows97的系统自然也捆绑了它。<br/><br/>　　据说当年微软主席比尔盖茨告诉记者："你们要记住一件事，Microsoft不需要从Internet软件上得到任何收入"。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_bill.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_bill.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>微软主席比尔·盖茨<br/><br/>　　既然IE是完全免费的，相对于收费的Netscape浏览器，大家都不约而同地选择了前者。<br/><br/>　　微软狂风骤雨般的攻势与手段并没有将Netscape（网景）公司吓倒，他们反而在1996年8月向美国联邦政府递交了一纸诉讼，控告 Microsoft采取不正当竞争手段。因为当年Microsoft向硬件厂商和Internet接入商承诺只要他们在各自的产品里捆绑IE，就会得到3 美元的折扣。<br/><br/>　　不过让人扼腕叹息的是围绕这个控告的调查旷日持久，微软顶着压力还在若无其事地进行推广，Netscape的市场份额继续萎缩。人们开始关注IE这颗璀灿的新星了，当年有市场调查结果表明，IE的市场份额已经从8%狂升到30%，虽然这个数据可能有点水份，不过从IE的表现来看可信度还是挺高的。Netscape虽然急得跳脚但却无可奈何。<br/><br/>　　<strong>IE4.0诞生的故事</strong><br/><br/>　　当历史的指针转到1997年10月，IE4.0正式版发布了。这个版本增强了对样式列表和文档对象模型的支持，新增了更多的新特性，浏览器的显示能力也明显改善。<br/><br/>　　IE 4.0当年发布的时候，分为两个版本：<br/><br/>　　一、 标准版：含浏览器，电子邮件、网络新闻管理的Outlook Express和多媒体增强组件。<br/>　　二、 完整版：比标准版增加多了网络电话功能的NetMeeting组件、编辑HTML文件的FrontPage Express组件、Web发行向导、多媒体组件NetShow和通讯组件Chat 2.0。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie40_win98_1.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie40_win98_1.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 4.0中的“关于”<br/><br/>　　捆绑的过程也并非一帆风顺，1997年12月，美国地区法官托马斯·宾菲尔德·杰克逊(ThomasPenfieldJackson)签发禁止令，要求微软公司不得勒令计算机生产商在安装Windows95操作系统时要安装其Internet Explorer浏览器软件，迫使微软公司暂时地停止了捆绑销售计划。<br/><br/>　　但众所周知财大气粗的微软并不会因此而屈服，仅仅过了六个月，1998年6月23日，一个由三名法官组成的上诉委员会取消了杰克逊法官对Windows95操作系统软件的限制令，称微软公司有足够的理由将浏览器软件与操作系统软件捆绑销售。<br/><br/>　　这下子微软终于可以放下心头之石开怀大笑了。因为1998年6月，在这个炎热的夏季里他们的最新杰作Windows 98操作系统第一版诞生，可以肆无忌惮地继续捆绑IE4.0浏览器而不用担心别人的指控。这是一个极其重要的胜利，属于微软的Inernet霸权时代已经来临了！<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie40_win98_2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie40_win98_2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 4.0界面截图<br/><br/>　　抛开官司的烦扰，我们发现更多的新特性也被集成在IE4.0里面，HTAs（HTML Applications）技术则允许我们直接将HTML页面转换为一个真正的应用程序。微软增加了WebBrowser控件，可以允许程序员在自己开发的软件里面，直接调用浏览器的窗口，也可以调用浏览器的各种功能。<br/><br/>　　在浏览的体验上面，IE4.0新增加了浏览栏的功能，当我们点击 "收藏"、"历史"等按钮时，它就会自动在浏览器的左方出现独立的小窗口，让你在此处调用功能，网页也会自动进行宽度和长度的调整。<br/><br/>　　到了98年1月份，Netscape浏览器终于顶不住压力了，它宣布向用户免费提供自己的浏览器软件，希望以此为契机换取新老用户的爱戴，重夺失去的市场份额，再次稳坐浏览器市场的老大地位。<br/><br/>　　商战如战场，残酷的浏览器大战会不会给Netscape一个重塑辉煌的机会呢？微软当然摇摇头说："No"。<br/><br/>　　<strong>IE5.0/5.5诞生的故事 </strong>[<a href="http://dl.pconline.com.cn/html/1/6/dlid=1866&dltypeid=1&pn=0&.html" target="_blank">IE 5.0下载</a>] [<a href="http://dl.pconline.com.cn/html/1/6/dlid=2896&dltypeid=1&pn=0&.html" target="_blank">IE 5.5下载</a>]<br/><br/>　　被捆绑进操作系统的IE使用份额逐渐增加，人们对于系统自带的这个小浏览器似乎感到十分满足，第三方浏览器因为需要另外下载，所以无论它的功能如何优秀，还是被IE所打败。从1998的6月的Beta1到11月的Beta2，IE5.0持续多个月的开发与测试赚足了众多用户的眼球。<br/><br/>　　终于，微软在1999年的3月发布了Internet Explorer 5.0正式版，这个版本提供了更多CSS2功能和新的CSS属性支持，其它改变也非常大，让用户十分的兴奋。因为就在这一年里，IE5.0增加了对XML语言的支持。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie50_win98se.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie50_win98se.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 5.0捆绑在Windows 98se中<br/><br/>　　XML之所以重要并非由于它是W3C提出来，而是因为沿用多年的HTML语言只关心信息的表现形式，而XML语言则能够同时关心信息本身的格式与数据内容，可以显著提高服务端的信息获取、生成、发布和共享能力。我们现在用的RSS阅读其实就是基于XML语言的。<br/><br/>　　对于站长来说，他们建立网站也可以选择多一种语言，从那一年起可以整站使用XML语言进行网页内容的描述了，这是一个很大的进步。<br/><br/>　　而IE的主要竞争对手呢？有一个数字我想大家应该要牢牢记住，1998年11月24日美国在线AOL 向世人宣布收购Netscape公司，为了得到它AOL这个巨人付出了42亿美元，在众人惊异的目光中，一代王者从此被紧紧地搂在AOL强壮的怀抱中，再也不复昔日的辉煌。<br/><br/>　　而当年受到网络泡沫导致股票上升的影响，实际收购Netscape公司费用最终居然接近90亿美元。正当人人都以为美国在线要将它发扬光大时，Netscape却沮丧地发现AOL从此对浏览器市场不闻不问了。这种奇怪的行径令Netscape从一个实际的互联网领航者退下火线，变为旁观者。难过地坐看微软IE逐渐发展、壮大乃至成为霸主。直至2000年11月才推出的Netscape 6版却再也无法挽回任何用户了。<br/><br/>　　不管怎么样Netscape与IE的斗争始终是是互联网发展史上最具传奇性的一段历史，不少互联网先驱们刚学上网的时候就是使用它来浏览简陋的网页，然而时移世易，江山还是那个江山，王者却不是那个王者了。<br/><br/>　　1999年5月，微软历史上一个重要的操作系统Windows 98 SE (即Win98第二版)发布了，里面同样采取捆绑政策。新的IE浏览器被"完美地"融合到操作系统里，从此没有人能够将IE跟Windows分开，此时 IE一路高歌猛进，版本已经升级到5.0了。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie50_win98se_2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie50_win98se_2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 5.0界面截图<br/><br/>　　上一个千年过去了，我们迈入了一个新的世代。公元十六世纪先知Nostradamus(诺斯特拉达姆)所描述的世界大毁灭没有来临，但对于 Netscape浏览器的创造者来说，他们看到的却是被IE毁灭之后的满目沧夷，现在Netscape已经无力再阻挡微软IE前进的步伐了，谁叫 Windows和IE都是微软开发的呢？<br/><br/>　　2000年2月呱呱坠地的Windows 2000更是为这场大战做了几乎完美的收尾，它已经预先捆绑了IE5.0。而在2000年7月发布的增加了更多CSS属性支持的IE5.5，则被集成到微软千年纪念版操作系统Windows Millennium Edition (ME)里面。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie55_winme.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie55_winme.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>界面漂亮了一点，且IE 5.5的安全性比5.0更胜一筹<br/><br/>　　IE5.5版并没有增加太多的先进技术，但在安全性上面有了新的尝试。它将一些补丁和升级文件整合在一块，可以提供更好的浏览安全体验。而对于Cookies的管理方面，如果某些网站企图在Cookies上做手脚而追踪用户的浏览习惯，它就会提醒用户注意。<br/><br/>　　另外IE5.5还支持了DHTML、SMIL等网页标准。而打印方面则第一次对预览打印进行支持。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie55_winme_2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie55_winme_2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 5.5的“收藏”夹，比以前多了不少默认的链接<br/><br/>　　<strong>IE6.0系列诞生的故事</strong> [<a href="http://dl.pconline.com.cn/html/1/1/dlid=1581&dltypeid=1&pn=0&linkPage=1.html" target="_blank">点击下载</a>]<br/>　<br/>　　2001年10月，IE6.0正式版发布。Internet Explorer 6 包括许多崭新和增强的功能，既可以帮助维护个人信息的隐秘性，又可以简化在 Web 上执行的日常工作。对日常的用户来说，最明显的改变，就是新颖的按钮，色彩丰富的菜单背景和工具栏了，它完美地融入了Windows XP的设计风格。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie60_winxp.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie60_winxp.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 6.0中的“关于”，有没发现界面变得更漂亮了？<br/><br/>　　IE6.0还为正在 World-Wide Web Consortium (W3C) 开发同步多媒体集成语言 (SMIL) 2.0 工作草稿的工作提供连续性支持，尤其在网页过渡方面，它允许应用筛选和效果，如图像渐变、使用擦除效果在文字或多媒体元素之间的过渡、对某个元素应用分级颜色背景，所有这些都在指定的时间内完成，而无需编写脚本。<br/><br/>　　微软还为IE6.0引进一组与使用鼠标轮有关的新事件。这些事件使内容或应用程序能够更好地响应用户输入。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie60_winxp_2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie60_winxp_2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>IE 6.0界面截图，融合了XP的风格<br/><br/>　　IE6是和Windows XP一起诞生的，这个卓越的操作系统解决了很多Win2000下的问题，但并没有因为IE6而获得太多的助力，IE在Windows XP里扮演了一个默默无闻的角色，似乎微软已经将研发的力度减少，功能逐渐趋于平淡。<br/><br/>　　2002年9月，IE6.0 SP1发布，这个版本仅仅是对安全漏洞进行了修补，没有动过大手术。另外2003年5月AOL与微软的反垄断官司终于结束，Netscape浏览器的开发被搁置并打发走了大部分的工作人员，可以说，历史上最惨烈的第一次浏览器大战已经基本结束，微软IE凭借着自身强大的财力物力，以不惜一切的代价叩响了新时代的大门。<br/><br/>　　但微软IE近年前进的步伐也开始放慢。各位看客可以掰开手指数一下，从2002年到2004年，IE再无任何新动作，是研发方面遇到了瓶颈，还是因为已将Netscape浏览器一脚踢到历史的角落里去，从此高枕无忧而不思进取了呢？笔者倾向于后者。<br/><br/>　　而且不止笔者这样想，连国外的一些开发社区的程序员们也是颇有微词了。从2003年微软中断了一个叫做麦金塔的浏览器开发，也恰恰证实了这点猜测。时至今日，关于IE的漏洞和黑客、病毒技术的危害已经不是什么新闻了，我们每天上网都要顶着"网络钓鱼"、"URL欺骗"、"黑客窃密"等等的风险，紧张地盯着Windows Updata是否有动静，随时准备下载最新的补丁应付黑客和病毒，我们不禁要问一声：IE你怎么啦？<br/><br/>　　也许是迫于压力，微软终于在2004年的8月推出Windows XP SP2，这个超级补丁包终于带给了我们IE用户颇大的惊喜，它对IE新增加的功能包括支持ActiveX插件屏蔽、网页广告的阻挡、管理加载项功能等，这是不是预示着微软终于再次从沉睡中醒来，要重新执掌互联网之牛耳呢？<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie6sp1.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie6sp1.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>WinXP SP2赋予了IE管理加载项功能<br/><br/>　　<strong>后IE时代：挑战IE霸主之位的众多后起之秀！</strong><br/><br/>　　天道循环，报应不爽。看到微软IE还拖着臃肿的身躯在互联网宽带中冲浪，遭受着日益严重的黑客攻击与病毒侵害时。新一代的弄潮儿已经开始为打倒IE作准备了，其中诞生了不少我们耳熟能详的新星，我们摘取两个重要的软件进行说明。<br/><br/>　　<strong>Mozilla FireFox(火狐)浏览器</strong> [<a href="http://dl.pconline.com.cn/html/1/2/dlid=8832&dltypeid=1&pn=0&.html" target="_blank">点击下载</a>]<br/>　　<br/>　　优裕的生活让人腐败，微软IE没有牺牲在与Netscape交战的炮火中，却差点倒在Netscape的子嗣手里。为什么这样说呢？因为当年AOL将所有Netscape的浏览器原始代码都送给了开源社群自行开发，同时协助他们成立了非盈利性的组织Mozilla Foundation（Mozilla基金会），它就是现在Molilla Firefox浏览器的管理和推进组织，从此FireFox成了削弱IE市场份额的一只重要棋子，Netscape的借尸还魂术宣告成功。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_Firefox.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_Firefox.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>FireFox现在可是越来越“火”了<br/><br/>　　虽然几年前它还是浏览器中的少数派，但如今FireFox已变成了能与IE正面角力的浏览器新秀，它现在的市场占有率看来还是颇为弱势，但随着一次次的版本交替与新功能、新插件的加入，它已经俨然成为新一代浏览器的代名词。<br/><br/>　　从一个使用者的角度来说，用FireFox浏览网页已经不存在太多的障碍，现在几乎所有的大网站已同时支持微软IE的浏览标准和遵循W3C的FireFox浏览标准。既然技术层面上的问题已经解决，那我们使用哪一款浏览器也就取决于它的功能以及安全程度了。<br/><br/>　　传闻：有趣的是Mozilla的名称来自"Mosaic"与日本怪兽哥斯拉的结合，开发人员真是太有幽默感了。<br/><br/>　　<strong>Opera 浏览器</strong> [<a href="http://dl.pconline.com.cn/html/1/1/dlid=121&dltypeid=1&pn=0&.html]点击下载" target="_blank">[/url]<br/><br/>　　1995年当IE 1.0在美国诞生的时候，来自挪威的Opera浏览器也同时降世。虽然后来几乎完全被IE所散发出来的光芒所掩盖，但路遥知马力，这匹老马在近几年已经逐渐散发出它强劲的功力来。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_Opera.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/713ie_Opera.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>老马识途，Opera近年又走出了一片新天地<br/><br/>　　它最大的特点是功能非常的齐全，有着独特的鼠标手势功能(鼠标手势为Opera所发明，后来IE外壳浏览器才采用)，多页面浏览、强大的收藏夹，网页缩放、方便的按钮自定义等等，足以在IE的眼皮底下笑傲江湖了。<br/><br/>　　因为微软的IE强制实行了不遵循W3C协议的奇怪网页浏览标准，所以旧版Opera浏览页面的效果不佳，不过最近Opera也解决了浏览问题，显示效果跟IE相差无几了。而且据说它最强的武功是在网络繁忙的时候可以故意挤掉IE的浏览带宽，是真是假大家可以去尝试一下。<br/><br/>　　<strong>IE的外壳浏览器</strong><br/><br/>　　但凡存活多年的老树总是显得花繁叶茂，围绕主干伸出的旁枝多不胜数。微软IE虽然功能上呈现原地踏步的状况，不过他的追随者却也没有闲着，遂以IE浏览器为核心而开发出多款精彩实用的外壳浏览器，这其中一个佼佼者便为MyIE。[[url=http://www.morequick.com/MyIEGB.zip]下载MyIE简体中文免安装版</a>]<br/><br/>　　Myie的开发步伐并没有停止，开发者又创造了另一个更多功能的浏览器叫做GreenBrowser。[<a href="http://dl.pconline.com.cn/html/1/3/dlid=10563&dltypeid=1&pn=0&.html" target="_blank">下载GreenBrower</a>]<br/>　　<br/>　　后来由Myie而发展出来的Myie2由于性能卓越令互联网掀起了一股多页面浏览器的小高潮，直至Myie2 1.0 版本发布，为了以后能有更大的发展，最终更名为 Maxthon，该名称延续至今。[<a href="http://dl.pconline.com.cn/html/1/8/dlid=7998&dltypeid=1&pn=0&.html" target="_blank">下载Maxthon</a>]<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/050302_maxthon3.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/2005/08/ie/050302_maxthon3.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>Maxthon是IE外壳浏览器中的佼佼者<br/><br/>　　<strong>结语</strong>：<br/>　　2005年是Mosaic浏览器面世的十二周年，也是微软Internet Explorer面世的<span style="color: #FF0000;">十周年</span>。在这短短的十二年间浏览器领域为我们上演了一场让人心潮澎湃，波澜壮阔的攻坚战，昔日的王者已不复当年的勇猛，后来居上的新秀还在争夺互联网的未来。当我们的习惯被IE牢牢掌控的时候，当我们把NCSA Mosaic、Netscape（网景）逐渐淡忘的时候，你会否期待着微软会再次带给我们新的惊喜呢？<br/><br/>　　然而源自Mocaic浏览器的IE代码始终拥有太多的缺陷和漏洞，就象一个年久失修的旧房子，已经不堪重负。再这样下去IE就没有办法与其它代码精炼的浏览器竞争了，与其修修补补不如推倒重来，于是微软有了重新研发新一代浏览器的打算，那就是大家所期待的IE7.0了。<br/><br/>　　我们猜到了开始，但还没猜到结果。下一个十年，继续威风八面的还会是IE吗？<br/>Tags - <a href="http://www.newtyper.com/blog/tags/ie/" rel="tag">ie</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%25B5%258F%25E8%25A7%2588%25E5%2599%25A8/" rel="tag">浏览器</a> , <a href="http://www.newtyper.com/blog/tags/%25E5%258D%2581%25E5%25B9%25B4/" rel="tag">十年</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/15/</link>
<title><![CDATA[Vista的第一次亲密接触]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Fri, 05 Aug 2005 03:12:05 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/15/</guid> 
<description>
<![CDATA[ 
	昨日晚上花了4个半小时后终于把microsoft的新品Vista Beta1落户自家电脑上。Vista并没有给我太大的惊喜，觉得还是延续以前Win系统的格局，或者我还探索的不够……笑……硬件方面基本满意都认出来了，但是仍旧有SATA Raid、电视卡和摄像头三样无法认出，后2者是在我预料之中的但是SATA Raid在无法认出之中是我没想到的，看样子microsoft还需要努力。软件方面，我家机器支持64位所以我安装的是64位版本的Vista，但是很另人满意的是我以前常用的32位软件都能在Vista上正常运作，当然杀毒软件我还没装。其他嘛还没怎么细看，这里就先截几张图给大家欣赏下。<br/> <br/>首先当然是微软的特色右下的版本号和我的电脑的属性<br/><a href="http://newtyper.com/hznter/upload/blog/up/vista006.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista006.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><a href="http://newtyper.com/hznter/upload/blog/up/vista001.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista001.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a> <br/>淡雅的登陆界面和越来越像苹果的界面<br/><a href="http://newtyper.com/hznter/upload/blog/up/vista_start.JPG" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista_start.JPG" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><a href="http://newtyper.com/hznter/upload/blog/up/vista002.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista002.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a> <br/>让我不太习惯的开始菜单和我的电脑<br/><a href="http://newtyper.com/hznter/upload/blog/up/vista005.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista005.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><a href="http://newtyper.com/hznter/upload/blog/up/vista003.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista003.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a> <br/>IE7…………没有什么太大的新喜<br/><a href="http://newtyper.com/hznter/upload/blog/up/vista004.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista004.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a> <br/>嗯……还是打上笑脸男人吧AIR的画册目录，微软的预览得到了加强，个人觉得还是不错的<br/><a href="http://newtyper.com/hznter/upload/blog/up/vista007.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/vista007.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/windows/" rel="tag">windows</a> , <a href="http://www.newtyper.com/blog/tags/vista/" rel="tag">vista</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%25B5%258B%25E8%25AF%2595%25E7%2589%2588/" rel="tag">测试版</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/23/</link>
<title><![CDATA[CSS opacity]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 11 Jul 2005 05:24:47 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/23/</guid> 
<description>
<![CDATA[ 
	From <a href="http://blog.timetide.net/archives/2004/06/18/20040618094448.php" target="_blank">http://blog.timetide.net/a...</a><br/>By Kevin Wen<br/><br/>前几天一位做网页设计的朋友问我这个问题：如何通过CSS来实现图片半透明效果，并且在IE和Mozilla上都可以得到支持。下面将我的方法分享给大家。<br/><br/>这个效果在IE和Mozilla浏览器上都可以工作，代码如下<br/>代码<br/><div class="code">&lt;img alt=&quot;powerbookg4.jpg&quot; src=&quot;archives/images/powerbookg4.jpg&quot; width=&quot;250&quot; height=&quot;60&quot; style=&quot;-moz-opacity:0.5; filter:alpha(opacity=50);cursor:hand;&quot; onmouseover=&quot;this.style.MozOpacity=1;<br/>this.filters.alpha.opacity=100&quot; onmouseout=&quot;this.style.MozOpacity=0.5;<br/>this.filters.alpha.opacity=50&quot;&gt;</div><br/><br/><br/>在IE中需要通过"filter"来定义透明度"opacity"，而在Mozilla中是可以直接解析"opacity",所以如果要使得这个效果在两种浏览器中都得到支持，需要把两种设定都加进去。针对IE的设定：this.filters.alpha.opacity=50　而针对 Mozilla的设定：this.style.MozOpacity=0.5.　大家可以直接用这行代码给图片定义，只须修改图片地址就能实现上图效果。<br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E7%25BD%2591%25E9%25A1%25B5/" rel="tag">网页</a> , <a href="http://www.newtyper.com/blog/tags/css/" rel="tag">css</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%258A%2580%25E5%25B7%25A7/" rel="tag">技巧</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/29/</link>
<title><![CDATA[About 3G]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Tue, 05 Jul 2005 16:03:11 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/29/</guid> 
<description>
<![CDATA[ 
	3G真的在走近我们，我现在的工作能让我跟快的了解到中国3G的发展，前2天就已经见到了国内还没上市的Nokia WCDMA GSM双模手机，今天终于借来小拍一番。不过我还是忘了它的型号了<br/><a href="http://newtyper.com/hznter/upload/blog/up/3G1.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/3G1.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0" width="90" height="120"/></a><a href="http://newtyper.com/hznter/upload/blog/up/3G2.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/3G2.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0" width="90" height="120"/></a><a href="http://newtyper.com/hznter/upload/blog/up/3G3.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/3G3.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0" width="120" height="90"/></a><br/><br/> &nbsp; &nbsp; &nbsp;注意看哦，由于某些原因我不能详细说明下图的意思，嘿嘿，但是只要国家一发3G牌照，我们就能享受3G给我带来的新的移动体验了！<br/><a href="http://newtyper.com/hznter/upload/blog/up/3G4.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/3G4.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/3g/" rel="tag">3g</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/61/</link>
<title><![CDATA[鼠标滚轮缩放图片脚本]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 21 Mar 2005 03:15:44 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/61/</guid> 
<description>
<![CDATA[ 
	可以限制缩放的大小，可以双击图片回复到原始大小。<br/><br/><div class="code">在HEAD添加<br/>&lt;script language=&quot;javascript&quot;&gt;<br/>var count = 10;<br/>function resizeimg(oImage)<br/>&#123;<br/>count = Counting(count);<br/>Resize(oImage,count);<br/>return false;<br/>&#125;<br/>function Counting(newzoom)&#123;<br/>if (event.wheelDelta &gt;= 120)<br/>newzoom++;<br/>else if (event.wheelDelta &lt;= -120)<br/>newzoom--;<br/>if (newzoom&lt;2) newzoom=2; ////只允许缩小到20%<br/>if (newzoom&gt;50) newzoom=50; ////只允许放大到500%<br/>return newzoom;<br/>&#125;<br/>function Resize(oImage,newzoom)&#123;<br/>oImage.style.zoom = newzoom + &#039;0%&#039;;<br/>count=newzoom;<br/>&#125;<br/>&lt;/script&gt;<br/><br/>然后在&lt;img src=&quot;&quot;&gt;中加入<br/>onDblClick=&quot;return Resize(this,10);return false;&quot;<br/>onmousewheel=&quot;return resizeimg(this)&quot;</div><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E6%25BB%259A%25E8%25BD%25AE/" rel="tag">滚轮</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25BC%25A9%25E6%2594%25BE/" rel="tag">缩放</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/62/</link>
<title><![CDATA[Gmail从入门到精通v1.1 （逻辑修改版）]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 21 Mar 2005 02:14:03 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/62/</guid> 
<description>
<![CDATA[ 
	Gmail从入门到精通v1.1 <br/>Gmail从入门到精通v1.1（来自<a href="http://www.softang.com" target="_blank">http://www.softang.com</a>） <br/>经过本人少量修改<br/>什么是Gmail？ <br/>Gmail是由著名搜索引擎Google推出的免费邮箱服务（<a href="https://www.gmail.com" target="_blank">https://www.gmail.com</a>）。 <br/>Gmail的登陆地址？ <br/><br/><a href="http://www.gamil.com" target="_blank">http://www.gamil.com</a> <br/>是Gmail的主页地址，但使用<a href="https://www.gmail.com" target="_blank">https://www.gmail.com</a> <br/><br/>可以得到更快的访问速度。 <br/>Gmail是免费的吗？ <br/>是的。Gmail是完全免费的。但目前Gmail没有开放公开申请，而采用邀请的方式。Gmail申请方法请参考"Gmail申请全攻略"。 <br/>Gmai有广告吗？ <br/>Gmail包括文字广告，但不包括图片广告和弹出广告。 <br/>Gmail的容量？ <br/>Gmail包括1G（即1000M）的邮箱空间。 <br/><br/>如何得到Gmail邀请？ <br/>由于目前Gmail还处于测试阶段而无法公开接受用户注册，并有限度地发放测试账号（只能通过已注册用户的邀请才能加入），使得许多网友无法一睹Gmail的迷人风采。有鉴于此，一些拥有邀请资格的Gmail用户纷纷伸出援手让他人一偿所愿，下面就来看看有什么途径可以让我们获得Gmail账号。　<br/>1.从Blogger中取得 <br/><br/><a href="http://www.blogger.com" target="_blank">www.blogger.com</a> <br/><br/>这个网站已经被Google收购，因此Google会不定期给当中的"积极分子"发放Gmail的测试邀请，只要你在上面注册成功并坚持每天发布一个以上的消息贴，连续10天到1个月，如果运气好的话，当你登录blogger的时候就会在屏幕右侧发现如下内容："Want Gmail?As an active Blogger user, we would like to invite you to try out Google's new email service, Would you like to give it a whirl? "(如图1），直接点击"YES，PLEASE"，然后再按照有关要求填写资料即可。 <br/>2.等待别人的邀请 <br/>如果你的朋友已经在Gmail上成功注册并取到了邀请资格（登录Gmail后在搜索按钮下方能看到一行以红色显示的字符："Invite a friend to join Gmail!"，如图2），则点击打开链接再填写你的Email地址后发送到你的邮箱就可以取得注册Gmail的资格。假如无法从朋友那里得到邀请，我们还可以尝试到一些大型论坛上去碰碰运气。下面就提供几个Gmail的申请链接： <br/>软件堂论坛Gmail申请专贴： <br/><br/><a href="http://www.softang.com/viewthread.php?tid=906" target="_blank">http://www.softang.com/vie...</a> <br/><br/><br/>博客中国： <br/><br/><a href="http://forum.blogchina.com/viewforum.php?f=73&sid=98c479805e97e8c6a7443ff37d3e49de" target="_blank">http://forum.blogchina.com...</a><br/><br/><br/>水木清华： <br/><br/><a href="http://www.smth.org/bbsdoc.php?board=GmailSwap" target="_blank">http://www.smth.org/bbsdoc...</a> <br/><br/><br/>GmailSwap交换： <br/><br/><a href="http://www.gmailswap.com" target="_blank">http://www.gmailswap.com</a> <br/><br/>提示：并不是每个Gmail账号都拥有邀请资格，而且即使得到邀请资格，在数量上也会有所差异。我从实际应用中发现，成为Gmail注册用户的时间越靠前，其获得邀请资格的数量越多，而且其派生功能也越强（即得到其邀请而注册后的用户也很容易得到同样的权利，只是数量上要少得多）。 <br/>如何接受别人的Gmail邀请 <br/>如果你收到一封主题为"XXX has invited you to open a Google mail account"邮件时，你只要按照信中提供的链接来输入个人信息就能拥有这个梦寐以求的帐号了。此外，当你拥有Gmail的测试帐号后，你也有机会邀请你的好友加入测试的行列（登陆后在屏幕的左下角，也就是Labels的下面会有"Invite a Friend"的链接提示，每个人的邀请名额都不同，名额用完后邀请链接会自动消失）。 <br/>Gmail申请全攻略<br/><a href="http://newtyper.com/hznter/upload/blog/up/gmail03.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/gmail03.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><a href="http://newtyper.com/hznter/upload/blog/up/gmail04.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/gmail04.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><a href="http://newtyper.com/hznter/upload/blog/up/gmail05.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/gmail05.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/><br/>Gmail的图片签名 <br/>访问 <br/><br/><a href="http://iridium.is-a-geek.com/test/GMail/" target="_blank">http://iridium.is-a-geek.c...</a> <br/><br/>，然后填上你的ID即可生成属于你的Gmail图片签名。 <br/>还有一个是 <br/><br/><a href="http://www.obsidian-blade.co.uk/gsig/gmail.php/yourname" target="_blank">http://www.obsidian-blade....</a> <br/><br/>，只要将yourname替换成你的ID也可生成图片， <br/>另外的地址是： <br/><br/><a href="http://www.nhacks.com/gmail/" target="_blank">http://www.nhacks.com/gmai...</a> <br/><br/>检验Gmail的ID是否可用 <br/><br/><a href="http://gmail.vlab.info/" target="_blank">http://gmail.vlab.info/</a> <br/><br/>，虽然Google禁止了大部分第三方网站来检验ID是否可用，但这个是一个漏网之鱼。 <br/>例如： <br/><br/><a href="http://www.obsidian-blade.co.uk/gsig/gmail.php/xiaotd" target="_blank">http://www.obsidian-blade....</a> <br/><br/>Gmail另类插件大全 <br/>官方出品费用全免的Gmail Notifier <br/>Google Gmail官方推出的用来检测Gmail新到邮件软件，能够及时的收到Gmail邮箱的邮件。 <br/>下载地址： <br/><br/><a href="http://toolbar.google.com/gmail-helper/" target="_blank">http://toolbar.google.com/...</a> <br/><br/>主页地址： <br/><br/><a href="http://toolbar.google.com/gmail-helper/" target="_blank">http://toolbar.google.com/...</a> <br/><br/>用于Apple MAC机器下的Gmail检测程序GmailStatus <br/>下载地址： <br/><br/><a href="http://homepage.mac.com/carsten.guenther/GmailStatus.dmg.gz" target="_blank">http://homepage.mac.com/ca...</a> <br/><br/>主页地址： <br/><br/><a href="http://homepage.mac.com/carsten.guenther/GmailStatus/" target="_blank">http://homepage.mac.com/ca...</a> <br/><br/>支持Mozilla&Firefox浏览器的GMail Notifier <br/>一款可以Mozilla&Firefox浏览器中做Gmail邮件检测的小软件。 <br/>下载地址： <br/><br/><a href="http://www.nexgenmedia.net/extensions/gm-notifier/gm-notifier-0_3_3.xpi" target="_blank">http://www.nexgenmedia.net...</a> <br/><br/>主页地址： <br/><br/><a href="http://www.nexgenmedia.net/extensions/" target="_blank">http://www.nexgenmedia.net...</a> <br/><br/>能够同时阻击垃圾邮件的Gmail邮件检测软件Pop Peeper <br/>除了支持Gmail外，Pop Peeper还同时支持POP3、Hotmail、MSN、Yahoo、Mail.com、MyWay、Excite、Lycos.com及RediffMail等邮箱， <br/>下载地址： <br/><br/><a href="http://www.poppeeper.com/data/POPPeeper-Install.exe" target="_blank">http://www.poppeeper.com/d...</a> <br/><br/>主页地址： <br/><br/><a href="http://www.poppeeper.com/" target="_blank">http://www.poppeeper.com/</a> <br/><br/>使用手机WAP方式登陆Gmail的gmail-mobile <br/>gmail-mobile是一个PHP的应用程序，用于让你在任何WAP手机上访问自己的Gmail账户。 <br/>下载地址： <br/><br/><a href="http://prdownloads.sourceforge.net/gmail-mobile/gmail-mobile-0.1.tar.gz?download" target="_blank">http://prdownloads.sourcef...</a> <br/><br/><br/>主页地址： <br/><br/><a href="http://sourceforge.net/projects/gmail-mobile/" target="_blank">http://sourceforge.net/pro...</a> <br/><br/>完全免费且开放源码的的Gmail邮件检测程序 <br/>KCheckGmail是一个开放源码的Gmail邮件检测程序，以systray形式驻留在系统状态栏。 <br/>下载地址： <br/><br/><a href="http://prdownloads.sourceforge.net/kcheckgmail/kcheckgmail-0.3.0.tar.bz2?download" target="_blank">http://prdownloads.sourcef...</a> <br/><br/><br/>主页地址： <br/><br/><a href="http://sourceforge.net/projects/kcheckgmail/" target="_blank">http://sourceforge.net/pro...</a> <br/><br/><br/>超级简单的Gmail检测程序Gnotifier <br/>这是一个超级简单的Gmail检测工具。 <br/>下载地址： <br/><br/><a href="http://prdownloads.sourceforge.net/gnotifier/GNotifier.exe?use_mirror=switch" target="_blank">http://prdownloads.sourcef...</a><br/><br/>主页地址： <br/><br/><a href="http://sourceforge.net/projects/gnotifier/" target="_blank">http://sourceforge.net/pro...</a> <br/><br/><br/>如何使用客户端Pop3方式收取Gmail？ （Gmail官方已经支持POP3和SMTP了，详情请自行访问Gmail了解）<br/>使用Gmail却不能使用Pop3方式用OE、Foxmail等客户端收取邮件，不免显得有点遗憾，好在还有些Gmail的志愿者编写了用于收取Gmail邮箱的软件，下面就介绍其中两款： <br/>Pop Goes the Gmail（PGtGM） <br/>PGtGM (Pop Goes the Gmail) 是一个类似于通过pop3来收取Hotmail邮件的小软件，能够让使用Gmail的用户同样能够通过pop3来收取Gmail邮件。这个软件通过扮演本地邮件服务器的角色，来让你的邮件客户端连接。这个软件能够连接到你的Gmail，下载邮件并转换邮件为Outlook等所使用的格式。PGtGM (Pop Goes the Gmail)是个能够将你的webmail下载到桌面的软件，并且完全免费。 <br/>主页地址： <br/><br/><a href="http://jaybe.org/info.htm" target="_blank">http://jaybe.org/info.htm</a> <br/><br/>下载地址： <br/><br/><a href="http://jaybe.org/pgtgm/pgtgm.zip" target="_blank">http://jaybe.org/pgtgm/pgt...</a> <br/><br/>支持Gmail，同时支持多个另类邮箱的FreePOPs <br/>FreePOPs是一个集成了POP3后台程序、LUA解译器和其他一些额外的HTTP及HTML分解库的实用工具，目前的最新版本是0.0.10版。现在FreePOPs有Windows、Linux和Mac OS三大版本，它的主要作用是将本地POP3请求转换为远程HTTP活动。结合其他插件使用之后，FreePOPs可以实现对包括Gmail在内的多个Web电子邮箱的客户端收信， <br/>主页地址： <br/><br/><a href="http://freepops.sf.net/" target="_blank">http://freepops.sf.net/</a> <br/><br/>下载地址： <br/><br/><a href="http://prdownloads.sourceforge.net/freepops/FreePOPs-0.0.10-SSL.exe?download" target="_blank">http://prdownloads.sourcef...</a><br/><br/>Gmail技巧大全 <br/>1、GMail Acc的id是不计算 "." 的，所以可以 a.b@gmail, ab@gmail 都是同一个邮箱。 <br/>2、它內建 filter 跟 label (webmail 有 label真少见到), 所以可以用 + 来帮助设置 <br/>filter, 如 ab+list1@gmail 跟 ab+list2@gmail 都可以送到 ab@gmail, 然而 filter <br/>写一下就會把 +list1 跟 +list2 分开了。 <br/>比如对应label1,可以写From :id1@xxx.com To ab+ <br/><a href="mailto:list1@gmail.com" target="_blank"> <br/>list1@gmail.com <br/></a> <br/>这样，通过filter的 <br/>设置 ，所有来自 <br/><a href="mailto:id1@xxx.com" target="_blank"> <br/>id1@xxx.com <br/></a> <br/>的邮件，会自动分拣到label中。 <br/>3、GMail是全 UTF-8 的系統，所以基本上它可正常处理多个国家的语言，但是有时候发过 <br/>去的信件可能有乱码，比如yahoo的邮箱，收到的是乱码，但是只要点击IE/查看/编码/选择 <br/>UTF-8编码，就可以正常显示了。 <br/>4、gmail的search暂时只支持英文，其他语言无效，相信未来会支持的更好一些。 <br/>5、GMail 目前是只认识 server "收到信的时间" 而非 Mail body claim 的时间。 这会有 <br/>什么问题呢？ 有些人沒有 auto forward 的功能，所以会几天固定 去其它信箱手动转信到 <br/>Gmail, 日期就全变一样了。 <br/>6、GMail 的 'conversation' 平常都是一起动作的，如果要删除其中的一些单个邮件是很 <br/>麻烦的，不过有个办法，可以拆开来，点击 More options, 选择Trash this messenger就 <br/>ok了，就不会影响到整个conversation了。 <br/>7、用yahoo,hotmail收到gmail邀请函的，注意在bulk,spam里面查看邀请函，呵呵 <br/>8、如何删除信件。我是这样操作的：选中邮件，将它们Move to Trash，然后呢，到Trash中，选择Delete Forever，就可以把信件永久删除了。 <br/>９、如何多选信件。很多email信箱都有提供一个复选框，用来一次选择叶面中的所有信件。在Gmail中，没有类似的复选框。不过它的复选操作更加简单。先选择地一封信，然后，按住Shift的同时，点击你要选的最后一封信，则自动多选了。 <br/>１０、自动关联的地址栏。在Gmail中写信，地址栏中用户无需填写email的全部，如果你要联系的人的email在你的通讯录中，那么，只需要打入他的姓名就可以了（细心的人应该会发现地址栏下面会自动出现提示），比如，打入Zhengxiaoyun，就会自动寄发给我。 <br/>１１、可以分化出多个mail，比如 zhengxiaoyun+baby，我在Gmail中设置过滤，凡是寄给zhengxiaoyun+baby的信全部转入baby的标签（labels，事先设置好）内。我们平常要和很多不同的人联系，可以给不同的人、网站、单位留不同形式的用户名（yourname+构造的别名），实现自动分拣的功能。 <br/>１２、所有发送、收取的email都会被自动列入contacts中，用户可以进一步到该页面编辑。 <br/>１３、如何把老的archive导进gmail: <br/>如果是outlook的.pst, 用mozilla import成mbox，使用gml (Gmail Loader: <br/><br/><a href="http://www.marklyon.org/gmail/)" target="_blank">http://www.marklyon.org/gm...</a> <br/><br/>，不过mozilla import的mbox它不认，要用becky import, 再export一遍，如果直接是unix mbox, 就直接gml，Gmail特色之一：快捷键操作 Gmail提供了很方便的的快捷键操作方式，和鼠标操作相比，可以节省不少时间。一个小小的改变，就能让界面具有很好的亲和力。 <br/>C--------书写新邮件， <br/>K---------移动到更新的对话中，如果是在inbox页面，它的作用实际是向上移动光标，也就是移动到更新的邮件。如果需要察看该邮件，回车好了（也可以用O键）。 <br/>J----------和K相反，它是移动到更早的对话中。 <br/>N，P------在邮件对话线程中移动。当我们打开一个包含多个邮件的对话的时候，可以使用N向下（更新的），也可以使用P向上（更早的）移动光标，如果需要打开光标所在的邮件，敲回车键。 <br/>U----------从对话的查看页面返回到对话列表页面。 <br/>Y----------把邮件或对话存档。 <br/><br/>X----------选中对话（高亮显示，复选框打勾） <br/>S----------给对话挂颗星星，比如可以挂上星星作为提示。 <br/>!-----------把该邮件报告为垃圾信件。 <br/>R----------回信。 <br/>A----------全部回复。 <br/>F-----------转发。 <br/>组合键： <br/>G+I-----------进入收件箱 <br/>G+S----------打开标记为星星的对话/邮件列表页面。 <br/>G+A----------进入所有信件列表页面。 <br/>如何去除邮件上的标签 <br/>要去除邮件的标签，必须先点击标签链接，进入该标签中，此时，会看到上方的按钮变成了remove labels "xxxx"，这时候，选中需要移除标签的邮件，之后点击该按钮即可。 <br/>如何删除单独的信件 <br/>Gmail分别为对话和单独的信件提供了删除功能，前者是通过扔倒垃圾箱（Trash）之后，在用旧删除。而后者，却需要用户足够细心才能发现。具体操作如下，先是点击删除邮件所在的对话，然后展开它，这时候应该能看到more options链接，点开它，就能看到删除该消息的链接了，它只删除一个消息，不会影响到对话中的其他信件。 <br/>如何导入地址到联系人列表？ <br/>从你的其他Webmail供应商及Email客户端软件导出地址薄为CVS文件。 <br/>登陆Gmail邮箱和点击页面顶部的"Contacts联系人"。会打开一个联系人列表的新窗口。点击"Import Contacts导入联系人" <br/>点击"浏览"，选择你的本地的CSV文件进行上传。 <br/>选择文件和点击"Import Contacts导入联系人"。上传完成文档后，会有一个对话框显示你联系人列表的新数目。<br/>Tags - <a href="http://www.newtyper.com/blog/tags/gmail/" rel="tag">gmail</a> , <a href="http://www.newtyper.com/blog/tags/%25E4%25BB%258B%25E7%25BB%258D/" rel="tag">介绍</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/63/</link>
<title><![CDATA[可在Google网站注册!Gmail申请放宽限制]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 21 Mar 2005 02:00:29 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/63/</guid> 
<description>
<![CDATA[ 
	根据国外网站(<a href="http://www.Infoworld.com)" target="_blank">www.Infoworld.com)</a>报道，Google日前放宽了其Web邮件服务Gmail的申请限制。从该网站提供的截图显示，普通用户已经可以通过访问Google网站并在网站指导下完成Gmail帐号的申请。而在此之前，想要注册Gmail帐号，用户必需通过Gmail的使用者的邀请才能获得。<br/>而Google公司的Web产品经理Marissa Mayer也证实：于周一开始每20个访问Google.com网站的用户中将有一位用户会得到申请Gmail帐户的机会。她还表示希望通过Google的广大用户群来扩展Gmail的用户范围。<br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/050317gmail-invite.jpg" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/050317gmail-invite.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>与以往不同的Google主页，在下方随机出现获得Gmail的注册链接<br/><br/><br/><a href="http://newtyper.com/hznter/upload/blog/up/050317gmail02_thumb.gif" target="_blank"><img src="http://newtyper.com/hznter/upload/blog/up/050317gmail02_thumb.gif" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>帐户申请页面<br/><br/>出处：<a href="http://www.pconline.com.cn/pcedu/softnews/yejie/0503/576210.html" target="_blank">http://www.pconline.com.cn...</a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/gmail/" rel="tag">gmail</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%2594%25B3%25E8%25AF%25B7/" rel="tag">申请</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/65/</link>
<title><![CDATA[FireFox的新标签插件～]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Sat, 19 Mar 2005 18:02:40 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/65/</guid> 
<description>
<![CDATA[ 
	最新版本tab mix 0.1.3 简体中文正式版<br/><br/>0.1.3 正式版<br/><br/>新增：新视窗链结被强制开在新分页的分页切换设定。<br/>修正：復原关闭分页的功能改进。<br/>修正：多重分页列的功能改进。<br/>其他许许多多小错误的修正。<br/><br/>0.1.3 Release Candidate 2<br/><br/>新增：“保护分页”与“锁定分页”可以从页面的右键选单啟动与关闭。<br/>修正：Javascript 的错误不再发生了!<br/>修正：“强制不同网域的链结开在新分页”功能。（感谢 Stuart Colville）<br/>修正：新的“保护分页”及“锁定分页”的状态图示。（感谢 ReinekeFux）<br/>修正：多重分页列的分页关闭按钮位置。<br/>修正：分页标籤上的下载进度显示方式。<br/>其他许许多多小错误的修正。<br/><br/>0.1.3 beta ~ 0.1.3 Release Candidate<br/><br/>多重分页列的显示改进：可设最多列数、只有一列时分页宽度会自动调整。<br/>新增“书籤群组”的开啟设定。<br/>新增“锁定分页”功能（含进阶设定中的“锁定所有分页”）。<br/>新增“保护分页”功能，被保护的分页将不会被关闭。<br/>新增“强制不同网域的链结开在新分页”功能。（感谢 Stuart Colville）<br/>新增视窗内容右键选单中关于“復原关闭分页”的选项。<br/>新增“工具”选单中的“分页列表”功能。<br/>新增“关闭最后一个分页时不关闭视窗”之功能。<br/>新增“Shift 点击分页”的动作，而“保护分页”及“锁定分页”也可由滑鼠的点击来达成。<br/>新增分页上显示“保护分页”及“锁定分页”的状态图示。<br/>新增各分页列表上的网站图示。<br/>修正“復原关闭分页”的功能。<br/>修正 bookmarklet 开新分页的问题。<br/>其他许许多多小错误的修正。<br/><br/><br/>0.1.3 beta ~ 0.1.3 beta-4<br/><br/>多重分页列的显示改进：可设最多列数、只有一列时分页宽度会自动调整。<br/>新增“书籤群组”的开啟设定。<br/>新增“锁定分页”功能（含进阶设定中的“锁定所有分页”）。<br/>新增“保护分页”功能，被保护的分页将不会被关闭。<br/>新增“强制不同网域的链结开在新分页”功能。<br/>新增视窗内容右键选单中关于“復原关闭分页”的选项。<br/>新增“工具”选单中的“分页列表”功能。<br/>新增“关闭最后一个分页时不关闭视窗”之功能。<br/>“復原关闭分页”功能的改进。<br/>各式的分页列表加上网站图示。<br/>其他许许多多小错误的修正。<br/><br/>0.1.3 beta<br/><br/>多重分页列的显示改进：最多显示三列、超过三列可捲动、只有一列时分页宽度会自动调整。<br/>新增“锁定分页”功能（含进阶设定中的“锁定所有分页”）。<br/>新增视窗内容右键选单中关于“復原关闭分页”的选项。<br/>新增“工具”选单中的“分页列表”功能。<br/>新增“关闭最后一个分页时不关闭视窗”之功能。<br/>“復原关闭分页”功能的改进。<br/>其他许许多多小错误的修正。<br/>0.1.2a<br/><br/>修正“復原关闭分页”几点严重的错误。<br/>修正与 Clone Window 套件的一个冲突。<br/>0.1.2<br/><br/>新增右键选单的设定。<br/>新增“復原关闭分页”的列表。<br/>新增“关闭右侧分页”及“关闭左侧分页”的功能。<br/>新增网页内容允许权的设定。<br/>按 Ctrl 加滑鼠左键可得到与滑鼠中键同样的效果。<br/>在分页工具列上留出空间，以方便拖曳与点击。<br/>标示未读分页。<br/>“復原关闭分页”功能的改进。<br/><br/>0.1.1<br/><br/>新增“復原被关闭的分页”功能。<br/>在“进阶设定”中新增“在背景开啟被导致新分页的视窗”设定。<br/>“开啟分页在目前分页旁边”新增“改变分页开啟顺序”的设定。<br/>关闭分页后的选择分页焦点错误已经修正，即使被关闭的分页未完成下载。<br/>右键选单中加上“复製分页”项目。<br/>还有许多小错误的修正。<br/><br/>下载页面：<a href="http://www.firefox.net.cn/newforum/viewtopic.php?t=2290" target="_blank">http://www.firefox.net.cn/...</a><br/>Tags - <a href="http://www.newtyper.com/blog/tags/firefox/" rel="tag">firefox</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%25A0%2587%25E7%25AD%25BE/" rel="tag">标签</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%258F%2592%25E4%25BB%25B6/" rel="tag">插件</a>
]]>
</description>
</item>
</channel>
</rss>