实现过程
限于这篇文章的篇幅,同时也为了更多的读者能够真正专注于问题本身,我不想就某一种Java集成开发环境来介绍这个Java Bean的制作过程,介绍一个方法时我发现步骤和命令很清晰,我相信有任何一种Java集成环境三天以上经验的读者都会知道如何把这些代码在集成环境中编译和运行。用集成环境讲述问题往往需要配很多屏幕截图,这也是我一直对集成环境很头疼的原因。我使用了一个普通的文本编辑器,同时使用了Sun公司标准的JDK 1.3.0 for Windows NT。
其实把C转换成Java对于一个有一定C语言基础的程序员并不困难,这两个语言的基本语法几乎完全一致.我大概花了一个小时的时间完成了代码的转换工作,我主要作了下面几件事:
- 把必须使用的一些#define的宏定义变成Class中的final static,这样保证在一个进程空间中的多个Instance共享这些数据
- 删去了一些无用的#if define,因为我只关心MD5,这个推荐的C实现同时实现了MD2 MD3和 MD4,而且有些#if define还和C不同编译器有关
- 将一些计算宏转换成final static 成员函数。
- 所有的变量命名与原来C实现中保持一致,在大小写上作一些符合Java习惯的变化,计算过程中的C函数变成了private方法(成员函数)。
- 关键变量的位长调整
- 定义了类和方法
需要注意的是,很多早期的C编译器的int类型是16 bit的,MD5使用了unsigned long int,并认为它是32bit的无符号整数。而在Java中int是32 bit的,long是64 bit的。在MD5的C实现中,使用了大量的位操作。这里需要指出的一点是,尽管Java提供了位操作,由于Java没有unsigned类型,对于右移位操作多提供了一个无符号右移:>>>,等价于C中的 >> 对于unsigned 数的处理。
因为Java不提供无符号数的运算,两个大int数相加就会溢出得到一个负数或异常,因此我将一些关键变量在Java中改成了long类型(64bit)。我个人认为这比自己去重新定义一组无符号数的类同时重载那些运算符要方便,同时效率高很多并且代码也易读,OO(Object Oriented)的滥用反而会导致效率低下。
限于篇幅,这里不再给出原始的C代码,有兴趣对照的读者朋友可以去看RFC 1321。MD5.java源代码
测试
在RFC 1321中,给出了Test suite用来检验你的实现是否正确:
MD5 ("") = d41d8cd98f00b204e9800998ecf8427eMD5 ("a") = 0cc175b9c0f1b6a831c399e269772661MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b……这些输出结果的含义是指:空字符串””的MD5值是d41d8cd98f00b204e9800998ecf8427e,字符串”a”的MD5值是0cc175b9c0f1b6a831c399e269772661…… 编译并运行我们的程序:
javac –d . MD5.java
java beartool.MD5
为了将来不与别人的同名程序冲突,我在我的程序的第一行使用了package beartool;
因此编译命令javac –d . MD5.java 命令在我们的工作目录下自动建立了一个beartool目录,目录下放着编译成功的 MD5.class
我们将得到和Test suite同样的结果。当然还可以继续测试你感兴趣的其它MD5变换,例如:
java beartool.MD5 1234
将给出1234的MD5值。
可能是我的计算机知识是从Apple II和Z80单板机开始的,我对大写十六进制代码有偏好,如果您想使用小写的Digest String只需要把byteHEX函数中的A、B、C、D、E、F改成a、b、 c、d、e、f就可以了。
MD5据称是一种比较耗时的计算,我们的Java版MD5一闪就算出来了,没遇到什么障碍,而且用肉眼感觉不出来Java版的MD5比C版的慢。
为了测试它的兼容性,我把这个MD5.class文件拷贝到我的另一台Linux+IBM JDK 1.3的机器上,执行后得到同样结果,确实是“一次编写到处运行了”。






