﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-cyberfan's blog-文章分类-java</title><link>http://www.cnitblog.com/cyberfan/category/571.html</link><description>正其谊不谋其利，明其道不计其功</description><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 19:50:48 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 19:50:48 GMT</pubDate><ttl>60</ttl><item><title>运用加密技术保护Java源代码</title><link>http://www.cnitblog.com/cyberfan/articles/1636.html</link><dc:creator>cyberfan</dc:creator><author>cyberfan</author><pubDate>Fri, 12 Aug 2005 08:37:00 GMT</pubDate><guid>http://www.cnitblog.com/cyberfan/articles/1636.html</guid><wfw:comment>http://www.cnitblog.com/cyberfan/comments/1636.html</wfw:comment><comments>http://www.cnitblog.com/cyberfan/articles/1636.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/cyberfan/comments/commentRss/1636.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/cyberfan/services/trackbacks/1636.html</trackback:ping><description><![CDATA[为什么要加密？<BR>对于传统的C或C++之类的语言来说，要在Web上保护源代码是很容易的，只要不发布它就可以。遗憾的是，Java程序的源代码很容易被别人偷看。只要有一个反编译器，任何人都可以分析别人的代码。Java的灵活性使得源代码很容易被窃取，但与此同时，它也使通过加密保护代码变得相对容易，我们唯一需要了解的就是Java的ClassLoader对象。当然，在加密过程中，有关Java Cryptography Extension（JCE）的知识也是必不可少的。<BR><BR>有几种技术可以“模糊”Java类文件，使得反编译器处理类文件的效果大打折扣。然而，修改反编译器使之能够处理这些经过模糊处理的类文件并不是什么难事，所以不能简单地依赖模糊技术来保证源代码的安全。<BR><BR>我们可以用流行的加密工具加密应用，比如PGP（Pretty Good Privacy）或GPG（GNU Privacy Guard）。这时，最终用户在运行应用之前必须先进行解密。但解密之后，最终用户就有了一份不加密的类文件，这和事先不进行加密没有什么差别。<BR><BR>Java运行时装入字节码的机制隐含地意味着可以对字节码进行修改。JVM每次装入类文件时都需要一个称为ClassLoader的对象，这个对象负责把新的类装入正在运行的JVM。JVM给ClassLoader一个包含了待装入类（比如java.lang.Object）名字的字符串，然后由ClassLoader负责找到类文件，装入原始数据，并把它转换成一个Class对象。<BR><BR>我们可以通过定制ClassLoader，在类文件执行之前修改它。这种技术的应用非常广泛――在这里，它的用途是在类文件装入之时进行解密，因此可以看成是一种即时解密器。由于解密后的字节码文件永远不会保存到文件系统，所以窃密者很难得到解密后的代码。<BR><BR>由于把原始字节码转换成Class对象的过程完全由系统负责，所以创建定制ClassLoader对象其实并不困难，只需先获得原始数据，接着就可以进行包含解密在内的任何转换。<BR><BR>Java 2在一定程度上简化了定制ClassLoader的构建。在Java 2中，loadClass的缺省实现仍旧负责处理所有必需的步骤，但为了顾及各种定制的类装入过程，它还调用一个新的findClass方法。<BR><BR>这为我们编写定制的ClassLoader提供了一条捷径，减少了麻烦：只需覆盖findClass，而不是覆盖loadClass。这种方法避免了重复所有装入器必需执行的公共步骤，因为这一切由loadClass负责。<BR><BR>不过，本文的定制ClassLoader并不使用这种方法。原因很简单。如果由默认的ClassLoader先寻找经过加密的类文件，它可以找到；但由于类文件已经加密，所以它不会认可这个类文件，装入过程将失败。因此，我们必须自己实现loadClass，稍微增加了一些工作量。<BR><BR>定制类装入器<BR>每一个运行着的JVM已经拥有一个ClassLoader。这个默认的ClassLoader根据CLASSPATH环境变量的值，在本地文件系统中寻找合适的字节码文件。<BR><BR>应用定制ClassLoader要求对这个过程有较为深入的认识。我们首先必须创建一个定制ClassLoader类的实例，然后显式地要求它装入另外一个类。这就强制JVM把该类以及所有它所需要的类关联到定制的ClassLoader。Listing 1显示了如何用定制ClassLoader装入类文件。<BR><BR>【Listing 1：利用定制的ClassLoader装入类文件】<BR><BR>// 首先创建一个ClassLoader对象<BR>ClassLoader myClassLoader = new myClassLoader();<BR><BR>// 利用定制ClassLoader对象装入类文件<BR>// 并把它转换成Class对象<BR>Class myClass = myClassLoader.loadClass( "mypackage.MyClass" );<BR><BR>// 最后，创建该类的一个实例<BR>Object newInstance = myClass.newInstance();<BR><BR>// 注意，MyClass所需要的所有其他类，都将通过<BR>// 定制的ClassLoader自动装入<BR><BR><BR><BR>如前所述，定制ClassLoader只需先获取类文件的数据，然后把字节码传递给运行时系统，由后者完成余下的任务。<BR><BR>ClassLoader有几个重要的方法。创建定制的ClassLoader时，我们只需覆盖其中的一个，即loadClass，提供获取原始类文件数据的代码。这个方法有两个参数：类的名字，以及一个表示JVM是否要求解析类名字的标记（即是否同时装入有依赖关系的类）。如果这个标记是true，我们只需在返回JVM之前调用resolveClass。<BR><BR>【Listing 2：ClassLoader.loadClass()的一个简单实现】<BR><BR>public Class loadClass( String name, boolean resolve )<BR>throws ClassNotFoundException {<BR>try {<BR>// 我们要创建的Class对象<BR>Class clasz = null;<BR><BR>// 必需的步骤1：如果类已经在系统缓冲之中，<BR>// 我们不必再次装入它<BR>clasz = findLoadedClass( name );<BR><BR>if (clasz != null)<BR>return clasz;<BR><BR>// 下面是定制部分<BR>byte classData[] = /* 通过某种方法获取字节码数据 */;<BR>if (classData != null) {<BR>// 成功读取字节码数据，现在把它转换成一个Class对象<BR>clasz = defineClass( name, classData, 0, classData.length );<BR>}<BR><BR>// 必需的步骤2：如果上面没有成功，<BR>// 我们尝试用默认的ClassLoader装入它<BR>if (clasz == null)<BR>clasz = findSystemClass( name );<BR><BR>// 必需的步骤3：如有必要，则装入相关的类<BR>if (resolve &amp;&amp; clasz != null)<BR>resolveClass( clasz );<BR><BR>// 把类返回给调用者<BR>return clasz;<BR><BR>} catch( IOException ie ) {<BR>throw new ClassNotFoundException( ie.toString() );<BR>} catch( GeneralSecurityException gse ) {<BR>throw new ClassNotFoundException( gse.toString() );<BR>}<BR>}<BR><BR><BR><BR>Listing 2显示了一个简单的loadClass实现。代码中的大部分对所有ClassLoader对象来说都一样，但有一小部分（已通过注释标记）是特有的。在处理过程中，ClassLoader对象要用到其他几个辅助方法：<BR><BR>findLoadedClass：用来进行检查，以便确认被请求的类当前还不存在。loadClass方法应该首先调用它。 <BR>defineClass：获得原始类文件字节码数据之后，调用defineClass把它转换成一个Class对象。任何loadClass实现都必须调用这个方法。 <BR>findSystemClass：提供默认ClassLoader的支持。如果用来寻找类的定制方法不能找到指定的类（或者有意地不用定制方法），则可以调用该方法尝试默认的装入方式。这是很有用的，特别是从普通的JAR文件装入标准Java类时。 <BR>resolveClass：当JVM想要装入的不仅包括指定的类，而且还包括该类引用的所有其他类时，它会把loadClass的resolve参数设置成true。这时，我们必须在返回刚刚装入的Class对象给调用者之前调用resolveClass。 <BR><BR>加密、解密<BR>Java加密扩展即Java Cryptography Extension，简称JCE。它是Sun的加密服务软件，包含了加密和密匙生成功能。JCE是JCA（Java Cryptography Architecture）的一种扩展。<BR><BR>JCE没有规定具体的加密算法，但提供了一个框架，加密算法的具体实现可以作为服务提供者加入。除了JCE框架之外，JCE软件包还包含了SunJCE服务提供者，其中包括许多有用的加密算法，比如DES（Data Encryption Standard）和Blowfish。<BR><BR>为简单计，在本文中我们将用DES算法加密和解密字节码。下面是用JCE加密和解密数据必须遵循的基本步骤：<BR><BR>步骤1：生成一个安全密匙。在加密或解密任何数据之前需要有一个密匙。密匙是随同被加密的应用一起发布的一小段数据，Listing 3显示了如何生成一个密匙。 【Listing 3：生成一个密匙】<BR><BR>// DES算法要求有一个可信任的随机数源<BR>SecureRandom sr = new SecureRandom();<BR><BR>// 为我们选择的DES算法生成一个KeyGenerator对象<BR>KeyGenerator kg = KeyGenerator.getInstance( "DES" );<BR>kg.init( sr );<BR><BR>// 生成密匙<BR>SecretKey key = kg.generateKey();<BR><BR>// 获取密匙数据<BR>byte rawKeyData[] = key.getEncoded();<BR><BR>/* 接下来就可以用密匙进行加密或解密，或者把它保存<BR>为文件供以后使用 */<BR>doSomething( rawKeyData );<BR><BR><BR><BR>步骤2：加密数据。得到密匙之后，接下来就可以用它加密数据。除了解密的ClassLoader之外，一般还要有一个加密待发布应用的独立程序（见Listing 4）。 【Listing 4：用密匙加密原始数据】<BR><BR>// DES算法要求有一个可信任的随机数源<BR>SecureRandom sr = new SecureRandom();<BR><BR>byte rawKeyData[] = /* 用某种方法获得密匙数据 */;<BR><BR>// 从原始密匙数据创建DESKeySpec对象<BR>DESKeySpec dks = new DESKeySpec( rawKeyData );<BR><BR>// 创建一个密匙工厂，然后用它把DESKeySpec转换成<BR>// 一个SecretKey对象<BR>SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );<BR>SecretKey key = keyFactory.generateSecret( dks );<BR><BR>// Cipher对象实际完成加密操作<BR>Cipher cipher = Cipher.getInstance( "DES" );<BR><BR>// 用密匙初始化Cipher对象<BR>cipher.init( Cipher.ENCRYPT_MODE, key, sr );<BR><BR>// 现在，获取数据并加密<BR>byte data[] = /* 用某种方法获取数据 */<BR><BR>// 正式执行加密操作<BR>byte encryptedData[] = cipher.doFinal( data );<BR><BR>// 进一步处理加密后的数据<BR>doSomething( encryptedData );<BR><BR><BR>步骤3：解密数据。运行经过加密的应用时，ClassLoader分析并解密类文件。操作步骤如Listing 5所示。 【Listing 5：用密匙解密数据】<BR><BR>// DES算法要求有一个可信任的随机数源<BR>SecureRandom sr = new SecureRandom();<BR><BR>byte rawKeyData[] = /* 用某种方法获取原始密匙数据 */;<BR><BR>// 从原始密匙数据创建一个DESKeySpec对象<BR>DESKeySpec dks = new DESKeySpec( rawKeyData );<BR><BR>// 创建一个密匙工厂，然后用它把DESKeySpec对象转换成<BR>// 一个SecretKey对象<BR>SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );<BR>SecretKey key = keyFactory.generateSecret( dks );<BR><BR>// Cipher对象实际完成解密操作<BR>Cipher cipher = Cipher.getInstance( "DES" );<BR><BR>// 用密匙初始化Cipher对象<BR>cipher.init( Cipher.DECRYPT_MODE, key, sr );<BR><BR>// 现在，获取数据并解密<BR>byte encryptedData[] = /* 获得经过加密的数据 */<BR><BR>// 正式执行解密操作<BR>byte decryptedData[] = cipher.doFinal( encryptedData );<BR><BR>// 进一步处理解密后的数据<BR>doSomething( decryptedData );<BR><BR><BR>应用实例<BR>前面介绍了如何加密和解密数据。要部署一个经过加密的应用，步骤如下：<BR><BR>步骤1：创建应用。我们的例子包含一个App主类，两个辅助类（分别称为Foo和Bar）。这个应用没有什么实际功用，但只要我们能够加密这个应用，加密其他应用也就不在话下。 <BR>步骤2：生成一个安全密匙。在命令行，利用GenerateKey工具（参见GenerateKey.java）把密匙写入一个文件： % java GenerateKey key.data<BR><BR><BR>步骤3：加密应用。在命令行，利用EncryptClasses工具（参见EncryptClasses.java）加密应用的类： % java EncryptClasses key.data App.class Foo.class Bar.class<BR><BR>该命令把每一个.class文件替换成它们各自的加密版本。 <BR><BR>步骤4：运行经过加密的应用。用户通过一个DecryptStart程序运行经过加密的应用。DecryptStart程序如Listing 6所示。 【Listing 6：DecryptStart.java，启动被加密应用的程序】<BR><BR>import java.io.*;<BR>import java.security.*;<BR>import java.lang.reflect.*;<BR>import javax.crypto.*;<BR>import javax.crypto.spec.*;<BR><BR>public class DecryptStart extends ClassLoader<BR>{<BR>// 这些对象在构造函数中设置，<BR>// 以后loadClass()方法将利用它们解密类<BR>private SecretKey key;<BR>private Cipher cipher;<BR><BR>// 构造函数：设置解密所需要的对象<BR>public DecryptStart( SecretKey key ) throws GeneralSecurityException,<BR>IOException {<BR>this.key = key;<BR><BR>String algorithm = "DES";<BR>SecureRandom sr = new SecureRandom();<BR>System.err.println( "[DecryptStart: creating cipher]" );<BR>cipher = Cipher.getInstance( algorithm );<BR>cipher.init( Cipher.DECRYPT_MODE, key, sr );<BR>}<BR><BR>// main过程：我们要在这里读入密匙，创建DecryptStart的<BR>// 实例，它就是我们的定制ClassLoader。<BR>// 设置好ClassLoader以后，我们用它装入应用实例，<BR>// 最后，我们通过Java Reflection API调用应用实例的main方法<BR>static public void main( String args[] ) throws Exception {<BR>String keyFilename = args[0];<BR>String appName = args[1];<BR><BR>// 这些是传递给应用本身的参数<BR>String realArgs[] = new String[args.length-2];<BR>System.arraycopy( args, 2, realArgs, 0, args.length-2 );<BR><BR>// 读取密匙<BR>System.err.println( "[DecryptStart: reading key]" );<BR>byte rawKey[] = Util.readFile( keyFilename );<BR>DESKeySpec dks = new DESKeySpec( rawKey );<BR>SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );<BR>SecretKey key = keyFactory.generateSecret( dks );<BR><BR>// 创建解密的ClassLoader<BR>DecryptStart dr = new DecryptStart( key );<BR><BR>// 创建应用主类的一个实例<BR>// 通过ClassLoader装入它<BR>System.err.println( "[DecryptStart: loading "+appName+"]" );<BR>Class clasz = dr.loadClass( appName );<BR><BR>// 最后，通过Reflection API调用应用实例<BR>// 的main()方法<BR><BR>// 获取一个对main()的引用<BR>String proto[] = new String[1];<BR>Class mainArgs[] = { (new String[1]).getClass() };<BR>Method main = clasz.getMethod( "main", mainArgs );<BR><BR>// 创建一个包含main()方法参数的数组<BR>Object argsArray[] = { realArgs };<BR>System.err.println( "[DecryptStart: running "+appName+".main()]" );<BR><BR>// 调用main()<BR>main.invoke( null, argsArray );<BR>}<BR><BR>public Class loadClass( String name, boolean resolve )<BR>throws ClassNotFoundException {<BR>try {<BR>// 我们要创建的Class对象<BR>Class clasz = null;<BR><BR>// 必需的步骤1：如果类已经在系统缓冲之中<BR>// 我们不必再次装入它<BR>clasz = findLoadedClass( name );<BR><BR>if (clasz != null)<BR>return clasz;<BR><BR>// 下面是定制部分<BR>try {<BR>// 读取经过加密的类文件<BR>byte classData[] = Util.readFile( name+".class" );<BR><BR>if (classData != null) {<BR>// 解密...<BR>byte decryptedClassData[] = cipher.doFinal( classData );<BR><BR>// ... 再把它转换成一个类<BR>clasz = defineClass( name, decryptedClassData,<BR>0, decryptedClassData.length );<BR>System.err.println( "[DecryptStart: decrypting class "+name+"]" );<BR>}<BR>} catch( FileNotFoundException fnfe ) {<BR>}<BR><BR>// 必需的步骤2：如果上面没有成功<BR>// 我们尝试用默认的ClassLoader装入它<BR>if (clasz == null)<BR>clasz = findSystemClass( name );<BR><BR>// 必需的步骤3：如有必要，则装入相关的类<BR>if (resolve &amp;&amp; clasz != null)<BR>resolveClass( clasz );<BR><BR>// 把类返回给调用者<BR>return clasz;<BR>} catch( IOException ie ) {<BR>throw new ClassNotFoundException( ie.toString()<BR>);<BR>} catch( GeneralSecurityException gse ) {<BR>throw new ClassNotFoundException( gse.toString()<BR>);<BR>}<BR>}<BR>}<BR><BR>对于未经加密的应用，正常执行方式如下： % java App arg0 arg1 arg2<BR><BR>对于经过加密的应用，则相应的运行方式为： % java DecryptStart key.data App arg0 arg1 arg2<BR><BR><BR>DecryptStart有两个目的。一个DecryptStart的实例就是一个实施即时解密操作的定制ClassLoader；同时，DecryptStart还包含一个main过程，它创建解密器实例并用它装入和运行应用。示例应用App的代码包含在App.java、Foo.java和Bar.java内。Util.java是一个文件I/O工具，本文示例多处用到了它。完整的代码请从本文最后下载。<BR><BR>注意事项<BR>我们看到，要在不修改源代码的情况下加密一个Java应用是很容易的。不过，世上没有完全安全的系统。本文的加密方式提供了一定程度的源代码保护，但对某些攻击来说它是脆弱的。<BR><BR>虽然应用本身经过了加密，但启动程序DecryptStart没有加密。攻击者可以反编译启动程序并修改它，把解密后的类文件保存到磁盘。降低这种风险的办法之一是对启动程序进行高质量的模糊处理。或者，启动程序也可以采用直接编译成机器语言的代码，使得启动程序具有传统执行文件格式的安全性。<BR><BR>另外还要记住的是，大多数JVM本身并不安全。狡猾的黑客可能会修改JVM，从ClassLoader之外获取解密后的代码并保存到磁盘，从而绕过本文的加密技术。Java没有为此提供真正有效的补救措施。<BR><BR>不过应该指出的是，所有这些可能的攻击都有一个前提，这就是攻击者可以得到密匙。如果没有密匙，应用的安全性就完全取决于加密算法的安全性。虽然这种保护代码的方法称不上十全十美，但它仍不失为一种保护知识产权和敏感用户数据的有效方案。<BR><BR>参考资料 <BR><BR>在运行时刻更新功能模块。介绍了一个利用类库加载器ClassLoader 实现在运行时刻更新部分功能模块的Java程序，并将其与C/C++中实现同样功能的动态链接库方案进行了比较。 <BR><BR>Java 技巧 105：利用 JWhich 掌握类路径。展示一个简单的工具，它可以清楚地确定类装载器从类路径中载入了什么 Java 类。 <BR><BR>要了解更多的 Java 安全信息，请阅读 java.sun.com的 Java Security API 页。 <BR><BR>如何封锁您的（或打开别人的） Java 代码。Java 代码反编译和模糊处理的指南。 <BR><BR>使您的软件运行起来：摆弄数字。真正安全的软件需要精确的随机数生成器。 <BR><BR>下载本文代码： EncryptedJavaClass_code.zip <BR><BR>关于作者<BR>俞良松，软件工程师，独立顾问和自由撰稿人。最初从事PB和Oracle开发，现主要兴趣在于Internet开发。您可以通过 javaman@163.net 和我联系。 <img src ="http://www.cnitblog.com/cyberfan/aggbug/1636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/cyberfan/" target="_blank">cyberfan</a> 2005-08-12 16:37 <a href="http://www.cnitblog.com/cyberfan/articles/1636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>制作可执行的JAR文件包及jar命令详解</title><link>http://www.cnitblog.com/cyberfan/articles/1634.html</link><dc:creator>cyberfan</dc:creator><author>cyberfan</author><pubDate>Fri, 12 Aug 2005 08:36:00 GMT</pubDate><guid>http://www.cnitblog.com/cyberfan/articles/1634.html</guid><wfw:comment>http://www.cnitblog.com/cyberfan/comments/1634.html</wfw:comment><comments>http://www.cnitblog.com/cyberfan/articles/1634.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/cyberfan/comments/commentRss/1634.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/cyberfan/services/trackbacks/1634.html</trackback:ping><description><![CDATA[常常在网上看到有人询问：如何把 java 程序编译成 .exe 文件。通常回答只有两种，一种是制作一个可执行的 JAR 文件包，然后就可以像.chm 文档一样双击运行了；而另一种是使用 JET 来进行 <BR>编译。但是 JET 是要用钱买的，而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件，性能也要打些折扣。所以，使用制作可执行 JAR 文件包的方法就是最佳选择了，何况它还能保持 Java 的跨平台特性。 <BR><BR>下面就来看看什么是 JAR 文件包吧： <BR><BR>1. JAR 文件包 <BR><BR>JAR 文件就是 Java Archive File，顾名思意，它的应用是与 Java 息息相关的，是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说，它就是 ZIP 文件，所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中，包含了一个 META-INF/MANIFEST.MF 文件，这个文件是在生成 JAR 文件的时候自动创建的。举个例子，如果我们具有如下目录结构的一些文件： <BR><BR>　　== <BR><BR>　　`-- test <BR><BR>　　　 `-- Test.class <BR><BR>把它压缩成 ZIP 文件 test.zip，则这个 ZIP 文件的内部目录结构为： <BR><BR>　　test.zip <BR><BR>　　`-- test <BR><BR>　　　 `-- Test.class <BR><BR>如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar，则这个 JAR 文件的内部目录结构为： <BR><BR>　　test.jar <BR><BR>　　|-- META-INF <BR><BR>　　|　 `-- MANIFEST.MF <BR><BR>　　`-- test <BR><BR>　　　　`--Test.class <BR><BR>2. 创建可执行的 JAR 文件包 <BR><BR>制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。 <BR><BR>Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放；运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数；运行时还要到控制台下去使用 java 命令来运行，如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此，许多人说，Java 是一种方便开发者苦了用户的程序设计语言。 <BR><BR>其实不然，如果开发者能够制作一个可执行的 JAR 文件包交给用户，那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候，安装文件会将 .jar 文件映射给 javaw.exe 打开。那么，对于一个可执行的 JAR 文件包，用户只需要双击它就可以运行程序了，和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么，现在的关键，就是如何来创建这个可执行的 JAR 文件包。 <BR><BR>创建可执行的 JAR 文件包，需要使用带 cvfm 参数的 jar 命令，同样以上述 test 目录为例，命令如下： <BR><BR>jar cvfm test.jar manifest.mf test <BR><BR>这里 test.jar 和 manifest.mf 两个文件，分别是对应的参数 f 和 m，其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包，光靠指定一个 manifest.mf 文件是不够的，因为 MANIFEST 是 JAR 文件包的特征，可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST，其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下： <BR><BR>Main-Class: 可执行主类全名(包含包名) <BR><BR>例如，假设上例中的 Test.class 是属于 test 包的，而且是可执行的类 (定义了 public static void main(String[]) 方法)，那么这个 manifest.mf 可以编辑如下： <BR><BR>Main-Class: test.Test &lt;回车&gt; <BR><BR>这个 manifest.mf 可以放在任何位置，也可以是其它的文件名，只需要有 Main-Class: test.Test 一行，且该行以一个回车符结束即可。创建了 manifest.mf 文件之后，我们的目录结构变为： <BR><BR>　　== <BR><BR>　　|-- test <BR><BR>　　|　 `-- Test.class <BR><BR>　　`-- manifest.mf <BR><BR>这时候，需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中，使用如下命令： <BR><BR>jar cvfm test.jar manifest.mf test <BR><BR>之后在“==”目录中创建了 test.jar，这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。 <BR><BR>需要注意的是，创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构，就像上例一样。而 Main-Class 指定的类，也必须是完整的、包含包路径的类名，如上例的 test.Test；而且在没有打成 JAR 文件包之前可以使用 java &lt;类名&gt; 来运行这个类，即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。 <BR><BR>3. jar 命令详解 <BR><BR>jar 是随 JDK 安装的，在 JDK 安装目录下的 bin 目录中，Windows 下文件名为 jar.exe，Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做，因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。 <BR><BR>使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下： <BR><BR>jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... <BR><BR>其中 {ctxu} 是 jar 命令的子命令，每次 jar 命令只能包含 ctxu 中的一个，它们分别表示： <BR><BR>-c　创建新的 JAR 文件包 <BR><BR>-t　列出 JAR 文件包的内容列表 <BR><BR>-x　展开 JAR 文件包的指定文件或者所有文件 <BR><BR>-u　更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中) <BR><BR>[vfm0M] 中的选项可以任选，也可以不选，它们是 jar 命令的选项参数 <BR><BR>-v　生成详细报告并打印到标准输出 <BR><BR>-f　指定 JAR 文件名，通常这个参数是必须的 <BR><BR>-m　指定需要包含的 MANIFEST 清单文件 <BR><BR>-0　只存储，不压缩，这样产生的 JAR 文件包会比不用该参数产生的体积大，但速度更快 <BR><BR>-M　不产生所有项的清单（MANIFEST〕文件，此参数会忽略 -m 参数 <BR><BR>[jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包，它是 -f 参数的附属参数 <BR><BR>[manifest-文件] 即 MANIFEST 清单文件，它是 -m 参数的附属参数 <BR><BR>[-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令，它只能在创建和更新 JAR 文件包的时候可用。　　 <BR><BR>文件名 ... 指定一个文件/目录列表，这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录，那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。 <BR><BR>下面举一些例子来说明 jar 命令的用法： <BR><BR>1) jar cf test.jar test <BR><BR>该命令没有执行过程的显示，执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar，那么该文件将被覆盖。 <BR><BR>2) jar cvf test.jar test <BR><BR>该命令与上例中的结果相同，但是由于 v 参数的作用，显示出了打包过程，如下： <BR><BR>标明清单(manifest) <BR><BR>增加：test/(读入= 0) (写出= 0)(存储了 0%) <BR><BR>增加：test/Test.class(读入= 7) (写出= 6)(压缩了 14%) <BR><BR>3) jar cvfM test.jar test <BR><BR>该命令与 2) 结果类似，但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件，打包过程的信息也略有差别： <BR><BR>增加：test/(读入= 0) (写出= 0)(存储了 0%) <BR><BR>增加：test/Test.class(读入= 7) (写出= 6)(压缩了 14%) <BR><BR>4) jar cvfm test.jar manifest.mf test <BR><BR>运行结果与 2) 相似，显示信息也相同，只是生成 JAR 包中的 META-INF/MANIFEST 内容不同，是包含了 manifest.mf 的内容 <BR><BR>5) jar tf test.jar <BR><BR>在 test.jar 已经存在的情况下，可以查看 test.jar 中的内容，如对于 2) 和 3) 生成的 test.jar 分别应该此命令，结果如下； <BR><BR>对于 2) <BR><BR>META-INF/ <BR><BR>META-INF/MANIFEST.MF <BR><BR>test/ <BR><BR>test/Test.class <BR><BR>对于 3) <BR><BR>test/ <BR><BR>test/Test.class <BR><BR>6) jar tvf test.jar <BR><BR>除显示 5) 中显示的内容外，还包括包内文件的详细信息，如： <BR><BR>0 Wed Jun 19 15:39:06 GMT 2002 META-INF/ <BR><BR>86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF <BR><BR>0 Wed Jun 19 15:33:04 GMT 2002 test/ <BR><BR>7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class <BR><BR>7) jar xf test.jar <BR><BR>解开 test.jar 到当前目录，不显示任何信息，对于 2) 生成的 test.jar，解开后的目录结构如下： <BR><BR>　　== <BR><BR>　　|-- META-INF <BR><BR>　　|　 `-- MANIFEST <BR><BR>　　`-- test <BR><BR>　　　　`--Test.class <BR><BR>8) jar xvf test.jar <BR><BR>运行结果与 7) 相同，对于解压过程有详细信息显示，如： <BR><BR>创建：META-INF/ <BR><BR>展开：META-INF/MANIFEST.MF <BR><BR>创建：test/ <BR><BR>展开：test/Test.class <BR><BR>9) jar uf test.jar manifest.mf <BR><BR>在 test.jar 中添加了文件 manifest.mf，此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下，如果使用 -m 参数并指定 manifest.mf 文件，那么 manifest.mf 是作为清单文件 MANIFEST 来使用的，它的内容会被添加到 MANIFEST 中；但是，如果作为一般文件添加到 JAR 文件包中，它跟一般文件无异。 <BR><BR>10) jar uvf test.jar manifest.mf <BR><BR>与 9) 结果相同，同时有详细信息显示，如： <BR><BR>增加：manifest.mf(读入= 17) (写出= 19)(压缩了 -11%) <BR><BR>4. 关于 JAR 文件包的一些技巧 <BR><BR>1) 使用 unzip 来解压 JAR 文件 <BR><BR>在介绍 JAR 文件的时候就已经说过了，JAR 文件实际上就是 ZIP 文件，所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件，如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观，方便。而使用 unzip，则是因为它解压时可以使用 -d 参数指定目标目录。 <BR><BR>在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的，因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下，再进行解压，比较麻烦。如果使用 unzip，就不需要这么麻烦了，只需要指定一个 -d 参数即可。如： <BR><BR>unzip test.jar -d dest/ <BR><BR>2) 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件 <BR><BR>上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件，所以，只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包，再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况，只需要将这个 MANIFEST 按需要修改即可。 <BR><BR>3) 使用 jar 命令创建 ZIP 文件 <BR><BR>有些 Linux 下提供了 unzip 命令，但没有 zip 命令，所以需要可以对 ZIP 文件进行解压，即不能创建 ZIP 文件。如要创建一个 ZIP 文件，使用带 -M 参数的 jar 命令即可，因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单，那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名，创建的就是一个不折不扣的 ZIP 文件了，如将上一节的第 3) 个例子略作改动： <BR><BR>jar cvfM test.zip test <img src ="http://www.cnitblog.com/cyberfan/aggbug/1634.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/cyberfan/" target="_blank">cyberfan</a> 2005-08-12 16:36 <a href="http://www.cnitblog.com/cyberfan/articles/1634.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何封锁您的（或打开别人的） Java 代码</title><link>http://www.cnitblog.com/cyberfan/articles/1635.html</link><dc:creator>cyberfan</dc:creator><author>cyberfan</author><pubDate>Fri, 12 Aug 2005 08:36:00 GMT</pubDate><guid>http://www.cnitblog.com/cyberfan/articles/1635.html</guid><wfw:comment>http://www.cnitblog.com/cyberfan/comments/1635.html</wfw:comment><comments>http://www.cnitblog.com/cyberfan/articles/1635.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/cyberfan/comments/commentRss/1635.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/cyberfan/services/trackbacks/1635.html</trackback:ping><description><![CDATA[没有比发现一个错误，却没有源代码就不能修改更令人沮丧的了。正是这个原因导致了 Java 反编译器的出现，它可以把编译后的字节码完全转回成源代码。尽管代码反编译器不只是针对 Java 语言，但它从来没有象在 Java 开发人员中那样被公开地或广泛地使用。<BR><BR>与反编译针锋相对的是模糊处理。假设反编译人员能很容易从编译后的代码中设法得到源代码，那么要保护您的代码和有价值的技术秘密就不是那么简单了。随着 Java 反编译器的普遍使用， Java 模糊处理器也同样被普及，它的作用就好像放一块烟幕在您的代码前面。反编译和模糊处理在商业开发领域中引起了一场争论 -- 争论中的大部分都集中在了 Java 语言上。<BR><BR>在本文中，我将让您了解代码反编译和模糊处理的具体过程，讨论在这两种技术之后的理论问题，同时简要地谈到它们在商业编程领域中所引起的争论。我还将介绍一些比较有名的反编译器和模糊处理器（有商业的，也有开放源代码的），并随着文章的深入使用它们来创建一些实例。<BR><BR>什么是反编译？<BR>反编译是一个将目标代码转换成源代码的过程。这应该很清楚了，因为编译是一个将源代码转换成目标代码的过程。但什么是目标代码呢？大体上的定义是：目标代码是一种用语言表示的代码，这种语言能通过实机或虚拟机直接执行。对于象 C 这样的语言，目标代码通常运行在硬件 CPU 上，而 Java 目标代码通常运行在虚拟机上。<BR><BR>反编译是困难的<BR>正如以上所描述的，反编译听上去比较简单，但它实际上是非常困难的 -- 从本质上说，它所包含的是根据小规模、低层次的行为来推断大规模、高层次的行为。为了对此有个直观的理解，我们把一个计算机程序看作是一个复杂的公司组织结构。高层管理人员向他们的下属下达类似“最大程度地提高技术生产能力”的命令，下属们再把这些命令转变成更具体的行动，例如安装新的 XML 数据库。<BR><BR>作为该公司的新雇员，您可能会问下属他或她在做些什么，并得到回答，“我在安装新的 XML 数据库。”从这句话中，您不可能推断出其最终目的是最大程度地提高技术生产能力。毕竟，最终目标不尽相同，例如可能是分离供应链或累积消费者的数据。<BR><BR>然而，如果属于好奇心特强的那类人，您可能会再多问几个问题，并让公司中不同级别的下属回答您的问题。最后，当把所有的答案汇总后，您可能会猜到企业更大的目标是最大程度地提高技术生产能力。<BR><BR>如果您把计算机程序的工作方式看作类似一个公司的组织结构，那么对于为什么反编译代码不是无关紧要的，以上的这个比方就会给你一个直接的感受。从比较理论化的角度来看，这儿要引用在该领域的杰出研究员 Cristina Cifuentes 对反编译过程的描述：<BR><BR>任何一个二进制改造工程都需要对存储在二进制文件中的代码进行反汇编。从理论上说，分离 von Neumann 上的数据和代码就好象停机问题，因此完全的静态翻译是不可能的。然而，实际上可以使用不同技术来提高可被静态翻译的代码的所占比例，或者采取可在运行中被使用的动态翻译技术。 --"Binary Reengineering of Distributed Object Technology"（请参阅 参考资料） <BR><BR>把目标代码转换成源代码并不是反编译时碰到的唯一问题。一个 Java 类文件潜在包含了一些不同类型的信息。知道类文件中可能包含了哪类信息对于了解您如何利用该信息以及对于信息作何种处理都是很重要的。这其实就是 Java 反汇编器所要做的。<BR><BR>反汇编一个类文件<BR>Java 类文件的真正二进制格式不是很重要。重要的是知道在那些字节中包含了哪些不同种类的信息。到了这一步，我们将利用多数 JDK 都带有的一个工具 -- javap。 javap 是一个 Java 代码反汇编器，它和反编译器是不同的。反汇编器把机器可读格式的目标代码（如清单 1 所示）转换成人们可读的代码（如清单 2 所示）。<BR><BR>清单 1. 一个类文件的原始内容<BR><BR>0000000 feca beba 0300 2d00 4200 0008 081f 3400<BR>0000020 0008 073f 2c00 0007 0735 3600 0007 0737<BR>0000040 3800 0007 0a39 0400 1500 000a 0007 0a15<BR>0000060 0800 1600 000a 0008 0a17 0800 1800 0009<BR>...<BR><BR><BR>清单 2. javap 的输出结果<BR><BR>Local variables for method void priv(int)<BR>Foo this pc=0, length=35, slot=0<BR>int argument pc=0, length=35, slot=1<BR><BR>Method void main(java.lang.String[])<BR>0 new #4 <BR>3 invokespecial #10 <BR>6 return<BR><BR><BR><BR>请注意，清单 2 所示的并不是源代码。该清单的第一部分列出了方法的局部变量；第二部分是汇编代码，它也是人们可读的目标代码。<BR><BR>一个类文件中的元素<BR>javap 被用来反汇编或解包一个类文件。这里简要列出了可以通过使用 javap 进行反汇编的 Java 类文件所包含的信息： <BR><BR>成员变量。每个类文件中包含了对应于该类每个数据成员的所有名称信息和类型信息。 <BR><BR>经过反汇编后的方法。类的每一个方法都是由一串虚拟机指令来表示的，并附带它的类型签名。 <BR><BR>行号。每个方法中的每个节被映射到源代码行，在可能的情况下，源代码行来生成节。这使得实时系统和调试器能够为在运行状态的程序提供堆栈跟踪。 <BR><BR>局部变量名一旦方法被编译了，这个方法的局部变量就不太需要名称了，但是能通过对 javac 编译器使用 -g 选项来包含它们。这也使得实时系统和调试器能帮助您。 <BR><BR>既然对 Java 类文件的内部情况已有所了解，让我们看一下如何能转换这些信息来达到我们的目的。<BR><BR>使用反编译器<BR>从概念上讲，反编译器使用起来非常简单。他就是把编译器逆过来用：你给它 .class 文件，它还给你一个源代码文件。<BR><BR>一些比较新的反编译器有精致的图形界面。但在一开始所举的例子中，我们将使用的是 Mocha，它是第一个公开的可利用的反编译器。在本文的最后，我会讨论一下在 GPL 下一个较新的反编译器。（请参阅 参考资料，下载 Mocha 并获取 Java 反编译器的清单。） <BR><BR>让我们假设在目录中有一个名为 Foo.class 的类文件。用 Mocha 对它进行反编译非常简单，只要键入以下命令：<BR><BR>$ java mocha.Decompiler Foo.class<BR><BR><BR><BR>这会生成一个新的名为 Foo.mocha 的文件（Mocha 使用 Foo.mocha 这个名字以避免覆盖原文件的源代码）。这个新文件就是 Java 的源文件，并且假设一切顺利的话，您现在就能正常地编译它。只需把它重命名为 Foo.java 就可以开始了。<BR><BR>但是这儿有个问题：如果在一些您已经有所改动的代码上运行 Mocha，您会注意到它生成的代码和源代码不是完全一样的。我举个例子，这样您能明白我的意思。清单 3 所示的原始源代码是来自一个名为 Foo.java 的测试程序。<BR><BR>清单 3. Foo.java 的一小部分原始源代码<BR><BR>private int member = 10;<BR><BR>public Foo() {<BR>int local = returnInteger();<BR>System.out.println( "foo constructor" );<BR>priv( local );<BR>}<BR><BR><BR><BR>以下是 Mocha 生成的代码<BR><BR>清单 4. Mocha 生成的 Foo.java 的源代码<BR><BR>private int member;<BR><BR>public Foo()<BR>{<BR>member = 10;<BR>int local = returnInteger();<BR>System.out.println("foo constructor");<BR>priv(local);<BR>}<BR><BR><BR><BR>这两个代码片段的成员变量 member 被初始化为 10 的位置不同。在原始源代码中，它在与声明的同一行中被表示为一个初始值，而在被反编译后的源代码中，它在一个构造符中被表示为一条赋值语句。反编译后的代码告诉我们一些有关源代码被编译的方法；即它的初始值是作为在构造符中的赋值来被编译的。通过观察其反编译后的输出结果，您能了解到不少 Java 编译器的工作方法。 <BR><BR>反编译是困难的：不断重复<BR>虽然 Mocha 的确可以反汇编您的目标代码，但它不会总是成功的。由于困难重重，没有一个反编译器能够准确无误地翻译出源代码，而且每个反编译器处理它们在翻译过程中的漏洞的方式也不同。举例来说，Mocha 有时在输出准确的循环构造的结构方面有一些问题。如果真的这样，它会在最终输出中使用伪 goto 语句，如清单 5 所示。 <BR><BR>清单 5. Mocha 不能准确地反编译<BR><BR>if (i1 == i3) goto 214 else 138;<BR>j3 = getSegment(i3).getZOrder();<BR>if (j1 != 1) goto 177 else 154;<BR>if (j3 &gt; k2 &amp;&amp; (!k1 || j3 &lt; j2)) goto 203 else 173;<BR>expression 0<BR>if (j3 &lt; k2 &amp;&amp; (!k1 || j3 &gt; j2)) goto 203 else 196;<BR>expression 0<BR>if == goto 201<BR>continue;<BR>i2 = i3;<BR><BR><BR><BR>撇开 Mocha 的问题不谈，反编译器在通常情况下还是能比较准确地翻译出源代码。一旦知道了某一反编译器的弱点，您可以手工分析和转换反编译后的代码，以使它们能较准确地符合原始源代码。随着反编译器正变得越来越出色，我们又碰到了另外一个问题：如果您不想让任何人能反编译您的代码，那该怎么办呢？<BR><BR>反编译和对安全的威胁<BR>虽然，大部分的代码反编译是完全正大光明的，但事实是一个优秀的反汇编器是软件侵权的必需工具之一。正因如此，尤其对于在商业和不开放源代码领域中的开发人员来说，便宜的（或免费的） Java 代码反汇编工具的存在是一个严重的问题。<BR><BR>就语言本身而言， 由于其相对简单的 Java 虚拟机（与真实的微处理器相比）和其写得很规范的字节码格式， Java 代码非常容易反汇编。而这随着 Java 语言在 Web 开发平台上的日益普及，已经在商业开发领域引起了很多争议。自从 Mocha 于 1996 年首次发布以来，一些在保护它们的源代码方面有过投资的公司和个人一直在为 Java 反编译器大吵大闹。<BR><BR>实际上，当 Mocha 第一次发布时，它的作者 Hanpeter van Vliet 曾被一些公司的诉讼威胁过（请参阅 参考资料）。起初，他把反编译器从他的网站上移去，但是他后来以 Crema 的形式提供了一个更好的解决方案。Crema 是一个 Java 模糊处理器，它完全对立于 Mocha。 <BR><BR>自 Crema 发布以来，许多 Java 模糊处理器开始出现，其中一些是商业的，也有一些是开放源代码的。正如您看到的那样，一个好的 Java 模糊处理器可以在很大程度上保护您的 Java 代码。<BR><BR>针锋相对的代码模糊处理<BR>代码模糊处理字面上的意思就是模糊处理您代码的行为。Java 模糊处理器用不易察觉的方法改变程序，以致于它的 运行对 JVM 来说是一模一样的，但它使得试图理解程序的人更加迷惑了。 <BR><BR>让我们看一下当反汇编器遇到经过模糊处理后的代码会发生什么情况。清单 6 显示了 Mocha 在尝试反汇编被一种名为 jmangle 的工具模糊处理的 Java 代码后的结果。请注意以下的一小段程序和我们在前面清单中使用的是相同的，尽管乍一看，您肯定不会这么认为。<BR><BR>清单 6. 经过 jmangle 模糊处理的代码<BR><BR>public Foo()<BR>{<BR>jm2 = 10;<BR>int i = jm0();<BR>System.out.println("foo constructor");<BR>jm1(i);<BR>}<BR><BR><BR><BR>象 jmangle 这样的模糊处理器把许多变量名和方法名（有时甚至是类名和包的名称）转换成没有意义的字符串。这样就使得人们难以阅读程序，但对于 JVM 来说，其在本质上和原来的程序是一样的。<BR><BR>变得卑鄙<BR>所有的模糊处理器都要使标记变得没有意义，但他们所做的不仅仅是这些。Crema 之所以臭名昭著是因为它用了许多卑鄙的手段来阻止反汇编，并且有许多在已经出现的模糊处理器中，纷纷仿效它。<BR><BR>一种常用的模糊处理代码的方法是用一个非法的字符串来替代类文件中的标记，这比使用没有意义的字符串更进了一步。替代的有可能是一个关键字，例如 private ，或者甚至是象 *** 这样没有意义的标记。一些虚拟机 -- 尤其在浏览器中 -- 对这些古怪的用法不会作出合法的反应。从技术上说，一个象 = 这样的变量与 Java 的规范是相反的；一些虚拟机可以忽略它，而另一些不可以这样。 <BR><BR>Crema 放置炸弹<BR>按字面意思，Crema 使用的另一个计策就是炸弹。Crema 具有完全关闭 Mocha 的能力。它在编译后的代码中添加一个小“炸弹”，导致 Mocha 在试图反编译代码时崩溃。<BR><BR>可惜，Crema 已经没有了，但有一种名为 HoseMocha 的工具是专门为关闭 Mocha 而设计的。为了了解 HoseMocha 是如何工作的，我们将使用 javap，这个值得信赖的反汇编器。清单 7 所示的是 HoseMocha 放置炸弹前的代码。<BR><BR>清单 7. 放置炸弹前的代码<BR><BR>Method void main(java.lang.String[])<BR>0 new #4 <BR>3 invokespecial #10 <BR>6 return<BR><BR><BR><BR>以下是 HoseMocha 处理后的代码。<BR><BR>清单 8. 放置炸弹后的代码<BR><BR>Method void main(java.lang.String[])<BR>0 new #4 <BR>3 invokespecial #10 <BR>6 return<BR>7 pop<BR><BR><BR><BR>您看到那颗炸弹吗？请注意现在这个程序在返回后面有一条 pop 语句。等一下 -- 一个函数在返回之后还能做什么吗？很显然，它不能，而这就是关键所在。在返回语句后放一条指令确保了它不会被执行。您这儿所见的是根本不可能被反汇编的。因为它没有对应任何可能的 Java 源代码，所以也就没有任何意义。<BR><BR>但为什么这一个小小的障碍就能导致 Mocha 崩溃呢？ Mocha 可以只是简单地忽略它，或发一条警告信息并继续下去。尽管 Mocha 对于此类炸弹的脆弱性可以被认为是一个程序错误，但更有可能的是 van Vliet 为了回应对 Mocha 的攻击而故意设置的。<BR><BR>到此为止，我们已经了解了较老的反汇编工具和模糊处理工具 -- 虽然有点过时，但还是比较出色的。但是，类似工具在这几年已经变得更加成熟，尤其在图形界面方面更是如此。在本文的最后，我们看一下一个较新的反汇编器，仅仅让您有个大致的概念。<BR><BR>这一领域的新成员<BR>在过去的五年中，不仅仅是反汇编和模糊处理的技术越来越复杂，而且这些工具的界面也更加华丽。在最近出现的反汇编器中，有几个能让您浏览 .class 文件的目录并且只要单击一下，就能对它们进行反汇编。<BR><BR>JODE （Java 优化和反编译环境）就是这样一个程序。在命令行中键入 .jar 文件的名称， JODE 就会允许您图形化地浏览它的类，并自动反汇编每个类以让您查看。这特别有助于通过 Java SDK 提供的库来查找源代码。简单地键入以下命令：<BR><BR>$ java jode.swingui.Main --classpath [path to your Java SDK]/jre/lib/rt.jar<BR><BR><BR><BR>您就会得到如图 1 所示的对文件的完整翻译。<BR><BR>图 1. JODE: 一种反汇编器<BR><BR><BR>请参阅 参考资料，获取更有用的工具的清单。 <BR><BR>结论<BR>无论选择使用象 Mocha 或 HoseMocha 这样的经典工具，还是乐于亲自研究一下更新的工具，您都应把这篇文章作为您学习 Java 反汇编和模糊处理的起点。在此，请浏览一下在 参考资料中所提供的许多链接，试着使用其中的一些工具，并准备以后不断磨练自己的技术。尽管有许多争议，反汇编和模糊处理的技术如今依然存在，并且在今后的几年中只会变得更加成熟和完善。 <BR><BR>参考资料 <BR><BR>您可以参阅本文在 developerWorks 全球站点上的 英文原文. <BR><BR>虽然 Mocha已经过时，但使用起来要比较有趣，而且偶尔会有一些用处。 <BR><BR>正如在它之前的 Crema， HoseMocha制造一个阻止 Mocha 工作的炸弹。 <BR><BR>Borland 的 JBuilder据说是基于原来 Crema 的代码。 <BR><BR>请查看 jmangle。 <BR><BR>SourceForge 如今拥有 JODE，它可以在 GPL 下获得。 <BR><BR>WingDis是另一个流行的商业反编译器。 <BR><BR>Blackdown 列出了许多在 Linux 上的 Java 开发工具，包括 JAD、它被认为是“最快的 Java 反汇编器”。 <BR><BR>Zelix KlassMaster是一个作为模糊处理器工作的商业类文件查看工具。 <BR><BR>Marc Meurrens 的 Java 代码工程是一个出色的网站，涵盖了汇编器、反汇编器、模糊处理器及相关信息。 <BR><BR>反汇编和反汇编器是另一个包罗万象的页面，有许多软件和研究论文的有用链接。 <BR><BR>Cristina Cifuentes维护着 反汇编页面，有许多有关反汇编的理论和实际工作的信息，包括一个名为 dcc 的 C 反汇编器。 <BR><BR>已故的 Maurice Halstead 被许多人认为是反汇编之父。请阅读有关他从 1960 年至 1976 年领导的 反汇编项目的信息。 <BR><BR>请参阅“ Java 反编译器比较”（JavaWorld，1997 年 7 月），这里有对 Java 反汇编器的大范围回顾。 <BR><BR>请阅读 Hanpeter van Vliet 的 原始反汇编器宣言（Web Techniques，1997 年 9 月）。 <BR><BR>IBM 苏黎世研究实验室在安全性和 Java 加密技术上投入了大量资源。 <BR><BR>请阅读 IBM 是如何研究 Java 安全性和分布式目标系统。 <BR><BR>在 “ 让您的软件运行：模糊安全性” （developerWorks，2000 年 10 月）中，作者 Gary McGraw 和 John Viega 讨论了试图在运行软件时实现保密。 <BR><BR>请不要错过 developerWorks 在这重要领域的 安全性专题。 <BR><BR>McGraw 和 Felten 的经典著作 Securing Java, 2nd Edition (John Wiley &amp; Sons, 1999)的第 6 章有关于 Java 反汇编器的更多信息。 <BR><BR>关于作者<BR>Greg Travis 是一名居住在纽约的自由程序员。他对计算机的兴趣可以追溯到 "The Bionic Woman" 中的这样一段情节，Jamie 试图逃离一幢其灯光和门都被邪恶的人工智能所控制的大楼，而且人工智能还通过扩音器嘲弄她。Greg 坚定地认为当计算机程序工作时，它是完全一致的。可通过 mito@panix.com联系 Greg。 <img src ="http://www.cnitblog.com/cyberfan/aggbug/1635.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/cyberfan/" target="_blank">cyberfan</a> 2005-08-12 16:36 <a href="http://www.cnitblog.com/cyberfan/articles/1635.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java学习从入门到精通</title><link>http://www.cnitblog.com/cyberfan/articles/1633.html</link><dc:creator>cyberfan</dc:creator><author>cyberfan</author><pubDate>Fri, 12 Aug 2005 08:35:00 GMT</pubDate><guid>http://www.cnitblog.com/cyberfan/articles/1633.html</guid><wfw:comment>http://www.cnitblog.com/cyberfan/comments/1633.html</wfw:comment><comments>http://www.cnitblog.com/cyberfan/articles/1633.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cnitblog.com/cyberfan/comments/commentRss/1633.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/cyberfan/services/trackbacks/1633.html</trackback:ping><description><![CDATA[Java Learning Path （一）、工具篇<BR><BR>一、 JDK (Java Development Kit)<BR><BR>JDK是整个Java的核心，包括了Java运行环境（Java Runtime Envirnment），一堆Java工具和Java基础的类库(rt.jar)。不论什么Java应用服务器实质都是内置了某个版本的JDK。因此掌握JDK是学好Java的第一步。最主流的JDK是Sun公司发布的JDK，除了Sun之外，还有很多公司和组织都开发了自己的JDK，例如IBM公司开发的JDK，BEA公司的Jrocket，还有GNU组织开发的JDK等等。其中IBM的JDK包含的JVM（Java Virtual Machine）运行效率要比Sun JDK包含的JVM高出许多。而专门运行在x86平台的Jrocket在服务端运行效率也要比Sun JDK好很多。但不管怎么说，我们还是需要先把Sun JDK掌握好。<BR><BR>1、 JDK的下载和安装<BR>JDK又叫做J2SE（Java2 SDK Standard Edition），可以从Sun的Java网站上下载到，<A title=http://java.sun.com/j2se/downloads.html href="http://java.sun.com/j2se/downloads.html" target=_blank>http://java.sun.com/j2se/downloads.html</A> ，JDK当前最新的版本是J2SDK1.4.2，建议下载该版本的JDK，下载页面在这里：<A title=http://java.sun.com/j2se/1.4.2/download.html。 href="http://java.sun.com/j2se/1.4.2/download.html。" target=_blank>http://java.sun.com/j2se/1.4.2/download.html。</A><BR><BR>下载好的JDK是一个可执行安装程序，默认安装完毕后会在C:\Program Files\Java\目录下安装一套JRE（供浏览器来使用），在C:\j2sdk1.4.2下安装一套JDK（也包括一套JRE）。然后我们需要在环境变量PATH的最前面增加java的路径C:\j2sdk1.4.2\bin。这样JDK就安装好了。<BR><BR>2、 JDK的命令工具<BR>JDK的最重要命令行工具：<BR>java： 启动JVM执行class<BR>javac： Java编译器<BR>jar： Java打包工具<BR>javadoc： Java文档生成器<BR>这些命令行必须要非常非常熟悉，对于每个参数都要很精通才行。对于这些命令的学习，JDK Documentation上有详细的文档。<BR><BR>二、 JDK Documentation<BR><BR>Documentation在JDK的下载页面也有下载连接，建议同时下载Documentation。Documentation是最最重要的编程手册，涵盖了整个Java所有方面的内容的描述。可以这样说，学习Java编程，大部分时间都是花在看这个Documentation上面的。我是随身携带的，写Java代码的时候，随时查看，须臾不离手。<BR><BR>三、 应用服务器(App Server)<BR><BR>App Server是运行Java企业组件的平台，构成了应用软件的主要运行环境。当前主流的App Server是BEA公司的Weblogic Server和IBM公司的Websphere以及免费的Jboss，选择其中一个进行学习就可以了，个人推荐Weblogic，因为它的体系结构更加干净，开发和部署更加方便，是Java企业软件开发人员首选的开发平台。下面简要介绍几种常用的App Server：<BR><BR>1、 Tomcat<BR>Tomcat严格意义上并不是一个真正的App Server，它只是一个可以支持运行Serlvet/JSP的Web容器，不过Tomcat也扩展了一些App Server的功能，如JNDI，数据库连接池，用户事务处理等等。Tomcat被非常广泛的应用在中小规模的Java Web应用中，因此本文做一点下载、安装和配置Tomcat的介绍：<BR><BR>Tomcat是Apache组织下Jakarta项目下的一个子项目，它的主网站是：<A title=http://jakarta.apache.org/tomcat/ href="http://jakarta.apache.org/tomcat/" target=_blank>http://jakarta.apache.org/tomcat/</A> ，Tomcat最新版本是Tomcat4.1.27，软件下载的连接是：<A title=http://www.apache.org/dist/jakarta/tomcat-4/binaries/ href="http://www.apache.org/dist/jakarta/tomcat-4/binaries/" target=_blank>http://www.apache.org/dist/jakarta/tomcat-4/binaries/</A> 。<BR><BR>下载Tomcat既可以直接下载zip包，也可以下载exe安装包（个人建议zip更干净些），不管哪种情况，下载完毕安装好以后（zip直接解压缩就可以了）。需要设置两个环境变量：<BR><BR>JAVA_HOME=C:\j2sdk1.4.2<BR>CATALINA_HOME=D:\tomcat4 (你的Tomcat安装目录)<BR><BR>这样就安装好了，启动Tomcat运行CATALINA_HOME\bin\startup.bat，关闭Tomcat运行shutdown.bat脚本。Tomcat启动以后，默认使用8080端口，因此可以用浏览器访问<A title=http://localhost:8080来测试Tomcat是否正常启动。 href="http://localhost:8080来测试Tomcat是否正常启动。/" target=_blank>http://localhost:8080来测试Tomcat是否正常启动。</A><BR><BR>Tomcat提供了两个Web界面的管理工具，URL分别是：<BR><A title=http://localhost:8080/admin/index.jsp href="http://localhost:8080/admin/index.jsp" target=_blank>http://localhost:8080/admin/index.jsp</A> <BR><A title=http://localhost:8080/manager/html href="http://localhost:8080/manager/html" target=_blank>http://localhost:8080/manager/html</A> <BR>在启用这两个管理工具之前，先需要手工配置一下管理员用户和口令。用一个文本工具打开CATALINA_HOME\conf\tomcat-users.xml这个文件，加入如下几行：<BR><BR>&lt;role rolename="manager"/&gt;<BR>&lt;role rolename="admin"/&gt;<BR>&lt;user username="robbin" password="12345678" roles="admin,manager,tomcat"/&gt;<BR><BR>这样用户“robbin”就具备了超级管理员权限。重新启动Tomcat以后，你就可以使用该用户来登陆如上的两个管理工具，通过Web方式进行Tomcat的配置和管理了。<BR><BR>2、 BEA Weblogic<BR>Weblogic可以到BEA的网站上免费注册之后下载到最新的Weblogic8.1企业版，License可以免费使用1年时间，其实这已经完全足够了。Weblogic的下载连接：<A title=http://commerce.bea.com/index.jsp，.../edocs.bea.com/ href="http://commerce.bea.com/index.jsp，.../edocs.bea.com/" target=_blank>http://commerce.bea.com/index.jsp，.../edocs.bea.com/</A> 。<BR><BR>3、 IBM Webshpere<BR>Websphere同样可以下载到免费的试用版本，到IBM的developerWorks网站可以看到Websphere试用产品的下载和相关的Websphere的资料，developerWorks中文网站的连接是：<A title=http://www-900.ibm.com/developerWorks/cn/wsdd/ href="http://www-900.ibm.com/developerWorks/cn/wsdd/" target=_blank>http://www-900.ibm.com/developerWorks/cn/wsdd/</A> ，Websphere的下载连接：<A title=http://www7b.software.ibm.com/wsdd/...WASsupport.html href="http://www7b.software.ibm.com/wsdd/...WASsupport.html" target=_blank>http://www7b.software.ibm.com/wsdd/...WASsupport.html</A> 。<BR><BR>4、 Jboss<BR>Jboss是免费开源的App Server，可以免费的从Jboss网站下载：<A title=http://www.jboss.org/index.html，然...n.com/idea.html href="http://www.jboss.org/index.html，然...n.com/idea.html" target=_blank>http://www.jboss.org/index.html，然...n.com/idea.html</A> <BR><BR>四、 Java应用的运行环境<BR><BR>Java的应用可以简单分为以下几个方面：<BR><BR>1、 Java的桌面应用<BR>桌面应用一般仅仅需要JRE的支持就足够了。<BR><BR>2、 Java Web应用<BR>Java的Web应用至少需要安装JDK和一个web容器（例如Tomcat），以及一个多用户数据库，Web应用至少分为三层：<BR>Browser层：浏览器显示用户页面<BR>Web层：运行Servlet/JSP<BR>DB层：后端数据库，向Java程序提供数据访问服务<BR><BR>3、 Java企业级应用<BR>企业级应用比较复杂，可以扩展到n层，最简单情况会分为4层：<BR>Browser层：浏览器显示用户页面<BR>Client层：Java客户端图形程序（或者嵌入式设备的程序）直接和Web层或者EJB层交互<BR>Web层：运行Servlet/JSP<BR>EJB层：运行EJB，完成业务逻辑运算<BR>DB层：后端数据库，向Java程序提供数据访问服务<BR><BR>4、 Java嵌入式应用<BR>Java嵌入式应用是一个方兴未艾的领域，从事嵌入式开发，需要从Sun下载J2ME开发包，J2ME包含了嵌入式设备专用虚拟机KVM，和普通的JDK中包含的JVM有所不同。另外还需要到特定的嵌入式厂商那里下载模拟器。<BR><BR>Java Learning Path（二）、书籍篇<BR><BR>学习一门新的知识，不可能指望只看一本，或者两本书就能够完全掌握。需要有一个循序渐进的阅读过程。我推荐Oreilly出版的Java系列书籍。<BR><BR>在这里我只想补充一点看法，很多人学习Java是从《Thinking in Java》这本书入手的，但是我认为这本书是不适合初学者的。我认为正确的使用这本书的方法应该是作为辅助的读物。《Thinking in Java》并不是在完整的介绍Java的整个体系，而是一种跳跃式的写作方法，是一种类似tips的方法来对Java很多知识点进行了深入的分析和解释。<BR><BR>对于初学者来说，最好是找一本Java入门的书籍，但是比较完整的循序的介绍Java的语法，面向对象的特性，核心类库等等，在看这本书的同时，可以同步来看《Thinking in Java》，来加深对Java的理解和原理的运用，同时又可以完整的了解Java的整个体系。<BR><BR>对于Java的入门书籍，蔡学镛推荐的是Oreilly的《Exploring Java, 2nd Edition》 或者《Java in a Nutshell,2nd Edition（针对C++背景）》，我并没有看过这两本书。其实我觉得电子工业出版社的《Java 2编程详解》或者《Java 2从入门到精通》就很不错。<BR><BR>在所有的Java书籍当中，其实最最有用的，并不是O'reilly的 Java Serials，真正最最有用处是JDK的Documentation！几乎你想获得的所有的知识在Documentation里面全部都有，其中最主要的部分当然是Java基础类库的API文档，是按照package来组织的，对于每一个class都有详细的解释，它的继承关系，是否实现了某个接口，通常用在哪些场合，还可以查到它所有的public的属性和方法，每个属性的解释，意义，每个方法的用途，调用的参数，参数的意义，返回值的类型，以及方法可能抛出的异常等等。可以这样来说，所有关于Java编程方面的书籍其实都不过是在用比较通俗易懂的语言，和良好的组织方式来介绍Documentation里面的某个package里面包含的一些类的用法而已。所以万变不离其宗，如果你有足够的能力来直接通过Documentation来学习Java的类库，那么基本上就不需要看其他的书籍了。除此之外，Documentation也是编程必备的手册，我的桌面上有三个Documentation的快捷方式，分别是J2SDK1.4.1的Documentation，Servlet2.3的Documentation和J2SDKEE1.3.1的Documentation。有了这个三个Documentation，什么其他的书籍都不需要了。<BR><BR>对于Java Web 编程来说，最核心的是要熟悉和掌握HTTP协议，这个就和Java无关了，在熟悉HTTP协议之后，就需要熟悉Java的实现HTTP协议的类库，也就是Servlet API，所以最重要的东西就是Servlet API。当然对于初学者而言，直接通过Servlet API来学习Web编程有很大的难度，我推荐O'reilly的《Java Server Pages 》这本书来学习Web 编程。<BR><BR>EJB的书籍当中，《Enterprise JavaBeans, 2nd Edition》是一本很不错的书， EJB的学习门槛是比较高，入门很难，但是这本书完全降低了学习的难度，特别重要的一点是，EJB的学习需要结合一种App Server的具体实现，所以在学习EJB的同时，必须同步的学习某种App Server，而这本书相关的出了三本书，分别是Weblogic6.1，Websphere4.0和JBoss3.0上面部署书中例子的实做。真是既有理论，又有实践。在学习EJB的同时，可以边看边做，EJB的学习会变得很轻松。<BR><BR>但是这本书也有一个问题，就是版本比较旧，主要讲EJB1.1规范和部分EJB2.0的规范。而Ed Roman写的《Mastering EJB 2.0》这本书完全是根据EJB2.0规范写的，深入浅出，覆盖了EJB编程的各个方面，并且还有很多编程经验tips，也是学习EJB非常推荐的书籍之一。<BR><BR>如果是结合Weblogic来学习J2EE的话，《J2EE应用与BEA Weblogic Server》绝对是首选读物，虽然是讲述的Weblogic6.0，仍然值得购买，这本书是BEA官方推荐的教材，作者也是BEA公司的工程师。现在中文版已经随处可见了。这本书结合Weblogic介绍了J2EE各个方面的技术在Weblogic平台上的开发和部署，实践指导意义非常强。<BR><BR>在掌握了Java平台基础知识和J2EE方面的知识以后，更进一步的是学习如何运用OO的方法进行软件的设计，那么就一定要学习“设计模式”。Sun公司出版了一本《J2EE核心模式》，是每个开发Java企业平台软件的架构师必备的书籍。这本书全面的介绍了J2EE体系架构的各种设计模式，是设计师的必读书籍。<BR><BR>Java Learning Path（三）过程篇<BR><BR>每个人的学习方法是不同的，一个人的方法不见得适合另一个人，我只能是谈自己的学习方法。因为我学习Java是完全自学的，从来没有问过别人，所以学习的过程基本上完全是自己摸索出来的。我也不知道这种方法是否是比较好的方法，只能给大家提供一点参考了。<BR><BR>学习Java的第一步是安装好JDK，写一个Hello JWorld， 其实JDK的学习没有那么简单，关于JDK有两个问题是很容易一直困扰Java程序员的地方：一个是CLASSPATH的问题，其实从原理上来说，是要搞清楚JRE的ClassLoader是如何加载Class的；另一个问题是package和import问题，如何来寻找类的路径问题。把这两个问题摸索清楚了，就扫除了学习Java和使用JDK的最大障碍。推荐看一下王森的《Java深度历险》，对这两个问题进行了深入的探讨。<BR><BR>第二步是学习Java的语法。Java的语法是类C++的，基本上主流的编程语言不是类C，就是类C++的，没有什么新东西，所以语法的学习，大概就是半天的时间足够了。唯一需要注意的是有几个不容易搞清楚的关键字的用法，public，protected，private，static，什么时候用，为什么要用，怎么用，这可能需要有人来指点一下，我当初是完全自己琢磨出来的，花了很久的时间。不过后来我看到《Thinking in Java》这本书上面是讲了这些概念的。<BR><BR>第三步是学习Java的面向对象的编程语言的特性的地方。比如继承，构造器，抽象类，接口，方法的多态，重载，覆盖，Java的异常处理机制。对于一个没有面向对象语言背景的人来说，我觉得这个过程需要花很长很长时间，因为学习Java之前没有C++的经验，只有C的经验，我是大概花了一个月左右吧，才彻底把这些概念都搞清楚，把书上面的例子反复的揣摩，修改，尝试，把那几章内容反复的看过来，看过去，看了不下5遍，才彻底领悟了。不过我想如果有C++经验的话，应该一两天时间足够了。那么在这个过程中，可以多看看《Thinking in Java》这本书，对面向对象的讲解非常透彻。可惜的是我学习的时候，并没有看到这本书，所以自己花了大量的时间，通过自己的尝试和揣摩来学会的。<BR><BR>第四步就是开始熟悉Java的类库。Java的基础类库其实就是JDK安装目录下面jre\lib\rt.jar这个包。学习基础类库就是学习rt.jar。基础类库里面的类非常非常多。据说有3000多个，我没有统计过。但是真正对于我们来说最核心的只有4个，分别是<BR>java.lang.*;<BR>java.io.*;<BR>java.util.*;<BR>java.sql.*;<BR><BR>这四个包的学习，每个包的学习都可以写成一本厚厚的教材，而O'reilly也确实是这样做的。我觉得如果时间比较紧，是不可能通过读四本书来学习。我觉得比较好的学习方法是这样的：<BR>首先要通读整个package的框架，了解整个package的class，interface，exception的构成，最好是能够找到介绍整个包框架的文章。这些专门介绍包的书籍的前几章应该就是这些总体的框架内容介绍。<BR><BR>对包整体框架的把握并不是要熟悉每个类的用法，记住它有哪些属性，方法。想记也记不住的。而是要知道包有哪些方面的类构成的，这些类的用途是什么，最核心的几个类分别是完成什么功能的。我在给人培训的时候一般是一次课讲一个包，所以不可能详细的介绍每个类的用法，但是我反复强调，我给你们讲这些包的不是要告诉你们类的方法是怎么调用的，也不要求你们记住类的方法调用，而是要你们了解，Java给我们提供了哪些类，每个类是用在什么场合，当我遇到问题的时候，我知道哪个类，或者哪几个类的组合可以解决我的问题，That'all！，当我们具体写程序的时候，只要你知道该用哪个类来完成你的工作就足够了。编码的时候，具体的方法调用，是边写代码，边查Documentation，所有的东西都在Documentation里面，不要求你一定记住，实际你也记不住3000多个类的总共将近10万个方法调用。所以对每个包的总体框架的把握就变得极为重要。<BR><BR>第五步，通过上面的学习，如果学的比较扎实的话，就打好了Java的基础了，剩下要做的工作是扫清Documentation里面除了上面4个包之外的其他一些比较有用处的类。相信进展到这一步，Java的自学能力已经被培养出来了，可以到了直接学习Documentation的水平了。除了要做GUI编程之外，JDK里面其他会有用处的包是这些：<BR>java.text.*;<BR>java.net.*;<BR>javax.naming.*;<BR>这些包里面真正用的比较多的类其实很少，只有几个，所以不需要花很多时间。<BR><BR>第六步，Java Web 编程<BR>Web编程的核心是HTTP协议，HTTP协议和Java无关，如果不熟悉HTTP协议的话，虽然也可以学好Servlet/JSP编程，但是达不到举一反三，一通百通的境界。所以HTTP协议的学习是必备的。如果熟悉了HTTP协议的话，又有了Java编程的良好的基础，学习Servlet/JSP简直易如反掌，我学习Servlet/JSP就用了不到一周的时间，然后就开始用JSP来做项目了。<BR><BR>在Servlet/JSP的学习中，重头仍然是Servlet Documentation。Servlet API最常用的类很少，花比较少的时间就可以掌握了。把这些类都看一遍，多写几个例子试试。Servlet/JSP编程本质就是在反复调用这些类来通过HTTP协议在Web Server和Brower之间交谈。另外对JSP，还需要熟悉几个常用JSP的标记，具体的写法记不住的话，临时查就是了。<BR><BR>此外Java Web编程学习的重点要放在Web Application的设计模式上，如何进行业务逻辑的分析，并且进行合理的设计，按照MVC设计模式的要求，运用Servlet和JSP分别完成不同的逻辑层，掌握如何在Servlet和JSP之间进行流程的控制和数据的共享，以及Web Application应该如何配置和部署。<BR><BR>第七步，J2EE编程<BR>以上的学习过程如果是比较顺利的话，进行到这一步，难度又陡然提高。因为上面的知识内容都是只涉及一个方面，而像EJB，JMS，JTA等核心的J2EE规范往往是几种Java技术的综合运用的结晶，所以掌握起来难度比较大。<BR><BR>首先一定要学习好JNDI，JNDI是App Server定位服务器资源（EJB组件，Datasouce，JMS）查找方法，如果对JNDI不熟悉的话，EJB，JMS这些东西几乎学不下去。JNDI其实就是javax.naming.*这个包，运用起来很简单。难点在于服务器资源文件的配置。对于服务器资源文件的配置，就需要看看专门的文档规范了，比如web.xml的写法，ejb-jar.xml的写法等等。针对每种不同的App Server，还有自己的服务资源配置文件，也是需要熟悉的。<BR><BR>然后可以学习JTA，主要是要理解JTA对于事务的控制的方法，以及该在什么场合使用JTA。这里可以简单的举个例子，我们知道一般情况可以对于一个数据库连接进行事务控制(conn.setAutoCommit(false),....,conn.commit())，做为一个原子操作，但是假设我的业务需求是要把对两个不同数据库的操作做为一个原子操作，你能做的到吗？这时候只能用JTA了。假设操作过程是先往A数据库插一条记录，然后删除B数据库另一个记录，我们自己写代码是控制不了把整个操作做为一个原子操作的。用JTA的话，由App Server来完成控制。<BR><BR>在学习EJB之前要学习对象序列化和RMI，RMI是EJB的基础。接着学习JMS和EJB，对于EJB来说，最关键是要理解EJB是如何通过RMI来实现对远端对象的调用的，以及在什么情况下要用到EJB。<BR><BR>在学习完EJB，JMS这些东西之后，你可能会意识到要急不可待学习两个领域的知识，一个是UML，另一个是Design Pattern。Java企业软件的设计非常重视框架(Framework)的设计，一个好的软件框架是软件开发成功的必要条件。在这个时候，应该开始把学习的重点放在设计模式和框架的学习上，通过学习和实际的编程经验来掌握EJB的设计模式和J2EE的核心模式。<BR><BR>J2EE规范里面，除了EJB，JMS，JTA，Servlet/JSP，JDBC之外还有很多很多的企业技术，这里不一一进行介绍了。<BR><BR>另外还有一个最新领域Web Services。Web Services也完全没有任何新东西，它像是一种黏合剂，可以把不同的服务统一起来提供一个统一的调用接口，作为使用者来说，我只要获得服务提供者给我的WSDL（对服务的描述），就够了，我完全不知道服务器提供者提供的服务究竟是EJB组件，还是.Net组件，还是什么CORBA组件，还是其他的什么实现，我也不需要知道。Web Services最伟大的地方就在于通过统一的服务提供方式和调用方式，实现了整个Internet服务的共享，是一个非常令人激动的技术领域。Web Services好像目前还没有什么很好的书籍，但是可以通过在网络上面查资料的方式来学习。<BR><BR>Java Learning Path（四） 方法篇<BR><BR>Java作为一门编程语言，最好的学习方法就是写代码。当你学习一个类以后，你就可以自己写个简单的例子程序来运行一下，看看有什么结果，然后再多调用几个类的方法，看看运行结果，这样非常直观的把类给学会了，而且记忆非常深刻。然后不应该满足把代码调通，你应该想想看如果我不这样写，换个方式，再试试行不行。记得哪个高人说过学习编程就是个破坏的过程，把书上的例子，自己学习Documentation编写的例子在运行通过以后，不断的尝试着用不同的方法实现，不断的尝试破坏代码的结构，看看它会有什么结果。通过这样的方式，你会很彻底的很精通的掌握Java。<BR><BR>举个例子，我们都编过Hello World<BR><BR>public class HelloWorld {<BR>public static void main(String[] args) {<BR>System.out.println("Hello World");<BR>}<BR>}<BR><BR>很多初学者不是很理解为什么main方法一定要这样来定义public static void main(String[] args)，能不能不这样写？包括我刚学习Java的时候也有这样的疑问。想知道答案吗？很简单，你把main改个名字运行一下，看看报什么错误，然后根据出错信息进行分析；把main的public取掉，在试试看，报什么错误；static去掉还能不能运行；不知道main方法是否一定要传一个String[]数组的，把String[]改掉，改成int[]，或者String试试看；不知道是否必须写args参数名称的，也可以把args改成别的名字，看看运行结果如何。<BR><BR>我当初学习Java的时候就是这样做的，把Hello World程序反复改了七八次，不断运行，分析运行结果，最后就彻底明白为什么了main方法是这样定义的了。<BR><BR>此外，我对于staic，public，private，Exception，try{ }catch {}finally{}等等等等一开始都不是很懂，都是把参考书上面的例子运行成功，然后就开始破坏它，不断的根据自己心里面的疑问来重新改写程序，看看能不能运行，运行出来是个什么样子，是否可以得到预期的结果。这样虽然比较费时间，不过一个例子程序这样反复破坏几次之后。我就对这个相关的知识彻底学通了。有时候甚至故意写一些错误的代码来运行，看看能否得到预期的运行错误。这样对于编程的掌握是及其深刻的。<BR><BR>其中特别值得一提的是JDK有一个非常棒的调试功能，-verbose <BR>java –verbose<BR>javac –verbose 以及其它很多JDK工具都有这个选项<BR>-verbose 可以显示在命令执行的过程中，JVM都依次加载哪里Class，通过这些宝贵的调试信息，可以帮助我们分析出JVM在执行的过程中都干了些什么。<BR><BR>另外，自己在学习过程中，写的很多的这种破坏例程，应该有意识的分门别类的保存下来，在工作中积累的典型例程也应该定期整理，日积月累，自己就有了一个代码库了。遇到类似的问题，到代码库里面 Copy &amp; Paste ，Search &amp; Replace，就好了，极大提高了开发速度。最理想的情况是把一些通用的例程自己再抽象一层，形成一个通用的类库，封装好。那么可复用性就更强了。<BR><BR>所以我觉得其实不是特别需要例程的，自己写的破坏例程就是最好的例子，如果你实在对自己写的代码不放心的话，我强烈推荐你看看JDK基础类库的Java源代码。在JDK安装目录下面会有一个src.zip，解开来就可以完整的看到整个JDK基础类库，也就是rt.jar的Java源代码，你可以参考一下Sun是怎么写Java程序的，规范是什么样子的。我自己在学习Java的类库的时候，当有些地方理解的不是很清楚的时候，或者想更加清晰的理解运作的细节的时候，往往会打开相应的类的源代码，通过看源代码，所有的问题都会一扫而空。<BR><BR>Java Learning Path（五）资源篇<BR><BR>1、 <A title=http://java.sun.com/ href="http://java.sun.com/" target=_blank>http://java.sun.com/</A> (英文)<BR>Sun的Java网站，是一个应该经常去看的地方。不用多说。<BR><BR>2、<A title=http://www-900.ibm.com/developerWorks/cn/ href="http://www-900.ibm.com/developerWorks/cn/" target=_blank>http://www-900.ibm.com/developerWorks/cn/</A> <BR>IBM的developerWorks网站，英语好的直接去英文主站点看。这里不但是一个极好的面向对象的分析设计网站，也是Web Services，Java，Linux极好的网站。强烈推荐！！！<BR><BR>3、<A title=http://www.javaworld.com/ href="http://www.javaworld.com/" target=_blank>http://www.javaworld.com/</A> (英文)<BR>关于Java很多新技术的讨论和新闻。想多了解Java的方方面面的应用，这里比较好。<BR><BR>4、<A title=http://dev2dev.bea.com.cn/index.jsp href="http://dev2dev.bea.com.cn/index.jsp" target=_blank>http://dev2dev.bea.com.cn/index.jsp</A> <BR>BEA的开发者园地，BEA作为最重要的App Server厂商，有很多独到的技术，在Weblogic上做开发的朋友不容错过。<BR><BR>5、<A title=http://www.huihoo.com/ href="http://www.huihoo.com/" target=_blank>http://www.huihoo.com/</A> <BR>灰狐动力网站，一个专业的中间件网站，虽然不是专业的Java网站，但是在J2EE企业应用技术方面有深厚的造诣。<BR><BR>6、<A title=http://www.theserverside.com/home/ href="http://www.theserverside.com/home/" target=_blank>http://www.theserverside.com/home/</A> (英文)<BR>TheServerSide是一个著名的专门面向Java Server端应用的网站。<BR><BR>7、<A title=http://www.javaresearch.org/ href="http://www.javaresearch.org/" target=_blank>http://www.javaresearch.org/</A> <BR>Java研究组织，有很多优秀的Java方面的文章和教程，特别是在JDO方面的文章比较丰富。<BR><BR>8、<A title=http://www.cnjsp.org/ href="http://www.cnjsp.org/" target=_blank>http://www.cnjsp.org/</A> <BR>JSP技术网站，有相当多的Java方面的文章和资源。<BR><BR>9、<A title=http://www.jdon.com/ href="http://www.jdon.com/" target=_blank>http://www.jdon.com/</A> <BR>Jdon论坛，是一个个人性质的中文J2EE专业技术论坛，在众多的Java的中文论坛中，Jdon一个是技术含量非常高，帖子质量非常好的论坛。<BR><BR>10、<A title=http://sourceforge.net/ href="http://sourceforge.net/" target=_blank>http://sourceforge.net/</A><BR>SourgeForge是一个开放源代码软件的大本营，其中也有非常非常丰富的Java的开放源代码的著名的软件。 <img src ="http://www.cnitblog.com/cyberfan/aggbug/1633.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/cyberfan/" target="_blank">cyberfan</a> 2005-08-12 16:35 <a href="http://www.cnitblog.com/cyberfan/articles/1633.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>