2012年9月6日
上篇博文中說(shuō)明了如何通過(guò)命令編譯及運(yùn)行自己的Java文件。但是當(dāng)前都是以項(xiàng)目為單位的,如何組織項(xiàng)目中的文件以及對(duì)項(xiàng)目的編譯運(yùn)行,是命令行編譯主要解決的問(wèn)題。
1. 項(xiàng)目組織 我們以下面的項(xiàng)目作為樣例來(lái)說(shuō)明:
Test/
|-- Test.jar
|-- classes
| |-- Main.class
| |-- OutterTest.class
| `-- inner
| `-- InnerTest.class
|-- run.py
|-- run.sh
`-- src
|-- Main.java
|-- OutterTest.java
`-- inner
`-- InnerTest.java
上面是一個(gè)項(xiàng)目:Test。其中,有兩個(gè)文件夾,src用來(lái)放置所有的源代碼,也就是.java文件;classes用來(lái)放置相應(yīng)的.class文件。Test.jar是最終生成的jar文件,run.py和run.sh是項(xiàng)目的腳本文件。下面列出三個(gè).java文件,只是簡(jiǎn)單的顯示一句話:
// Main.java
import inner.*;
public class Main
{
public static void main(String[] args)
{
System.out.println("main: hello word!");
OutterTest out1 = new OutterTest();
out1.hello();
InnerTest in1 = new InnerTest();
in1.hello();
}
}
// OutterTest.java
public class OutterTest
{
public void hello()
{
System.out.println("Hello OutterTest!");
}
}
// InnerTest.java
package inner;
public class InnerTest
{
public void hello()
{
System.out.println("Hello InnerTest!");
}
}
注意上面的InnerTest類,它在package inner中,所以將InnerTest.java放在inner文件夾下,這樣可以保證統(tǒng)一。在Eclipse中,新建一個(gè)類時(shí),會(huì)讓你填寫(xiě)package名,然后Eclipse會(huì)為你新新建一個(gè)相應(yīng)的文件夾。
2. 項(xiàng)目編譯 javac -d classes src/*.java src/inner/*.java 由上一篇知道,javac中-d表示”指定存放生成的類文件的位置“,也就是將生成的.class文件放在-d指定的文件夾中。需要指出的是,classes文件夾是手動(dòng)建立的。
另外,javac還可以批量編譯.java文件,上面的命令表示編譯src目錄下的所以.java文件、編譯src/inner目錄下的所有.java文件。這樣就可以批量編譯.java文件,并將生成的.class文件放在classes文件夾中。這里同樣要指出一點(diǎn),因?yàn)閜ackage inner的關(guān)系,會(huì)自動(dòng)建立inner文件夾,并將所有package inner下的類文件.class放在classes/inner下。
3.項(xiàng)目打包 jar -cvf Test.jar -C classes/ . 這個(gè)命令將會(huì)把classes下的所有文件(包括.class和文件夾等)打包為Test.jar文件。
上篇博客中,介紹了參數(shù)-C的意義:-C 更改為指定的目錄并包含其中的文件,如果有任何目錄文件, 則對(duì)其進(jìn)行遞歸處理。它相當(dāng)于使用 cd 命令轉(zhuǎn)指定目錄下。
注意后面的"."的用法,jar可以遞歸的打包文件夾,"."表示是當(dāng)前文件夾。如果執(zhí)行命令“jar -cvf Test.jar .”,表示將當(dāng)前目錄下的所有文件及文件夾打包。所以上面的命令的意思就是“將classes下的所有文件打包為Test.jar”。
4.項(xiàng)目運(yùn)行 java -cp Test.jar Main 通過(guò)上面的命令就可以執(zhí)行Test.jar中的Main.class。其中,cp指定了jar文件的位置。
5. 腳本文件 通過(guò)上面的幾步,我們就可以完成整個(gè)項(xiàng)目的編譯和運(yùn)行。而且,通過(guò)src和classes兩個(gè)文件夾將源文件和目標(biāo)文件分開(kāi),使項(xiàng)目比較整潔。但是如果每次編譯、運(yùn)行都要輸入上面一系列命令,還是比較繁瑣的,尤其當(dāng)項(xiàng)目文件較多時(shí),這時(shí)通過(guò)腳本文件管理整個(gè)項(xiàng)目是明智的選擇。
下面是項(xiàng)目的腳本文件run.py
import os
import sys
if __name__ == "__main__":
ProjectJar = "Test.jar"
if sys.argv[1] == "c":
print("Compile program.")
src = "src/*.java src/inner/*.java"
os.system("javac -d classes " + src)
os.system("jar -cvf " + ProjectJar + " -C classes/ .")
if sys.argv[1] == "r":
print("Run program.")
os.system("java -cp " + ProjectJar + " Main")
print("Over!")
通過(guò)這個(gè)腳本文件,可以使用"python run.py c"完成項(xiàng)目的編譯、打包;使用"python run.py r"運(yùn)行項(xiàng)目。
通過(guò)這篇文章,我們已經(jīng)了解了Java項(xiàng)目的管理方法,以及編譯、打包、運(yùn)行的命令行,最后介紹了使用腳本文件有效管理項(xiàng)目。
附件中包含整個(gè)項(xiàng)目,同時(shí)還包括一個(gè)run.sh,方便不熟悉python的人查看。
附件:
Test.tar
2012年8月22日
當(dāng)前大部分開(kāi)發(fā)者在開(kāi)發(fā)Java程序時(shí)使用Eclipse,它可以方便的進(jìn)行程序的編譯、打包和運(yùn)行。但是不使用IDE,在完全的命令行下進(jìn)行Java開(kāi)發(fā)者從未用過(guò)的。在命令行下進(jìn)行開(kāi)發(fā)不是用來(lái)在展現(xiàn)自己有多牛,而是通過(guò)命令行開(kāi)發(fā),可以對(duì)Java的編譯、jar包等各個(gè)部分有一個(gè)深入了解。
下面的幾篇博客將會(huì)對(duì)Java的編譯、打包和運(yùn)行方法由淺入深的進(jìn)行介紹。
在這里使用的操作系統(tǒng)是Linux,并提供相應(yīng)的shell和python腳本。
首先介紹一下三個(gè)常用的命令:javac、jar、java。每個(gè)命令都有不同的參數(shù),這些參數(shù)的用法會(huì)詳細(xì)介紹。
1. javac javac的功能是對(duì)java源代碼進(jìn)行編譯,將后綴為.java的文件編譯為.class的文件。javac的一般格式是
javac <選項(xiàng)> <源文件>
例如:
javac Main.java
會(huì)產(chǎn)生Main.class文件。
javac的常用選項(xiàng)有:
-classpath <路徑> 指定查找用戶類文件的位置
-cp <路徑> 指定查找用戶類文件的位置(與上面的選項(xiàng)一樣,cp是classpath的簡(jiǎn)寫(xiě))
-d <目錄> 指定存放生成的類文件的位置
2.jar jar的功能是根據(jù)選項(xiàng)將指定的一些.class文件打包為一個(gè)jar包。jar的一般格式是
jar {ctxui}[vfm0Me] [jar-文件] [manifest-文件] [-C 目錄](méi) 文件名 ...
例如,
jar cvf Test.jar Main.class Bar.class
它將Main.class和Bar.class打包為一個(gè)文件Test.jar。
jar命令的選項(xiàng)比較多,用到選項(xiàng)包括:
-c 創(chuàng)建新的歸檔文件
-t 列出歸檔目錄
-x 從檔案中提取指定的 (或所有) 文件
-u 更新現(xiàn)有的歸檔文件
-v 在標(biāo)準(zhǔn)輸出中生成詳細(xì)輸出
-f 指定歸檔文件名
-m 包含指定清單文件中的清單信息
-e 為捆綁到可執(zhí)行 jar 文件的獨(dú)立應(yīng)用程序,指定應(yīng)用程序入口點(diǎn)
-C 更改為指定的目錄并包含其中的文件,如果有任何目錄文件, 則對(duì)其進(jìn)行遞歸處理。
例如,
上例中的cvf參數(shù),分別表示創(chuàng)建新的jar文件、創(chuàng)建時(shí)顯示jar包的信息(可以執(zhí)行看一下)、指定jar包名為Test.jar。
jar tf Test.jar 查看Test.jar的內(nèi)容,其中t表示列出jar包內(nèi)容,f指定jar包名
jar xf Test.jar 解壓Test.jar文件
jar xf Test.jar Main.class 僅解壓Test.jar中的Main.class文件
這里要指出的是,f/m/e都指定一個(gè)名稱(jar包名, 清單文件名和入口點(diǎn)名稱),相應(yīng)的名稱順序與參數(shù)的順序要一致。
3.java java的功能是執(zhí)行應(yīng)用程序。java的一般格式是
執(zhí)行一個(gè)類: java [ options ] class [ argument ... ]
執(zhí)行一個(gè)jar包:java [ options ] -jar file.jar [ argument ... ]
例如:
java Main
執(zhí)行Main.class,注意上面沒(méi)有.class后綴
java Test.jar
執(zhí)行一個(gè)jar包,這個(gè)jar包中要指定了程序入口點(diǎn)(通過(guò)在MANIFEST.MF文件中指定)。
常用的java的選項(xiàng):
-classpath<類搜索路徑> 指定用戶類文件的位置,可能為文件夾、zip、jar文件
總結(jié)
通過(guò)這篇內(nèi)容,我們應(yīng)用學(xué)會(huì)了如何使用javac編譯自己的類,并使用java執(zhí)行自己的類。但是關(guān)于打包的操作及jar的執(zhí)行比較復(fù)雜,將在以后繼續(xù)介紹。
2012年8月17日
Java中計(jì)算中文的MD5值
前幾天的工作中,需要計(jì)算中文的MD5值,計(jì)算的函數(shù)接口及調(diào)用方式如下:
public static String getMD5(byte[] source);
String s = "中文編碼";
String md5_value = getMD5(s.getBytes());
其中g(shù)etBytes函數(shù)使用平臺(tái)默認(rèn)的字符集將string編碼為byte序列。由于平臺(tái)的中文編碼方式可能不同,所以同一中文經(jīng)過(guò)getBytes得到的二進(jìn)制是不一樣的。為保證每次得到的結(jié)果一致,或者使用指定的編碼方式得到byte序列,應(yīng)該在getBytes中使用參數(shù)。
String md5_value = getMD5(s.getBytes("utf-8"));
這樣得到的值就是一樣的。
2012年5月14日
crontab命令是Unix/Linux中的一個(gè)常用命令,用于設(shè)置周期性被執(zhí)行的指令。沒(méi)有用過(guò)的可以查一下,在運(yùn)行服務(wù)端程序時(shí)會(huì)經(jīng)常用到。 程序使用Java讀取一個(gè)含有中文的文件,進(jìn)行處理后,將結(jié)果寫(xiě)到一個(gè)結(jié)果文件中。在運(yùn)行的程序時(shí),出現(xiàn)了這樣一個(gè)問(wèn)題:在本地環(huán)境下,運(yùn)行正確;但是當(dāng)使用crontab定時(shí)執(zhí)行時(shí),得到的卻是錯(cuò)誤的結(jié)果。 經(jīng)過(guò)一定的調(diào)研發(fā)現(xiàn)原來(lái)是對(duì)crontab的機(jī)制沒(méi)有弄明白導(dǎo)致的。crontab運(yùn)行程序時(shí),會(huì)使用它自己的環(huán)境變量,這個(gè)環(huán)境變量與你本地的環(huán)境變量可能會(huì)不同。比如,在你本地情況下,環(huán)境變量的語(yǔ)言為en_US.UTF-8,而在crontab中,環(huán)境變量可能是zh_CN.GBK,這樣會(huì)導(dǎo)致讀寫(xiě)文件時(shí)——尤其是讀寫(xiě)中文文件時(shí)內(nèi)容編碼錯(cuò)誤,進(jìn)而導(dǎo)致結(jié)果出錯(cuò)。 所以在使用Java讀寫(xiě)文件時(shí),一定要指定編碼格式,而不是使用環(huán)境變量的格式。例如下面的語(yǔ)句用于讀取utf-8格式的文件:
String encodeType = "utf-8";
File in_file = new File("test.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(in_file), encodeType));
網(wǎng)上也有關(guān)于修改crontab環(huán)境變量的方法,但是我不建議使用,因?yàn)橐粋€(gè)系統(tǒng)可能是很多人共用的,修改crontab環(huán)境變量可能會(huì)引起他人程序的問(wèn)題,最好修改自己的程序,保證它不依賴具體的環(huán)境變量。
2011年7月20日
很多同學(xué)和朋友經(jīng)常向我抱怨說(shuō),算法已經(jīng)成為他們學(xué)習(xí)和工作中的一個(gè)負(fù)擔(dān)。在他們看來(lái),算法只能應(yīng)付一下考試,在學(xué)習(xí)中沒(méi)有任何用處;也有人說(shuō),求職過(guò)程中考算法純粹是浪費(fèi)精力,在實(shí)際工作中沒(méi)有用到任何算法。
在我的本科四年中,花了大量時(shí)間學(xué)習(xí)算法,參加算法比賽——成績(jī)很濫,朋友們都說(shuō),和我的付出嚴(yán)重不成比例,但是我一點(diǎn)也不后悔花費(fèi)時(shí)間學(xué)習(xí)算法。算法讓我在平時(shí)的學(xué)習(xí)中應(yīng)對(duì)各個(gè)科目都從容自如,學(xué)習(xí)起來(lái)事半功倍。
在平時(shí)的學(xué)習(xí)交流中,有時(shí)看其他人的代碼相當(dāng)別扭,不是看不懂(有時(shí)真看不懂),而是很納悶,為什么要這么寫(xiě)呢,明明有很明顯的順暢的思路,干嗎要寫(xiě)這么繞呢?我將我覺(jué)得正確的思路寫(xiě)出來(lái)的時(shí)候,對(duì)方也會(huì)覺(jué)得這樣寫(xiě)順暢了很多,代碼也小了很多。
在一般應(yīng)用中,不會(huì)說(shuō)讓你寫(xiě)一個(gè)KMP算法,或者給你設(shè)計(jì)一個(gè)動(dòng)態(tài)規(guī)劃的題目讓你來(lái)解。但是如果你會(huì)這些算法,掌握了這種優(yōu)化的思想,在平時(shí)的編寫(xiě)中會(huì)不知不覺(jué)的寫(xiě)出比較高效的代碼。
有人說(shuō),現(xiàn)在中國(guó)的軟件現(xiàn)狀就是這樣,不在乎你實(shí)現(xiàn)的有多快,只會(huì)在乎你有沒(méi)有實(shí)現(xiàn)所有的功能點(diǎn),隨著硬件條件的改善,一般情況下都能滿足要求。這樣說(shuō)也是沒(méi)錯(cuò),如果你甘愿只做一名碼工,因?yàn)閷?duì)于相當(dāng)大的一部分軟件,效率并不是最重要的,甚至第二、第三重要都排不上。但是對(duì)于企業(yè)級(jí)的應(yīng)用和軟件的核心部分,往往效率是非常重要的。
如果將編程和寫(xiě)作類比的話,可以進(jìn)行這個(gè)的比較。編程必須會(huì)語(yǔ)言C/C++、Java、Python等,寫(xiě)作也一定要會(huì)一個(gè)語(yǔ)言漢語(yǔ)、英語(yǔ)、法語(yǔ),當(dāng)然計(jì)算機(jī)語(yǔ)言有效率的優(yōu)劣以及適合的應(yīng)用的場(chǎng)景,而自然語(yǔ)言沒(méi)有這種屬性。算法修養(yǎng)之于編程就像文學(xué)修養(yǎng)之于創(chuàng)作,沒(méi)有一定的文學(xué)修養(yǎng)就寫(xiě)不出好的作品,同樣沒(méi)有良好的算法修養(yǎng)也創(chuàng)造不出好的代碼。數(shù)據(jù)結(jié)構(gòu)就像寫(xiě)作中的排比句等,它們是骨架,結(jié)構(gòu)化編程就像是寫(xiě)作中的段落,它們使得層次分明。
在靜靜的夜晚,明月透過(guò)窗子,照進(jìn)小屋,頓時(shí)一種異樣的情懷涌上心頭。這時(shí)有下面幾種人,第一種,嚎啕大哭,“媽媽……”;第二種,潸然淚下,“我嚓,想家了是不”;第三種,沉思狀,“有首詩(shī)可以表達(dá)這個(gè)情懷”,拿起全唐詩(shī),查找了整晚,夜盡天明時(shí),驚呼“找到了,原來(lái)是‘舉頭望明月,底頭思故鄉(xiāng)’啊,我記得有的。”;第四種,徘徊低吟“舉頭望明月,底頭思故鄉(xiāng)”;第五種,心中油然而生,“獨(dú)在異鄉(xiāng)為異客,明月千里寄鄉(xiāng)思”。
同樣地,這也反映了一個(gè)程序員算法修養(yǎng)的幾種境界。第一種,菜鳥(niǎo),不會(huì)表達(dá)自己的思想,這種多見(jiàn)于初學(xué)語(yǔ)言的人,只會(huì)寫(xiě)hello world之類程序的人;第二種,入門,可以表達(dá)出自己的想法,但是不懂什么算法,只是憑自己對(duì)計(jì)算機(jī)語(yǔ)言的理解寫(xiě)程序,當(dāng)然寫(xiě)出的程序功能點(diǎn)可能不少,但是不完善,潛在的bug很多;第三種,有一定的算法基礎(chǔ),但是沒(méi)有掌握完全,寫(xiě)程序時(shí)有高效的意識(shí),對(duì)一些核心程序,看著資料也能寫(xiě)出相應(yīng)的代碼,但是不能活學(xué)活用;第四種,有不錯(cuò)的算法修養(yǎng),寫(xiě)出的代碼清晰高效,可勝任關(guān)鍵代碼的編寫(xiě);第五種,有很高的算法修養(yǎng),理解了算法的精髓,自己可以得出算法供世人學(xué)習(xí),一般是大師級(jí)人物。國(guó)內(nèi)有相當(dāng)一部分程序員就是處于第二階段,他們也有穩(wěn)定的工作,甚至不錯(cuò)的收入,但是總感覺(jué)遇到了瓶頸,自己的能力躊躇不前。
有人說(shuō),現(xiàn)在各種庫(kù)都有,那些代碼都非常高效,我只要熟練使用這些庫(kù)就行了,不用學(xué)習(xí)算法。可以達(dá)到熟練使用幾個(gè)庫(kù)的程序員也算是第三階段,但是要想熟練駕馭更多的庫(kù),將庫(kù)函數(shù)熟練的使用,沒(méi)有一定的算法基礎(chǔ)是不行的。比如,如果你想熟練使用STL庫(kù),那么對(duì)各種容器和算法都應(yīng)該有一定的認(rèn)識(shí),什么時(shí)候使用list,為什么快排不能應(yīng)用于list?要想回答這些問(wèn)題,必須掌握初步的算法。
對(duì)于想要在技術(shù)上有一定作為的程序員,沒(méi)有算法修養(yǎng),就如同金庸小說(shuō)中的學(xué)武之人不學(xué)習(xí)內(nèi)功,它可以將外功學(xué)的爐火純清,憑著這份功力可以走遍半個(gè)江湖,但是一旦遇到高手就掛了;反之如果有內(nèi)功修為,學(xué)習(xí)外功可學(xué)一知十,觸類旁通,內(nèi)功修為越高將來(lái)的成就越大。
學(xué)習(xí)算法吧!
2011年1月21日
最近做數(shù)據(jù)時(shí),使用神經(jīng)網(wǎng)絡(luò)建模。在神經(jīng)網(wǎng)絡(luò)中,會(huì)用到激發(fā)函數(shù)(activation function)。
典型的激發(fā)函數(shù)有Sigmod函數(shù):
雙曲正切函數(shù):
這兩個(gè)都涉及到指數(shù)函數(shù),在C中,為求指數(shù)函數(shù),使用exp()函數(shù)。
在數(shù)次出錯(cuò)后找到問(wèn)題,原來(lái)是我的指數(shù)值過(guò)大,數(shù)據(jù)中有時(shí)會(huì)出現(xiàn)超過(guò)1000的數(shù)字,這導(dǎo)致在求值過(guò)程中,即使使用double型,也使得結(jié)果溢出。
解決方法是定義一個(gè)指數(shù)函數(shù),當(dāng)指數(shù)值超過(guò)一定界限便指定一個(gè)相對(duì)無(wú)窮大的值,這樣也符合數(shù)學(xué)定義。在我的處理中,將界限設(shè)定為15,當(dāng)該值大于15時(shí),返回3000000;當(dāng)界限值小于-15時(shí),返回0。
2010年4月11日
在配置文件共享時(shí),對(duì)沒(méi)有經(jīng)驗(yàn)的用戶會(huì)出現(xiàn)很多問(wèn)題,這里介紹三點(diǎn)注意事項(xiàng):
1. 首先打開(kāi)菜單中“工具”->“文件夾選項(xiàng)”->“查看”,確認(rèn)“使用簡(jiǎn)單文件共享”沒(méi)有被選上;
2. 右鍵“我的電腦”,“管理”->“系統(tǒng)工具”->“本地用戶和組”->“用戶”,右鍵“Guest”,“屬性”,在“常規(guī)”選項(xiàng)卡中,取消“賬戶已停用”;
3. 打開(kāi)“控制面板”->“管理工具”->“本地安全策略”,打開(kāi)“本地策略”->“用戶權(quán)利指派”,雙擊“拒絕從網(wǎng)絡(luò)訪問(wèn)這臺(tái)計(jì)算機(jī)”,確認(rèn)其中的“guest”已刪除。
本文針對(duì)XP用戶,其它用戶可參照。
2009年4月29日
賦值運(yùn)算符和復(fù)制構(gòu)造函數(shù)都是用已存在A的對(duì)象來(lái)創(chuàng)建另一個(gè)對(duì)象B。不同之處在于:賦值運(yùn)算符處理兩個(gè)已有對(duì)象,即賦值前B應(yīng)該是存在的;復(fù)制構(gòu)造函數(shù)是生成一個(gè)全新的對(duì)象,即調(diào)用復(fù)制構(gòu)造函數(shù)之前B不存在。
CTemp a(b); //復(fù)制構(gòu)造函數(shù),C++風(fēng)格的初始化
CTemp a=b; //仍然是復(fù)制構(gòu)造函數(shù),不過(guò)這種風(fēng)格只是為了與C兼容,與上面的效果一樣
在這之前a不存在,或者說(shuō)還未構(gòu)造好。
CTemp a;
a=b; //賦值運(yùn)算符
在這之前a已經(jīng)通過(guò)默認(rèn)構(gòu)造函數(shù)構(gòu)造完成。
我覺(jué)得將賦值運(yùn)算符稱為“
賦值構(gòu)造函數(shù)”是錯(cuò)誤的,構(gòu)造函數(shù)發(fā)生在對(duì)象創(chuàng)建時(shí)期,而賦值是運(yùn)算符,發(fā)生在“運(yùn)算”時(shí)期,賦值運(yùn)算前,對(duì)象已經(jīng)構(gòu)造完成,所以不能稱之為“構(gòu)造函數(shù)”。
一家之言!!