编译成功的例子
❶ 如何用maven将java8写的代码编译为java6平台的
在一般的Java应用开发过程中,开发人员使用Java的方式比较简单。打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了。这种开发模式背后的过程是:开发人员编写的是Java源代码文件(.java),IDE会负责调用Java的编译器把Java源代码编译成平台无关的字节代码(byte code),以类文件的形式保存在磁盘上(.class)。Java虚拟机(JVM)会负责把Java字节代码加载并执行。Java通过这种方式来实现其“编写一次,到处运行(Write once, run anywhere)” 的目标。Java类文件中包含的字节代码可以被不同平台上的JVM所使用。Java字节代码不仅可以以文件形式存在于磁盘上,也可以通过网络方式来下载,还可以只存在于内存中。JVM中的类加载器会负责从包含字节代码的字节数组(byte[])中定义出Java类。在某些情况下,可能会需要动态的生成 Java字节代码,或是对已有的Java字节代码进行修改。这个时候就需要用到本文中将要介绍的相关技术。首先介绍一下如何动态编译Java源文件。
动态编译Java源文件
在一般情况下,开发人员都是在程序运行之前就编写完成了全部的Java源代码并且成功编译。对有些应用来说,Java源代码的内容在运行时刻才能确定。这个时候就需要动态编译源代码来生成Java字节代码,再由JVM来加载执行。典型的场景是很多算法竞赛的在线评测系统(如PKU JudgeOnline),允许用户上传Java代码,由系统在后台编译、运行并进行判定。在动态编译Java源文件时,使用的做法是直接在程序中调用Java编译器。
JSR 199引入了Java编译器API。如果使用JDK 6的话,可以通过此API来动态编译Java代码。比如下面的代码用来动态编译最简单的Hello World类。该Java类的代码是保存在一个字符串中的。
01 public class CompilerTest {
02 public static void main(String[] args) throws Exception {
03 String source = "public class Main { public static void main(String[] args) {System.out.println(\"Hello World!\");} }";
04 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
05 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
06 StringSourceJavaObject sourceObject = newCompilerTest.StringSourceJavaObject("Main", source);
07 Iterable< extends JavaFileObject> fileObjects = Arrays.asList(sourceObject);
08 CompilationTask task = compiler.getTask(null, fileManager, null,null, null, fileObjects);
09 boolean result = task.call();
10 if (result) {
11 System.out.println("编译成功。");
12 }
13 }
14
15 static class StringSourceJavaObject extends SimpleJavaFileObject {
16
17 private String content = null;
18 public StringSourceJavaObject(String name, String content) ??throwsURISyntaxException {
19 super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
20 this.content = content;
21 }
22
23 public CharSequence getCharContent(boolean ignoreEncodingErrors) ??throws IOException {
24 return content;
25 }
26 }
27 }
如果不能使用JDK 6提供的Java编译器API的话,可以使用JDK中的工具类com.sun.tools.javac.Main,不过该工具类只能编译存放在磁盘上的文件,类似于直接使用javac命令。
另外一个可用的工具是Eclipse JDT Core提供的编译器。这是Eclipse Java开发环境使用的增量式Java编译器,支持运行和调试有错误的代码。该编译器也可以单独使用。Play框架在内部使用了JDT的编译器来动态编译Java源代码。在开发模式下,Play框架会定期扫描项目中的Java源代码文件,一旦发现有修改,会自动编译 Java源代码。因此在修改代码之后,刷新页面就可以看到变化。使用这些动态编译的方式的时候,需要确保JDK中的tools.jar在应用的 CLASSPATH中。
下面介绍一个例子,是关于如何在Java里面做四则运算,比如求出来(3+4)*7-10的值。一般的做法是分析输入的运算表达式,自己来模拟计算过程。考虑到括号的存在和运算符的优先级等问题,这样的计算过程会比较复杂,而且容易出错。另外一种做法是可以用JSR 223引入的脚本语言支持,直接把输入的表达式当做JavaScript或是JavaFX脚本来执行,得到结果。下面的代码使用的做法是动态生成Java源代码并编译,接着加载Java类来执行并获取结果。这种做法完全使用Java来实现。
01 private static double calculate(String expr) throws CalculationException {
02 String className = "CalculatorMain";
03 String methodName = "calculate";
04 String source = "public class " + className
05 + " { public static double " + methodName + "() { return " + expr +"; } }";
06 //省略动态编译Java源代码的相关代码,参见上一节
07 boolean result = task.call();
08 if (result) {
09 ClassLoader loader = Calculator.class.getClassLoader();
10 try {
11 Class<?> clazz = loader.loadClass(className);
12 Method method = clazz.getMethod(methodName, new Class<?>[] {});
13 Object value = method.invoke(null, new Object[] {});
14 return (Double) value;
15 } catch (Exception e) {
16 throw new CalculationException("内部错误。");
17 }
18 } else {
19 throw new CalculationException("错误的表达式。");
20 }
21 }
上面的代码给出了使用动态生成的Java字节代码的基本模式,即通过类加载器来加载字节代码,创建Java类的对象的实例,再通过Java反射API来调用对象中的方法。
Java字节代码增强
Java 字节代码增强指的是在Java字节代码生成之后,对其进行修改,增强其功能。这种做法相当于对应用程序的二进制文件进行修改。在很多Java框架中都可以见到这种实现方式。Java字节代码增强通常与Java源文件中的注解(annotation)一块使用。注解在Java源代码中声明了需要增强的行为及相关的元数据,由框架在运行时刻完成对字节代码的增强。Java字节代码增强应用的场景比较多,一般都集中在减少冗余代码和对开发人员屏蔽底层的实现细节上。用过JavaBeans的人可能对其中那些必须添加的getter/setter方法感到很繁琐,并且难以维护。而通过字节代码增强,开发人员只需要声明Bean中的属性即可,getter/setter方法可以通过修改字节代码来自动添加。用过JPA的人,在调试程序的时候,会发现实体类中被添加了一些额外的 域和方法。这些域和方法是在运行时刻由JPA的实现动态添加的。字节代码增强在面向方面编程(AOP)的一些实现中也有使用。
❷ 关于如何判断gcc之类的编译器的编译结果
我们再使用gcc编译的时候可以让他的输出信息保存到文件当中
gccmain.c-omain&>status.txt
上面的命令就是将gcc编译的信息保存到status.txt文件中,然后我们再程序中读取文件,看文件是否有内容,没有内容就说明没有报错和警告,编译成功。有内容就对每一行内容进行判断,看是warring还是error,只有warring也代表编译成功,有error代表编译失败,然后把这些报错信息都打印出来就好了。
下面看一下例子:
❸ Java杩涢桩锛歋E6璋幂敤缂栬疟鍣ㄧ殑涓ょ嶆柟娉昜1]
銆銆鍦ㄥ緢澶钦ava搴旂敤涓闇瑕佸湪绋嫔簭涓璋幂敤Java缂栬疟鍣ㄦ潵缂栬疟鍜岃繍琛 浣嗗湪镞╂湡镄勭増链涓锛圝ava SE 鍙娄互鍓岖増链锛変腑鍙鑳介氲繃tools jar涓镄 sun tools javac鍖呮潵璋幂敤Java缂栬疟鍣 浣嗙敱浜巘ools jar涓嶆槸镙囧嗳镄凧ava搴 鍦ㄤ娇鐢ㄦ椂蹇呴’瑕佽剧疆杩欎釜jar镄勮矾寰 钥屽湪Java SE 涓涓烘垜浠鎻愪緵浜嗘爣鍑嗙殑鍖呮潵镎崭綔Java缂栬疟鍣 杩椤氨鏄痡avax tools鍖 浣跨敤杩欎釜鍖 鎴戜滑鍙浠ヤ笉鐢ㄥ皢jar鏂囦欢璺寰勬坊锷犲埌classpath涓浜
銆銆涓 浣跨敤JavaCompiler鎺ュ彛𨱒ョ紪璇慗ava婧愮▼搴
銆銆浣跨敤Java API𨱒ョ紪璇慗ava婧愮▼搴忔湁寰埚氭柟娉 鐜板湪璁╂垜浠𨱒ョ湅涓绉嶆渶绠鍗旷殑鏂规硶 阃氲繃JavaCompiler杩涜岀紪璇
銆銆鎴戜滑鍙浠ラ氲繃ToolProvider绫荤殑闱欐佹柟娉昰etSystemJavaCompiler𨱒ュ缑鍒颁竴涓狫avaCompiler鎺ュ彛镄勫疄渚
銆銆JavaCompiler piler = ToolProvider getSystemJavaCompiler();
銆銆JavaCompiler涓链镙稿绩镄勬柟娉曟槸run 阃氲繃杩欎釜鏂规硶鍙浠ョ紪璇慾ava婧愮▼搴 杩欎釜鏂规硶链 涓锲哄畾鍙傛暟鍜 涓鍙鍙桦弬鏁帮纸鍙鍙桦弬鏁版槸浠嶫ave SE 寮濮嬫彁渚涚殑涓涓鏂扮殑鍙傛暟绫诲瀷 鐢╰ype钬 argu琛ㄧず锛 鍓 涓鍙傛暟鍒嗗埆鐢ㄦ潵涓箦ava缂栬疟鍣ㄦ彁渚涘弬鏁 寰楀埌Java缂栬疟鍣ㄧ殑杈揿嚭淇℃伅浠ュ强鎺ユ敹缂栬疟鍣ㄧ殑阌栾淇℃伅 钖庨溃镄勫彲鍙桦弬鏁板彲浠ヤ紶鍏ヤ竴涓鎴栧氢釜Java婧愮▼搴忔枃浠 濡傛灉run缂栬疟鎴愬姛 杩斿洖
銆銆int run(InputStream in OutputStream out OutputStream err String arguments)
銆銆濡傛灉鍓 涓鍙傛暟浼犲叆镄勬槸null 闾d箞run鏂规硶灏嗕互镙囧嗳镄勮緭鍏 杈揿嚭浠f浛 鍗砈ystem in System out鍜孲ystem err 濡傛灉鎴戜滑瑕佺紪璇戜竴涓猼est java鏂囦欢 骞跺皢浣跨敤镙囧嗳杈揿叆杈揿嚭 run镄勪娇鐢ㄦ柟娉曞备笅
銆銆int results = tool run(null null null test java );
銆銆涓嬮溃鏄浣跨敤JavaCompiler镄勫畬鏁翠唬镰
銆銆import java io *;銆銆import javax tools *;銆銆public class test_pilerapi銆銆{銆 銆public static void main(String args[]) throws IOException銆 銆{銆 銆JavaCompiler piler = ToolProvider getSystemJavaCompiler();銆 銆int results = piler run(null null null test java );銆 銆System out println((results == )? 缂栬疟鎴愬姛 : 缂栬疟澶辫触 );銆 銆// 鍦ㄧ▼搴忎腑杩愯宼est銆 銆Runtime run = Runtime getRuntime();銆 銆Process p = run exec( java test );銆 銆BufferedInputStream in = new BufferedInputStream(p getInputStream());銆 銆BufferedReader br = new BufferedReader(new InputStreamReader(in));銆 銆String s;銆 銆while ((s = br readLine()) != null)銆 銆System out println(s);銆 銆}銆銆}銆銆public class test銆銆{銆 銆public static void main(String[] args) throws Exception銆 銆{銆 銆System out println( JavaCompiler娴嬭瘯鎴愬姛锛 );銆 銆}銆銆}
銆銆缂栬疟鎴愬姛镄勮緭鍑虹粨鏋
銆銆缂栬疟鎴愬姛
銆銆JavaCompiler娴嬭瘯鎴愬姛
銆銆缂栬疟澶辫触镄勮緭鍑虹粨鏋
銆銆test java: : 镓句笉鍒扮﹀彿
銆銆绗﹀彿 鏂规硶 printlnln(java lang String)
銆銆浣岖疆 绫 java io PrintStream
銆銆System out printlnln( JavaCompiler娴嬭瘯鎴愬姛锛 );
銆銆^
銆銆 阌栾
銆銆缂栬疟澶辫触
銆銆浜 浣跨敤StandardJavaFileManager缂栬疟Java婧愮▼搴
銆銆鍦ㄧ涓閮ㄥ垎鎴戜滑璁ㄨ鸿皟鐢╦ava缂栬疟鍣ㄧ殑链瀹规槗镄勬柟娉 杩欑嶆柟娉曞彲浠ュ緢濂藉湴宸ヤ綔 浣嗗畠纭涓嶈兘镟存湁鏁埚湴寰楀埌鎴戜滑镓闇瑕佺殑淇℃伅 濡傛爣鍑嗙殑杈揿叆 杈揿嚭淇℃伅 钥屽湪Java SE 涓链濂界殑鏂规硶鏄浣跨敤StandardJavaFileManager绫 杩欎釜绫诲彲浠ュ緢濂藉湴鎺у埗杈揿叆 杈揿嚭 骞朵笖鍙浠ラ氲繃DiagnosticListener寰楀埌璇婃柇淇℃伅 钥娈iagnosticCollector绫诲氨鏄痩istener镄勫疄鐜
銆銆浣跨敤StandardJavaFileManager闇瑕佷袱姝 棣栧厛寤虹珛涓涓狣iagnosticCollector瀹炰緥浠ュ强阃氲繃JavaCompiler镄刧etStandardFileManager()鏂规硶寰楀埌涓涓猄tandardFileManager瀵硅薄 链钖庨氲繃CompilationTask涓镄刢all鏂规硶缂栬疟婧愮▼搴
銆銆鍦ㄤ娇鐢ㄨ繖绉嶆柟娉曡皟鐢↗ava缂栬疟镞舵渶澶嶆潅镄勬柟娉曞氨鏄痝etTask 涓嬮溃璁╂垜浠璁ㄨ轰竴涓媑etTask鏂规硶 杩欎釜鏂规硶链夊备笅镓绀虹殑 涓鍙傛暟
銆銆getTask(Writer out JavaFileManager fileManager 銆銆DiagnosticListener diagnosticListener 銆銆Iterable options 銆銆Iterable classes 銆銆Iterable pilationUnits)
銆銆杩欎簺鍙傛暟澶у氭暟閮藉彲涓簄ull 瀹冧滑镄勫惈涔夋墍涓
銆銆路out: 鐢ㄤ簬杈揿嚭阌栾镄勬祦 榛樿ゆ槸System err
銆銆路fileManager: 镙囧嗳镄勬枃浠剁$悊
銆銆路diagnosticListener: 缂栬疟鍣ㄧ殑榛樿よ屼负
銆銆路options: 缂栬疟鍣ㄧ殑阃夐”
銆銆路classes 鍙备笌缂栬疟镄刢lass
銆銆链钖庝竴涓鍙傛暟pilationUnits涓嶈兘涓簄ull 锲犱负杩欎釜瀵硅薄淇濆瓨浜嗕綘𨱍崇紪璇戠殑Java鏂囦欢
銆銆鍦ㄤ娇鐢ㄥ畬getTask钖 闇瑕侀氲繃StandardJavaFileManager镄刧etJavaFileObjectsFromFiles鎴杇etJavaFileObjectsFromStrings鏂规硶寰楀埌pilationUnits瀵硅薄 璋幂敤杩欎袱涓鏂规硶镄勬柟寮忓备笅
銆銆Iterable getJavaFileObjectsFromFiles(銆銆Iterable files)銆銆Iterable getJavaFileObjectsFromStrings(銆銆Iterable names)銆銆String[] filenames = 钬;銆銆Iterable pilationUnits =銆銆fileManager getJavaFileObjectsFromFiles(Arrays asList(filenames));銆銆JavaCompiler CompilationTask task = piler getTask(null fileManager 銆銆diagnostics options null pilationUnits);銆銆链钖庨渶瑕佸叧闂璮ileManager close();
銆銆涓嬮溃鏄涓涓瀹屾暣镄勬紨绀虹▼搴
銆銆import java io *;銆銆import java util *;銆銆import javax tools *;銆銆public class test_pilerapi銆銆{銆 銆private static void pilejava() throws Exception銆 銆{銆 銆JavaCompiler piler = ToolProvider getSystemJavaCompiler();銆 銆// 寤虹珛DiagnosticCollector瀵硅薄銆 銆DiagnosticCollector diagnostics = new DiagnosticCollector();銆 銆StandardJavaFileManager fileManager = piler getStandardFileManager(diagnostics null null);銆 銆// 寤虹珛鐢ㄤ簬淇濆瓨琚缂栬疟鏂囦欢钖岖殑瀵硅薄銆 銆// 姣忎釜鏂囦欢琚淇濆瓨鍦ㄤ竴涓浠嶫avaFileObject缁ф圹镄勭被涓銆 銆Iterable pilationUnits = fileManager銆 銆 getJavaFileObjectsFromStrings(Arrays asList( test java ));銆 銆JavaCompiler CompilationTask task = piler getTask(null fileManager 銆 銆diagnostics null null pilationUnits);銆 銆// 缂栬疟婧愮▼搴忋 銆boolean success = task call();銆 銆fileManager close();銆 銆System out println((success)? 缂栬疟鎴愬姛 : 缂栬疟澶辫触 );銆 銆}銆銆 public static void main(String args[]) throws Exception銆 銆{銆 銆pilejava();銆銆 }銆銆}
銆銆濡傛灉𨱍冲缑鍒板叿浣撶殑缂栬疟阌栾 鍙浠ュ笵iagnostics杩涜屾壂鎻 浠g爜濡备笅
銆銆for (Diagnostic diagnostic : diagnostics getDiagnostics())銆銆System out printf(銆銆 Code: %s%n +銆銆 Kind: %s%n +銆銆 Position: %s%n +銆銆 Start Position: %s%n +銆銆 End Position: %s%n +銆銆 Source: %s%n +銆銆 Message: %s%n 銆銆diagnostic getCode() diagnostic getKind() 銆銆diagnostic getPosition() diagnostic getStartPosition() 銆銆diagnostic getEndPosition() diagnostic getSource() 銆銆diagnostic getMessage(null));銆銆琚缂栬疟镄则est java浠g爜濡备笅 銆銆public class test銆銆{銆銆 public static void main(String[] args) throws Exception銆 銆{銆 銆aa; //阌栾璇鍙ャ 銆System out println( JavaCompiler娴嬭瘯鎴愬姛锛 );銆 銆}銆銆}
銆銆鍦ㄨ繖娈典唬镰佷腑澶氩啓浜嗕釜aa 寰楀埌镄勭紪璇戦敊璇涓
銆銆Code: piler err not stmt銆銆Kind: ERROR銆銆Position: 銆銆Start Position: 銆銆End Position: 銆銆Source: test java銆銆Message: test java: : 涓嶆槸璇鍙ャ銆Success: false
銆銆阃氲繃JavaCompiler杩涜岀紪璇戦兘鏄鍦ㄥ綋鍓岖洰褰曚笅鐢熸垚 class鏂囦欢 钥屼娇鐢ㄧ紪璇戦夐”鍙浠ユ敼鍙樿繖涓榛樿ょ洰褰 缂栬疟阃夐”鏄涓涓鍏幂礌涓篠tring绫诲瀷镄処terable闆嗗悎 濡傛垜浠鍙浠ヤ娇鐢ㄥ备笅浠g爜鍦―鐩樻牴鐩褰曚笅鐢熸垚 class鏂囦欢
銆銆Iterable options = Arrays asList( d d:\ );銆銆JavaCompiler CompilationTask task = piler getTask(null fileManager 銆銆diagnostics options null pilationUnits);
銆銆鍦ㄤ笂闱㈢殑渚嫔瓙涓璷ptions澶勭殑鍙傛暟涓簄ull 钥岃佷紶阃掔紪璇戝櫒镄勫弬鏁 灏遍渶瑕佸皢options浼犲叆
銆銆链夋椂鎴戜滑缂栬疟涓涓狫ava婧愮▼搴忔枃浠 钥岃繖涓婧愮▼搴忔枃浠堕渶瑕佸彟鍑犱釜Java鏂囦欢 钥岃繖浜汮ava鏂囦欢鍙埚湪鍙﹀栦竴涓鐩褰 闾d箞杩椤氨闇瑕佷负缂栬疟鍣ㄦ寚瀹氲繖浜涙枃浠舵墍鍦ㄧ殑鐩褰
銆銆Iterable options = Arrays asList( sourcepath d:\src );
銆銆涓婇溃镄勪唬镰佹寚瀹氱殑琚缂栬疟Java鏂囦欢镓渚濊禆镄勬簮鏂囦欢镓鍦ㄧ殑鐩褰
lishixin/Article/program/Java/hx/201311/27239
❹ MD5是如何编译的
MD5简介
MD5的全称是Message-Digest Algorithm 5,在90年代初由MIT的计算机科学实验室和RSA Data Security Inc发明,经MD2、MD3和MD4发展而来。
Message-Digest泛指字节串(Message)的Hash变换,就是把一个任意长度的字节串变换成一定长的大整数。请注意我使用了“字节串”而不是“字符串”这个词,是因为这种变换只与字节的值有关,与字符集或编码方式无关。
MD5将任意长度的“字节串”变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法,换句话说就是,即使你看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串,从数学原理上说,是因为原始的字符串有无穷多个,这有点象不存在反函数的数学函数。
MD5的典型应用是对一段Message(字节串)产生fingerprint(指纹),以防止被“篡改”。举个例子,你将一段话写在一个叫readme.txt文件中,并对这个readme.txt产生一个MD5的值并记录在案,然后你可以传播这个文件给别人,别人如果修改了文件中的任何内容,你对这个文件重新计算MD5时就会发现。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。
MD5还广泛用于加密和解密技术上,在很多操作系统中,用户的密码是以MD5值(或类似的其它算法)的方式保存的,用户Login的时候,系统是把用户输入的密码计算成MD5值,然后再去和系统中保存的MD5值进行比较,而系统并不“知道”用户的密码是什么。
一些黑客破获这种密码的方法是一种被称为“跑字典”的方法。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。
即使假设密码的最大长度为8,同时密码只能是字母和数字,共26+26+10=62个字符,排列组合出的字典的项数则是P(62,1)+P(62,2)….+P(62,8),那也已经是一个很天文的数字了,存储这个字典就需要TB级的磁盘组,而且这种方法还有一个前提,就是能获得目标账户的密码MD5值的情况下才可以。
在很多电子商务和社区应用中,管理用户的Account是一种最常用的基本功能,尽管很多Application Server提供了这些基本组件,但很多应用开发者为了管理的更大的灵活性还是喜欢采用关系数据库来管理用户,懒惰的做法是用户的密码往往使用明文或简单的变换后直接保存在数据库中,因此这些用户的密码对软件开发者或系统管理员来说可以说毫无保密可言,本文的目的是介绍MD5的Java Bean的实现,同时给出用MD5来处理用户的Account密码的例子,这种方法使得管理员和程序设计者都无法看到用户的密码,尽管他们可以初始化它们。但重要的一点是对于用户密码设置习惯的保护。
有兴趣的读者可以从这里取得MD5也就是RFC 1321的文本。 http://www.ietf.org/rfc/rfc1321.txt
实现策略
MD5的算法在RFC1321中实际上已经提供了C的实现,我们其实马上就能想到,至少有两种用Java实现它的方法,第一种是,用Java语言重新写整个算法,或者再说简单点就是把C程序改写成Java程序。第二种是,用JNI(Java Native Interface)来实现,核心算法仍然用这个C程序,用Java类给它包个壳。
但我个人认为,JNI应该是Java为了解决某类问题时的没有办法的办法(比如与操作系统或I/O设备密切相关的应用),同时为了提供和其它语言的互操作性的一个手段。使用JNI带来的最大问题是引入了平台的依赖性,打破了SUN所鼓吹的“一次编写到处运行”的Java好处。因此,我决定采取第一种方法,一来和大家一起尝试一下“一次编写到处运行”的好处,二来检验一下Java 2现在对于比较密集的计算的效率问题。
实现过程
限于这篇文章的篇幅,同时也为了更多的读者能够真正专注于问题本身,我不想就某一种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 ("") =
MD5 ("a") =
MD5 ("abc") =
MD5 ("message digest") =
MD5 ("abcdefghijklmnopqrstuvwxyz") =
……
这些输出结果的含义是指:空字符串””的MD5值是,字符串”a”的MD5值是……
编译并运行我们的程序:
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的机器上,执行后得到同样结果,确实是“一次编写到处运行了”。
Java Bean简述
现在,我们已经完成并简单测试了这个Java Class,我们文章的标题是做一个Java Bean。
其实普通的Java Bean很简单,并不是什么全新的或伟大的概念,就是一个Java的Class,尽管 Sun规定了一些需要实现的方法,但并不是强制的。而EJB(Enterprise Java Bean)无非规定了一些必须实现(非常类似于响应事件)的方法,这些方法是供EJB Container使用(调用)的。
在一个Java Application或Applet里使用这个bean非常简单,最简单的方法是你要使用这个类的源码工作目录下建一个beartool目录,把这个class文件拷贝进去,然后在你的程序中import beartool.MD5就可以了。最后打包成.jar或.war是保持这个相对的目录关系就行了。
Java还有一个小小的好处是你并不需要摘除我们的MD5类中那个main方法,它已经是一个可以工作的Java Bean了。Java有一个非常大的优点是她允许很方便地让多种运行形式在同一组代码中共存,比如,你可以写一个类,它即是一个控制台Application和GUI Application,同时又是一个Applet,同时还是一个Java Bean,这对于测试、维护和发布程序提供了极大的方便,这里的测试方法main还可以放到一个内部类中,有兴趣的读者可以参考: http://www.cn.ibm.com/developerWorks/java/jw-tips/tip106/index.shtml
这里讲述了把测试和示例代码放在一个内部静态类的好处,是一种不错的工程化技巧和途径。
把Java Bean装到JSP里
正如我们在本文开头讲述的那样,我们对这个MD5 Bean的应用是基于一个用户管理,这里我们假设了一个虚拟社区的用户login过程,用户的信息保存在数据库的个名为users的表中。这个表有两个字段和我们的这个例子有关,userid :char(20)和pwdmd5 :char(32),userid是这个表的Primary Key,pwdmd5保存密码的MD5串,MD5值是一个128bit的大整数,表示成16进制的ASCII需要32个字符。
这里给出两个文件,login.html是用来接受用户输入的form,login.jsp用来模拟使用MD5 Bean的login过程。
为了使我们的测试环境简单起见,我们在JSP中使用了JDK内置的JDBC-ODBC Bridge Driver,community是ODBC的DSN的名字,如果你使用其它的JDBC Driver,替换掉login.jsp中的
Connection con= DriverManager.getConnection("jdbc:odbc:community", "", "");
即可。
login.jsp的工作原理很简单,通过post接收用户输入的UserID和Password,然后将Password变换成MD5串,然后在users表中寻找UserID和pwdmd5,因为UserID是users表的Primary Key,如果变换后的pwdmd5与表中的记录不符,那么SQL查询会得到一个空的结果集。
这里需要简单介绍的是,使用这个Bean只需要在你的JSP应用程序的WEB-INF/classes下建立一个beartool目录,然后将MD5.class拷贝到那个目录下就可以了。如果你使用一些集成开发环境,请参考它们的deploy工具的说明。在JSP使用一个java Bean关键的一句声明是程序中的第2行:
<jsp:useBean id='oMD5' scope='request' class='beartool.MD5'/>
这是所有JSP规范要求JSP容器开发者必须提供的标准Tag。
id=实际上是指示JSP Container创建Bean的实例时用的实例变量名。在后面的<%和%>之间的Java程序中,你可以引用它。在程序中可以看到,通过 pwdmd5=oMD5.getMD5ofStr (password)引用了我们的MD5 Java Bean提供的唯一一个公共方法: getMD5ofStr。
Java Application Server执行.JSP的过程是先把它预编译成.java(那些Tag在预编译时会成为java语句),然后再编译成.class。这些都是系统自动完成和维护的,那个.class也称为Servlet。当然,如果你愿意,你也可以帮助Java Application Server去干本该它干的事情,自己直接去写Servlet,但用Servlet去输出HTML那简直是回到了用C写CGI程序的恶梦时代。
如果你的输出是一个复杂的表格,比较方便的方法我想还是用一个你所熟悉的HTML编辑器编写一个“模板”,然后在把JSP代码“嵌入”进去。尽管这种JSP代码被有些专家指责为“空心粉”,它的确有个缺点是代码比较难管理和重复使用,但是程序设计永远需要的就是这样的权衡。我个人认为,对于中、小型项目,比较理想的结构是把数据表示(或不严格地称作WEB界面相关)的部分用JSP写,和界面不相关的放在Bean里面,一般情况下是不需要直接写Servlet的。
如果你觉得这种方法不是非常的OO(Object Oriented),你可以继承(extends)它一把,再写一个bean把用户管理的功能包进去。
到底能不能兼容?
我测试了三种Java应用服务器环境,Resin 1.2.3、Sun J2EE 1.2、IBM WebSphere 3.5,所幸的是这个Java Bean都没有任何问题,原因其实是因为它仅仅是个计算程序,不涉及操作系统,I/O设备。其实用其它语言也能简单地实现它的兼容性的,Java的唯一优点是,你只需提供一个形态的运行码就可以了。请注意“形态”二字,现在很多计算结构和操作系统除了语言本身之外都定义了大量的代码形态,很简单的一段C语言核心代码,转换成不同形态要考虑很多问题,使用很多工具,同时受很多限制,有时候学习一种新的“形态”所花费的精力可能比解决问题本身还多。比如光Windows就有EXE、Service、的普通DLL、COM DLL以前还有OCX等等等等,在Unix上虽说要简单一些,但要也要提供一个.h定义一大堆宏,还要考虑不同平台编译器版本的位长度问题。我想这是Java对我来说的一个非常重要的魅力吧。
MD5算法说明
一、补位
二、补数据长度
三、初始化MD5参数
四、处理位操作函数
五、主要变换过程
六、输出结果
补位:
MD5算法先对输入的数据进行补位,使得数据位长度LEN对512求余的结果是448。即数据扩展至K*512+448位。即K*64+56个字节,K为整数。
具体补位操作:补一个1,然后补0至满足上述要求。
补数据长度:
用一个64位的数字表示数据的原始长度B,把B用两个32位数表示。这时,数
据就被填补成长度为512位的倍数。
初始化MD5参数:
四个32位整数 (A,B,C,D) 用来计算信息摘要,初始化使用的是十六进制表
示的数字
A=0X01234567
B=0X89abcdef
C=0Xfedcba98
D=0X76543210
处理位操作函数:
X,Y,Z为32位整数。
F(X,Y,Z) = X&Y|NOT(X)&Z
G(X,Y,Z) = X&Z|Y?(Z)
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X|not(Z))
主要变换过程:
使用常数组T[1 ... 64], T[i]为32位整数用16进制表示,数据用16个32位
的整数数组M[]表示。
具体过程如下:
/* 处理数据原文 */
For i = 0 to N/16-1 do
/*每一次,把数据原文存放在16个元素的数组X中. */
For j = 0 to 15 do
Set X[j] to M[i*16+j].
end /结束对J的循环
/* Save A as AA, B as BB, C as CC, and D as DD.
*/
AA = A
BB = B
CC = C
DD = D
/* 第1轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3
22 4]
[ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7
22 8]
[ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA
11 22 12]
[ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15]
[BCDA 15 22 16]
/* 第2轮* */
/* 以 [abcd k s i]表示如下操作
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA
0 20 20]
[ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23]
[BCDA 4 20 24]
[ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA
8 20 28]
[ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA
12 20 32]
/* 第3轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35]
[BCDA 14 23 36]
[ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA
10 23 40]
[ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43]
[BCDA 6 23 44]
[ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47]
[BCDA 2 23 48]
/* 第4轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51]
[BCDA 5 21 52]
[ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55]
[BCDA 1 21 56]
[ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59]
[BCDA 13 21 60]
[ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63]
[BCDA 9 21 64]
/* 然后进行如下操作 */
A = A + AA
B = B + BB
C = C + CC
D = D + DD
end /* 结束对I的循环*/
输出结果。
❺ 如何编译运行一个简单的java程序
通常开发一个java应用程序可分为三个步骤:
1.创建一个带有文件扩展名 *.java 的源文件
1).使用编辑器(如记事本,小编使用的是notepad++),输入以下6行文本:
1 //一个简单的application例子:打印一行文本
2 class Hello {
3 public static void main (String args[]){
4 System.out.println("Hello Java,This is my first Java Application!");
5 }
6 }
输入完成,如下图所示
爪哇之路开启成功。