JAVA中的傳遞都是值傳遞嗎?有沒有引用傳遞呢?
在回答這兩個(gè)問題前,讓我們首先來看一段代碼:
- public class ParamTest {
-
- protected int num = 0;
-
-
- public void change(int i) {
- i = 5;
- }
-
-
- public void change(ParamTest t) {
- ParamTest tmp = new ParamTest();
- tmp.num = 9;
- t = tmp;
- }
-
-
- public void add(int i) {
- i += 10;
- }
-
-
- public void add(ParamTest pt) {
- pt.num += 20;
- }
-
- public static void main(String[] args) {
- ParamTest t = new ParamTest();
-
- System.out.println("參數(shù)--基本類型");
- System.out.println("原有的值:" + t.num);
-
- t.change(t.num);
- System.out.println("賦值之后:" + t.num);
-
- t.change(t);
- System.out.println("運(yùn)算之后:" + t.num);
-
- System.out.println();
-
- t = new ParamTest();
- System.out.println("參數(shù)--引用類型");
- System.out.println("原有的值:" + t.num);
-
- t.add(t.num);
- System.out.println("賦引用后:" + t.num);
-
- t.add(t);
- System.out.println("改屬性后:" + t.num);
- }
- }
public class ParamTest {
// 初始值為0
protected int num = 0;
// 為方法參數(shù)重新賦值
public void change(int i) {
i = 5;
}
// 為方法參數(shù)重新賦值
public void change(ParamTest t) {
ParamTest tmp = new ParamTest();
tmp.num = 9;
t = tmp;
}
// 改變方法參數(shù)的值
public void add(int i) {
i += 10;
}
// 改變方法參數(shù)屬性的值
public void add(ParamTest pt) {
pt.num += 20;
}
public static void main(String[] args) {
ParamTest t = new ParamTest();
System.out.println("參數(shù)--基本類型");
System.out.println("原有的值:" + t.num);
// 為基本類型參數(shù)重新賦值
t.change(t.num);
System.out.println("賦值之后:" + t.num);
// 為引用型參數(shù)重新賦值
t.change(t);
System.out.println("運(yùn)算之后:" + t.num);
System.out.println();
t = new ParamTest();
System.out.println("參數(shù)--引用類型");
System.out.println("原有的值:" + t.num);
// 改變基本類型參數(shù)的值
t.add(t.num);
System.out.println("賦引用后:" + t.num);
// 改變引用類型參數(shù)所指向?qū)ο蟮膶傩灾?
t.add(t);
System.out.println("改屬性后:" + t.num);
}
}
這段代碼的運(yùn)行結(jié)果如下:
- 參數(shù)--基本類型
- 原有的值:0
- 賦值之后:0
- 運(yùn)算之后:0
-
- 參數(shù)--引用類型
- 原有的值:0
- 賦引用后:0
- 改屬性后:20
從上面這個(gè)直觀的結(jié)果中我們很容易得出如下結(jié)論:
- 對(duì)于基本類型,在方法體內(nèi)對(duì)方法參數(shù)進(jìn)行重新賦值,并不會(huì)改變?cè)凶兞康闹怠?/li>
- 對(duì)于引用類型,在方法體內(nèi)對(duì)方法參數(shù)進(jìn)行重新賦予引用,并不會(huì)改變?cè)凶兞克钟械囊谩?/li>
- 方法體內(nèi)對(duì)參數(shù)進(jìn)行運(yùn)算,不影響原有變量的值。
- 方法體內(nèi)對(duì)參數(shù)所指向?qū)ο蟮膶傩赃M(jìn)行運(yùn)算,將改變?cè)凶兞克赶驅(qū)ο蟮膶傩灾怠?/li>
上面總結(jié)出來的不過是我們所看到的表面現(xiàn)象。那么,為什么會(huì)出現(xiàn)這樣的現(xiàn)象呢?這就要說到值傳遞和引用傳遞的概念了。這個(gè)問題向來是頗有爭(zhēng)議的。
大家都知道,在JAVA中變量有以下兩種:
- 基本類型變量,包括char、byte、short、int、long、float、double、boolean。
- 引用類型變量,包括類、接口、數(shù)組(基本類型數(shù)組和對(duì)象數(shù)組)。
當(dāng)基本類型的變量被當(dāng)作參數(shù)傳遞給方法時(shí),JAVA虛擬機(jī)所做的工作是把這個(gè)值拷貝了一份,然后把拷貝后的值傳遞到了方法的內(nèi)部。因此在上面的例子中,我們回頭來看看這個(gè)方法:
-
- public void change(int i) {
- i = 5;
- }
// 為方法參數(shù)重新賦值
public void change(int i) {
i = 5;
}
在這個(gè)方法被調(diào)用時(shí),變量i和ParamTest型對(duì)象t的屬性num具有相同的值,卻是兩個(gè)不同變量。變量i是由JAVA虛擬機(jī)創(chuàng)建的作用域在
change(int i)方法內(nèi)的局部變量,在這個(gè)方法執(zhí)行完畢后,它的生命周期就結(jié)束了。在JAVA虛擬機(jī)中,它們是以類似如下的方式存儲(chǔ)的:
很明顯,在基本類型被作為參數(shù)傳遞給方式時(shí),是值傳遞,在整個(gè)過程中根本沒有牽扯到引用這個(gè)概念。這也是大家所公認(rèn)的。對(duì)于布爾型變量當(dāng)然也是如此,請(qǐng)看下面的例子:
- public class BooleanTest {
-
- boolean bool = true;
-
-
- public void change(boolean b) {
- b = false;
- }
-
-
- public void calculate(boolean b) {
- b = b && false;
-
- System.out.println("b運(yùn)算后的值:" + b);
- }
-
- public static void main(String[] args) {
- BooleanTest t = new BooleanTest();
-
- System.out.println("參數(shù)--布爾型");
- System.out.println("原有的值:" + t.bool);
-
- t.change(t.bool);
- System.out.println("賦值之后:" + t.bool);
-
-
- t.calculate(t.bool);
- System.out.println("運(yùn)算之后:" + t.bool);
- }
- }
public class BooleanTest {
// 布爾型值
boolean bool = true;
// 為布爾型參數(shù)重新賦值
public void change(boolean b) {
b = false;
}
// 對(duì)布爾型參數(shù)進(jìn)行運(yùn)算
public void calculate(boolean b) {
b = b && false;
// 為了方便對(duì)比,將運(yùn)算結(jié)果輸出
System.out.println("b運(yùn)算后的值:" + b);
}
public static void main(String[] args) {
BooleanTest t = new BooleanTest();
System.out.println("參數(shù)--布爾型");
System.out.println("原有的值:" + t.bool);
// 為布爾型參數(shù)重新賦值
t.change(t.bool);
System.out.println("賦值之后:" + t.bool);
// 改變布爾型參數(shù)的值
t.calculate(t.bool);
System.out.println("運(yùn)算之后:" + t.bool);
}
}
輸出結(jié)果如下:
- 參數(shù)--布爾型
- 原有的值:true
- 賦值之后:true
- b運(yùn)算后的值:false
- 運(yùn)算之后:true
那么當(dāng)引用型變量被當(dāng)作參數(shù)傳遞給方法時(shí)JAVA虛擬機(jī)又是怎樣處理的呢?同樣,它會(huì)拷貝一份這個(gè)變量所持有的引用,然后把它傳遞給JAVA虛擬機(jī)為方法
創(chuàng)建的局部變量,從而這兩個(gè)變量指向了同一個(gè)對(duì)象。在篇首所舉的示例中,ParamTest類型變量t和局部變量pt在JAVA虛擬機(jī)中是以如下的方式存
儲(chǔ)的:
有一種說法是當(dāng)一個(gè)對(duì)象或引用類型變量被當(dāng)作參數(shù)傳遞時(shí),也是值傳遞,這個(gè)值就是對(duì)象的引用,因此JAVA中只有值傳遞,沒有引用傳遞。還有一種說法是引
用可以看作是對(duì)象的別名,當(dāng)對(duì)象被當(dāng)作參數(shù)傳遞給方法時(shí),傳遞的是對(duì)象的引用,因此是引用傳遞。這兩種觀點(diǎn)各有支持者,但是前一種觀點(diǎn)被絕大多數(shù)人所接
受,其中有《Core Java》一書的作者,以及JAVA的創(chuàng)造者James Gosling,而《Thinking in
Java》一書的作者Bruce Eckel則站在了中立的立場(chǎng)上。
我個(gè)人認(rèn)為值傳遞中的值指的是基本類型的數(shù)值,即使對(duì)于布爾型,雖然它的表現(xiàn)形式為true和false,但是在棧中,它仍然是以數(shù)值形式保存的,即0表
示false,其它數(shù)值表示true。而引用是我們用來操作對(duì)象的工具,它包含了對(duì)象在堆中保存地址的信息。即使在被作為參數(shù)傳遞給方法時(shí),實(shí)際上傳遞的
是它的拷貝,但那仍是引用。因此,用引用傳遞來區(qū)別與值傳遞,概念上更加清晰。
最后我們得出如下的結(jié)論:
- 基本類型和基本類型變量被當(dāng)作參數(shù)傳遞給方法時(shí),是值傳遞。在方法實(shí)體中,無法給原變量重新賦值,也無法改變它的值。
- 對(duì)象和引用型變量被當(dāng)作參數(shù)傳遞給方法時(shí),在方法實(shí)體中,無法給原變量重新賦值,但是可以改變它所指向?qū)ο蟮膶傩浴V劣诘降姿侵祩鬟f還是引用傳遞,這并不重要,重要的是我們要清楚當(dāng)一個(gè)引用被作為參數(shù)傳遞給一個(gè)方法時(shí),在這個(gè)方法體內(nèi)會(huì)發(fā)生什么。
什么叫引用?只因?yàn)檫@個(gè)變量的值和其它的不一樣.
首先理解:都是變量
int i;
ArrayList b;
i和b都是變量.
但i是基本變量,也叫原始變量.
其它的就叫引用變量,因?yàn)樗闹凳且粋€(gè)內(nèi)存地址值.引用對(duì)象的.但記住:它們都是有一個(gè)值的!i是一個(gè)數(shù)字,而b是一個(gè)內(nèi)存地址值(簡(jiǎn)單的說是一個(gè)十六進(jìn)
制的值).除了基本變量之外的變量都是引用變量.Vector a;這里的a也是一個(gè)變量.它也是有值的,它的值是一個(gè)十六進(jìn)制的值.
變量的賦值:
int i=10;
int j=i;
//這里把i的值10給了j,所以j的值也是10
ArrayList b=new ArrayList();
ArrayList c=b;
//首先,b是一個(gè)引用變量,它的"值":是一個(gè)內(nèi)存地址值!!! new
ArrayList()要分配一段內(nèi)存保存它們,怎么樣找到這段內(nèi)存?那就是通過b里的值了.b的值就是new
ArrayList()所占內(nèi)存的首地址.然后c也是一個(gè)引用變量,它的值(地址值)和b是一樣的.也就是new
ArrayList()所占內(nèi)存的首地址.所以當(dāng)通過b或者c進(jìn)行操作時(shí),它們都是操作同一個(gè)對(duì)象的.
在方法調(diào)用的時(shí)候,方法的參數(shù)實(shí)際也就是一個(gè)變量.如果是基本類型變量的時(shí)候,假設(shè)有方法method(int aa);
int j=10;
method(j);
這里邊,int aa實(shí)際也是定義了一個(gè)變量,調(diào)用的時(shí)候把j的值:10也給了aa.所以aa也是10,改變了aa的值并不會(huì)改變j的值.
如果是引用變量的時(shí)候,假設(shè)有方法methodA(ArrayList aa);
ArrayList b = new ArrayList();
methodA(b);
//方法定義了變量aa,調(diào)用的時(shí)候把b的值(地址值!!!!!)給了aa,所以aa與b有一樣的值(地址值!!!!),在方法里通過aa去操作的時(shí)候,b所引用的對(duì)象也就被改變了,因?yàn)樗鼈円猛粋€(gè)對(duì)象.
紙 a = new 銀行帳戶();//開一個(gè)銀行帳戶,返回一個(gè)卡號(hào)給你,寫在你的紙a里邊.
用一張紙(引用變量),把你的銀行卡號(hào)寫在上邊,然后調(diào)用我的時(shí)候,我用另外一張紙(引用變量---方法的形數(shù)),把你的號(hào)碼抄過來.然后我通過這個(gè)卡號(hào),去到銀行找到你的帳號(hào),給你存點(diǎn)錢.
然后你用你的紙(引用變量)上的卡號(hào) <沒變,還是那個(gè)卡號(hào)>再去查詢銀行帳號(hào)的時(shí)候就會(huì)發(fā)現(xiàn)了多了一些錢了.....
說說我對(duì)值傳遞和引用傳遞的看法:
首先我認(rèn)為,大家對(duì)Java傳遞參數(shù)的行為是清楚的,這個(gè)爭(zhēng)論只是一個(gè)語(yǔ)義上的爭(zhēng)論。
也就是我們是否需要區(qū)分值傳遞和應(yīng)用傳遞呢?或者說這樣的區(qū)分有沒有意義?是否合理?
博主認(rèn)為存在引用傳遞的關(guān)鍵點(diǎn)在于,傳遞的對(duì)象地址值,本質(zhì)上它是一個(gè)引用,無論它是否被copy過。
認(rèn)為只有值傳遞的關(guān)鍵點(diǎn)在于,傳遞的對(duì)象地址值,它是一個(gè)值的copy,這個(gè)值代表的意義無所謂。
引用是c++里的概念,由于java跟c++是有一定關(guān)系的,這里把引用遷移過來,如果合理未嘗不可。
c++中關(guān)于引用的解釋一般喜歡說是看作“別名”,我查了幾本書,大部分提到引用并不會(huì)分配內(nèi)存空間,也有一本書提到,某些編譯器會(huì)分配存儲(chǔ)空間來存儲(chǔ)被引用對(duì)象的地址。
那么還是回到語(yǔ)義上來,c++里的這個(gè)引用,語(yǔ)義上是“別名”的意思,我的理解是,一組指向同一個(gè)對(duì)象的別名應(yīng)該只存儲(chǔ)一份內(nèi)存地址。當(dāng)然具體實(shí)現(xiàn)可能會(huì)
把引用當(dāng)做一個(gè)不可變的指針來處理(每個(gè)別名都存儲(chǔ)自己的對(duì)象地址)。但是請(qǐng)注意,我們應(yīng)該關(guān)注于它的語(yǔ)義,即:它沒有任何值的copy,即使是一個(gè)地
址,只是另外一個(gè)名字而已。
但是java里面沒有這樣的概念,所有的地址傳遞其行為是值的傳遞方式,語(yǔ)義上統(tǒng)一成值傳遞更為清晰,我們只需要考慮這個(gè)值具體是什么,無非兩種,要么是基本類型值,要么是個(gè)地址。
所以我認(rèn)為這個(gè)“引用”的概念放到j(luò)ava中并不合適。只有值傳遞的說法更合理。
Linux 發(fā)展到今天,可用的軟件已經(jīng)非常多了。這樣自然會(huì)有一些軟件的功能大致上相同。例如,同樣是編輯器,就有
nvi、vim、emacs、nano,而且我說的這些還只是一部分。大多數(shù)情況下,這樣的功能相似的軟件都是同時(shí)安裝在系統(tǒng)里的,可以用它們的名稱來執(zhí)
行。例如,要執(zhí)行 vim,只要在終端下輸入 vim
并按回車就可以了。不過,有些情況下我們需要用一個(gè)相對(duì)固定的命令調(diào)用這些程序中的一個(gè)。例如,當(dāng)我們寫一個(gè)腳本程序時(shí),只要寫下
editor,而不希望要為“編輯器是哪個(gè)”而操心。Debian 提供了一種機(jī)制來解決這個(gè)問題,而 update-alternatives
就是用來實(shí)現(xiàn)這種機(jī)制的。
在說明 update-alternatives 的詳細(xì)內(nèi)容之間,先讓我們看看系統(tǒng)中已有的例子。打開終端,執(zhí)行下面的命令:
herbert@natsu:~$ ls -l /usr/bin/editor
lrwxrwxrwx 1 root root 24 2004-09-26 08:48 /usr/bin/editor -> /etc/alternatives/editor
herbert@natsu:~$ ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 12 2004-10-27 16:24 /etc/alternatives/editor -> /usr/bin/vim
herbert@natsu:~$
我
們看到,editor 這個(gè)可執(zhí)行命令實(shí)際上是個(gè)符號(hào)鏈接,它指向 /etc/alternatives/editor;而
/etc/alternatives/editor 也是個(gè)符號(hào)鏈接,它指向 /usr/bin/vim。這樣,當(dāng)我輸入 editor
并回車時(shí),將執(zhí)行 vim。之所以要在 /usr/bin 和 /etc/alternatives
中費(fèi)心建立這樣兩個(gè)鏈接,就是要實(shí)現(xiàn)上面說到的特性:方便腳本
程序的編寫和系統(tǒng)的管理。
下面我們就來看看 update-alternatives 的功能。當(dāng)然,如果你覺得我說得不詳細(xì),可以看看這個(gè)命令的 manpage:UPDATE-ALTERNATIVES(8)。
首先要介紹的參數(shù)是 --display。它使我們可以看到一個(gè)命令的所有可選命令。執(zhí)行
natsu:/home/herbert# update-alternatives --display editor
editor - status is auto.
link currently points to /usr/bin/vim
/bin/ed - priority -100
slave editor.1.gz: /usr/share/man/man1/ed.1.gz
/usr/bin/nvi - priority 19
slave editor.1.gz: /usr/share/man/man1/nvi.1.gz
/bin/nano - priority 40
slave editor.1.gz: /usr/share/man/man1/nano.1.gz
/usr/bin/vim - priority 120
slave editor.1.gz: /usr/share/man/man1/vim.1.gz
/usr/bin/emacs21 - priority 0
slave editor.1.gz: /usr/share/man/man1/emacs.1emacs21.gz
Current `best' version is /usr/bin/vim.
natsu:/home/herbert#
你可以看到我的機(jī)器上的所有可以用來被 editor 鏈接的命令。
下面說說 --config。這個(gè)選項(xiàng)使我們可以選擇其中一個(gè)命令:
natsu:/home/herbert# update-alternatives --config editor
There are 5 alternatives which provide `editor'.
Selection Alternative
-----------------------------------------------
1 /bin/ed
2 /usr/bin/nvi
3 /bin/nano
*+ 4 /usr/bin/vim
5 /usr/bin/emacs21
Press enter to keep the default[*], or type selection number: 4
Using `/usr/bin/vim' to provide `editor'.
natsu:/home/herbert#
我并沒有修改它,因?yàn)槲疫€是比較喜歡 vim 的。當(dāng)然,你可以選擇別的程序。
說
到這里我們就要介紹一些概念了。首先,update-alternatives 在一般情況下是由 postinst 和 prerm
這樣的安裝腳本自動(dòng)調(diào)用的,所以一個(gè) alternative 的狀態(tài)有兩種:自動(dòng)和手動(dòng)。每個(gè) alternative
的初始狀態(tài)都是自動(dòng)。如果系統(tǒng)發(fā)現(xiàn)管理員手動(dòng)修改了一個(gè)
alternative,它的狀態(tài)就從自動(dòng)變成了手動(dòng),這樣安裝腳本就不會(huì)更新它了。如果你希望將一個(gè) alternative 變回自動(dòng),只要執(zhí)行
update-alternatives --auto editor
就可以了。你注意到了嗎?我們說到了“名字”。該怎樣寫名字呢?這就是我們要介紹的第二個(gè)概念:
general name -- 這是指一系列功能相似的程序的“公用”名字(包括絕對(duì)路徑),比如 /usr/bin/editor。
link -- 這是指一個(gè) alternative 在 /etc/alternative 中的名字,比如 editor。
alternative -- 顧名思義,這是指一個(gè)可選的程序所在的路徑(包括絕對(duì)路徑),比如 /usr/bin/vim。
--
auto,--display 和 --config 跟的都是
link。我們要說的第三個(gè)概念是優(yōu)先級(jí)。這個(gè)比較簡(jiǎn)單,當(dāng)然優(yōu)先級(jí)越高的程序越好啦(在大多數(shù)情況下,我不想爭(zhēng)論)最后一個(gè)概念是主和從的
alternative。想想看,你將 /usr/bin/editor 鏈接到了 vim,可是當(dāng)你執(zhí)行 man editor 時(shí)看到的卻是
emacs 的 manpage,你會(huì)做何感想呢?這就引出了主和從 alternative 的概念了:當(dāng)更新主的 alternative
時(shí),從的 alternative 也會(huì)被更新。
說完這四個(gè)重要的概念后,我們介紹另外兩個(gè)選項(xiàng)。至于其他的。。。。我相信你會(huì)去看手冊(cè)頁(yè)的,對(duì)嗎?
第一個(gè)是 --install。它的格式是:
update-alternatives --install gen link alt pri [--slave sgen slink salt] ...
gen,
link,alt,pri 分別是我們上面說過的。如果需要從的 alternative,你可以用 --slave
加在后面。如果你在向一個(gè)已經(jīng)存在的 alternative 組中添加新的 alternatives,該命令會(huì)把這些 alternatives
加入到這個(gè)已經(jīng)存在的 alternative 組的
列表中,并用新的可選命令作為新的命令;否則,將會(huì)建立一個(gè)新的自動(dòng)的 alternative 組。
嗚呼!我加入了一個(gè)錯(cuò)誤的 alternative。我不想要這個(gè) alternative 了。在這種情況 下,可以執(zhí)行下面的命令:
update-alternatives --remove name path
name
是一個(gè)在 /etc/alternatives 中的名字,也就是上面的 link,而 path
是希望刪除的可選程序名的絕對(duì)路徑名(放心,這樣只是從列表中刪除了這個(gè)程序,并不會(huì)真的從硬盤上刪除程序的可執(zhí)行文件)。如果從一個(gè)
alternative 組中刪除了一個(gè)正在被鏈接的程序并且這個(gè)組仍然沒有變成空的,update-alternatives
會(huì)自動(dòng)用一個(gè)具有其他優(yōu)先級(jí)的可選程序代替原來的程序。如果這個(gè)組變成空的了,那么連這個(gè) alternative
組都會(huì)被移除。如果刪除的程序沒有被鏈接,則只有有關(guān)這個(gè)程序的信息會(huì)被移除。
說個(gè)例子吧。我下載了 Eclipse,并且安裝了
gcj 和 gij。可是我發(fā)現(xiàn) GNU 的 java 工具還不足以運(yùn)行 Eclipse。我只好到 Sun 公司的網(wǎng)頁(yè)上下載了它的 java
工具 jdk。因?yàn)槭亲约喊惭b的,我將它們安裝在 /usr/local 上,以便將來重新安裝 Linux
系統(tǒng)時(shí)這些程序仍然可以使用。于是我要做的就是用這個(gè) jdk 中的 java 和 javac 來代替系統(tǒng)原來的。執(zhí)行
natsu:/home/herbert# update-alternatives --display java
java - status is auto.
link currently points to /usr/local/j2sdk1.4.2_06/bin/java
/usr/bin/gij-wrapper-3.3 - priority 33
slave java.1.gz: /usr/share/man/man1/gij-wrapper-3.3.1.gz
/usr/local/j2sdk1.4.2_06/bin/java - priority 100
slave java.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/java.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/java.
natsu:/home/herbert# update-alternatives --display javac
javac - status is auto.
link currently points to /usr/local/j2sdk1.4.2_06/bin/javac
/usr/bin/gcj-wrapper-3.3 - priority 33
slave javah: /usr/bin/gcjh-wrapper-3.3
slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.3.1.gz
slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.3.1.gz
/usr/bin/gcj-wrapper-3.4 - priority 33
slave javah: /usr/bin/gcjh-wrapper-3.4
slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.4.1.gz
slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.4.1.gz
/usr/local/j2sdk1.4.2_06/bin/javac - priority 100
slave javah: /usr/local/j2sdk1.4.2_06/bin/javah
slave javac.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javac.1
slave javah.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javah.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/javac.
natsu:/home/herbert#
(你看到的是我更新以后的)就可以得到關(guān)于要更新哪些 alternatives 的信息。我是這么更新的:
update-alternatives
--install /usr/bin/javac javac /usr/local/j2sdk1.4.2_06/bin/javac 100
--slave /usr/bin/javah javah /usr/local/j2sdk1.4.2_06/bin/javah --slave
/usr/share/man/man1/javac.1.gz javac.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/javac.1 --slave
/usr/share/man/man1/javah.1.gz javah.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/javah.1
update-alternatives
--install /usr/bin/java java /usr/local/j2sdk1.4.2_06/bin/java 100
--slave /usr/share/man/man1/java.1.gz java.1.gz
/usr/local/j2sdk1.4.2_06/man/man1/java.1
1, insert Ubuntu 7.10 CD
a, format disc(primary 10G ext3; extend 59G ext3; swap 1G)
b, install(timezone shanghai; en_US; "prepare disc space" manual, or the system will partition autoly)
c, auto restart, go on install system(remenber cut off the net line except the netwidth is large, or it will cost long time to download from far away)
2, config
a, sources list
sudo vim /etc/apt/sources.list
# add "deb http://debian.exoweb.net/debian.cn99.com/debian etch main" into it
sudo apt-get update
sudo apt-get upgrade
b, vedio card driver
在ubuntu7.10下裝nvidia 7 series顯卡并配置雙屏顯示:
一,顯卡驅(qū)動(dòng) + 雙顯示器
(修改X配置命令:sudo dpkg-reconfigure xserver-xorg)
1,到nvidia網(wǎng)站下載7系列顯卡的最新驅(qū)動(dòng)
2,ensure that the linux-restricted-modules or linux-restricted-modules-common packages have been uninstalled. Alternatively, you can edit the /etc/default/linux-restricted-modules or /etc/default/linux-restricted-modules-common configuration file and disable the NVIDIA linux-restricted kernel modules (nvidia, nvidia_legacy) via:
DISABLED_MODULES="nv nvidia_new"
3,
sudo apt-get remove --purge nvidia-glx nvidia-glx-new
sudo rm /etc/init.d/nvidia-glx /etc/init.d/nvidia-kernel /lib/linux-restricted-modules/.nvidia_new_installed
4,然后ctrl+alt+1進(jìn)入tty1
sudo /etc/init.d/gdm stop
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
(這時(shí)會(huì)出現(xiàn)錯(cuò)誤提示,說少了“libc header file...libc development package”)
sudo apt-get install sudo apt-get install build-essential xorg-dev pkg-config linux-headers-$(uname -r), libc6-dev
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
sudo /etc/init.d/gdm start
用application -> system tools里的nvidia工具去配置雙顯示器
c, multi-language
System -> Administration -> Language support: install English and Chinese
check "input method"
d, Wen Quan Yi font
browse http://wenq.org/, and download 文泉驛點(diǎn)陣宋體 and 文泉驛矢量正黑, then install them
System -> Preference -> Appearance -> Fonts 前四項(xiàng)選擇:點(diǎn)陣宋體(WenQuanYi Bitmap Song), 第五項(xiàng)不改(Monospace)
sudo fc-cache -f -v (刷新字體緩存,每次修改字體都要這樣,不然Xorg會(huì)很慢)
e, stardict
sudo apt-get install stardict
(http://stardict.sourceforge.net/Dictionaries_zh_CN.php 下載朗道英漢,漢英字典)
tar -xjvf *** --directory /usr/share/stardict/dic/
f, pidgin internet messager
sudo apt-get install gaim-guifications
config: Tools -> Plugins -> (check) Guifications; then, config it to uncheck on "Chat message"
3, install and config Software
sudo apt-get install postgresql-8.1 python2.4 ipython vim-gnome sun-java5-jdk eclipse subversion build-essential ssh build-essential meld kompare
a, postgresql
sudo su - postgres (for user postgres has Null password, so you can't just "su - postgres", or you can sudo "sudo passwd postgres" to set password for postgres, then "su - postgres")
createuser (enter username and password.)
config postgresql as below:
In /etc/postgresql/8.1/main/postgresql.conf, Change listen_addresses to '*' and change datestyle to 'ISO,European' and uncomment them.
In /etc/postgresql/8.1/main/pg_hba.conf, 最后加入一行“host all justin 127.0.0.1/16 trust”
b, eclipse
sudo eclipse, exit, eclipse
c, ssh
When other mathines want to ssh or scp your mathine which is new OS, it should "rm ~/.ssh/known_hosts" to reload the new Cert.
d, kompare
add a file svndiff in src with context
"""
if [ $1 ] ; then
svn up -r $1
svn st -q
svn log -r $1
PRE=`expr $1 - 1`
svn diff --diff-cmd=diff -x "-U 10000" -r$PRE:$1 > /tmp/$1.diff
cat /tmp/$1.diff | kompare -
else
svn up
svn st
svn diff --diff-cmd=diff -x "-U 10000" | kompare -
fi
"""
then, in src, ./svndiff 9827 will show diff about r9827
e, firefox add-ons
firebug, flashblock
3, chroot
a,
sudo apt-get install debootstrap
sudo debootstrap --arch i386 etch /home/etch http://debian.exoweb.net/debian.cn99.com/debian/
(if in 64 bit system, use --arch amd64)
sudo chroot /home/etch
#in etch as root
apt-get install locales
dpkg-reconfigure locales #(choose en_us UTF8 as before)
apt-get install vim vim-gnome xbase-clients less sudo postgresql-client subversion
echo "etch" > /etc/debian-chroot
visudo (add user justin to sudo)
adduser justin (刪除的命令是userdel justin)
在ubuntu的/usr/bin/etch加入:
sudo cp /etc/passwd /home/etch/etc/
sudo cp /etc/shadow /home/etch/etc/
sudo cp /etc/group /home/etch/etc/
sudo cp /etc/sudoers /home/etch/etc/
sudo cp /etc/resolv.conf /home/etch/etc/
sudo cp /etc/hosts /home/etch/etc/
在/etc/fstab加入:
/home /home/etch/home none bind 0 0
/tmp /home/etch/tmp none bind 0 0
/dev /home/etch/dev none bind 0 0
/proc /home/etch/proc none bind 0 0
sudo chroot /home/etch/ su - justin
現(xiàn)在就可一享受chroot的雙系統(tǒng)了
b, run X in etch 3 steps
b1, (etch)mkdir /tmp/.X11-unix
(ubuntu)sudo echo "/tmp/.X11-unix/x0 /home/justin/etch/tmp/.X11-unix/x0 none bind 0 0" >> /etc/fstab
# another way is write it in to /etc/fstab, or sudo mount --bind /tmp/*** /home/justin/etch/tmp/***
b2, (etch)vim ~/.bashrc # add "export DISPLAY=:0.0"
b3, (ubuntu) cp ~/.Xauthority ~/etch/home/justin/ (其實(shí)這步不需要,因?yàn)樯厦嬉呀?jīng)把/home mount到了/home/etch/home了)
c, install java
#download jdk-1_5_0_14-linux-i586.bin to /opt/, and into etch/opt/
sudo chmod +x jdk-1_5_0_14-linux-i586.bin
sudo ./jdk-1_5_0_14-linux-i586.bin
vim ~/.bashrc
#add below in the end of .bashrc
#export JAVA_HOME=/opt/jdk1.5.0_14
#export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
#export PATH=$JAVA_HOME/bin:$PATH
java -version
#java version "1.5.0_14"
#Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03)
#Java HotSpot(TM) Client VM (build 1.5.0_14-b03, mixed mode, sharing)
配置默認(rèn)Java使用哪個(gè) sudo update-alternatives --config java
一,兩個(gè)數(shù)的最大公約數(shù):
1、歐幾里德算法
歐幾里德算法又稱輾轉(zhuǎn)相除法,用于計(jì)算兩個(gè)整數(shù)a,b的最大公約數(shù)。其計(jì)算原理依賴于下面的定理:
定理:gcd(a,b) = gcd(b,a mod b)
證明:a可以表示成a = kb + r,則r = a mod b
假設(shè)d是a,b的一個(gè)公約數(shù),則有
d|a, d|b,而r = a - kb,因此d|r
因此d是(b,a mod b)的公約數(shù)
假設(shè)d 是(b,a mod b)的公約數(shù),則
d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公約數(shù)
因此(a,b)和(b,a mod b)的公約數(shù)是一樣的,其最大公約數(shù)也必然相等,得證
歐幾里德算法就是根據(jù)這個(gè)原理來做的,其算法用C++語(yǔ)言描述為:
void swap(int & a, int & b){
int c = a;
a = b;
b = c;
}
int gcd(int a,int b){
if(0 == a ){
return b;
}
if( 0 == b){
return a;
}
if(a > b){
swap(a,b);
}
int c;
for(c = a % b ; c > 0 ; c = a % b){
a = b;
b = c;
}
return b;
}
2、Stein算法
歐幾里德算法是計(jì)算兩個(gè)數(shù)最大公約數(shù)的傳統(tǒng)算法,它無論從理論還是從效率上都是很好的。但是有一個(gè)致命的缺陷,這個(gè)缺陷只有在大素?cái)?shù)時(shí)才會(huì)顯現(xiàn)出來。
考慮現(xiàn)在的硬件平臺(tái),一般整數(shù)最多也就是64位,對(duì)于這樣的整數(shù),計(jì)算兩個(gè)數(shù)之間的模是很簡(jiǎn)單的。對(duì)于字長(zhǎng)為32位的平臺(tái),計(jì)算兩個(gè)不超過32位的整數(shù)的
模,只需要一個(gè)指令周期,而計(jì)算64位以下的整數(shù)模,也不過幾個(gè)周期而已。但是對(duì)于更大的素?cái)?shù),這樣的計(jì)算過程就不得不由用戶來設(shè)計(jì),為了計(jì)算兩個(gè)超過
64位的整數(shù)的模,用戶也許不得不采用類似于多位數(shù)除法手算過程中的試商法,這個(gè)過程不但復(fù)雜,而且消耗了很多CPU時(shí)間。對(duì)于現(xiàn)代密碼算法,要求計(jì)算
128位以上的素?cái)?shù)的情況比比皆是,設(shè)計(jì)這樣的程序迫切希望能夠拋棄除法和取模。
Stein算法由J. Stein 1961年提出,這個(gè)方法也是計(jì)算兩個(gè)數(shù)的最大公約數(shù)。和歐幾里德算法 算法不同的是,Stein算法只有整數(shù)的移位和加減法,這對(duì)于程序設(shè)計(jì)者是一個(gè)福音。
為了說明Stein算法的正確性,首先必須注意到以下結(jié)論:
gcd(a,a) = a,也就是一個(gè)數(shù)和它自身的公約數(shù)是其自身
gcd(ka,kb) = k gcd(a,b),也就是最大公約數(shù)運(yùn)算和倍乘運(yùn)算可以交換,特殊的,當(dāng)k=2時(shí),說明兩個(gè)偶數(shù)的最大公約數(shù)必然能被2整除
C++/java 實(shí)現(xiàn)
// c++/java stein 算法
int gcd(int a,int b){
if(a<b){//arrange so that a>b
int temp = a;
a = b;
b=temp;
}
if(0==b)//the base case
return a;
if(a%2==0 && b%2 ==0)//a and b are even
return 2*gcd(a/2,b/2);
if ( a%2 == 0)// only a is even
return gcd(a/2,b);
if ( b%2==0 )// only b is even
return gcd(a,b/2);
return gcd((a+b)/2,(a-b)/2);// a and b are odd
}
二,多個(gè)數(shù)的最大公約數(shù):(python實(shí)現(xiàn):取出數(shù)組a中最小的,從2到最小的循環(huán),找出其中最大的能被數(shù)組中所有數(shù)整除的那個(gè)數(shù),就是最大公約數(shù))
def gcd(a):
a.sort()
min = a[0]
result = 1
for i in range(2, min+1):
flag = True
for j in a:
if j % i != 0:
flag = False
if flag == True:
result = i
return result