$emit传递多个参数_10年架构师深解java核心技术:方法参数+对象构造,确定不学?...
方法參數(shù)
首先回顧一下在程序設(shè)計(jì)語(yǔ)言中有關(guān)參數(shù)傳遞給方法(或函數(shù))的一些專(zhuān)業(yè)術(shù)語(yǔ)。值調(diào)用(call by value)表示方法接收的是調(diào)用者提供的值。而引用調(diào)用(call by reference)表示方法接收的是調(diào)用者提供的變量位置。可以想到,一個(gè)方法可以修改傳遞引用所對(duì)應(yīng)的變量值,而不能修改傳遞值調(diào)用所對(duì)應(yīng)的變量值。這一點(diǎn)不僅僅是Java 語(yǔ)言,其他程序設(shè)計(jì)語(yǔ)言也是如此。“……調(diào)用”(call by)是一個(gè)標(biāo)準(zhǔn)的計(jì)算機(jī)科學(xué)術(shù)語(yǔ),它用來(lái)描述各個(gè)程序設(shè)計(jì)語(yǔ)言中方法參數(shù)的傳遞方式。(事實(shí)上,以前還有名稱(chēng)調(diào)用(call by name),Algol程序設(shè)計(jì)語(yǔ)言—最古老的高級(jí)程序設(shè)計(jì)語(yǔ)言之一—使用的就是這種參數(shù)傳遞方式。不過(guò),對(duì)于今天來(lái)說(shuō),這種傳遞方式已經(jīng)成為歷史。)Java程序設(shè)計(jì)語(yǔ)言使用值調(diào)用。也就是說(shuō),方法得到的是所有參數(shù)值的一個(gè)拷貝,特別是,方法不能修改傳遞給它的任何參數(shù)變量的內(nèi)容。
例如,考慮下面的調(diào)用:
double percent = 10;
harry.raiseSalary(percent);
不必理睬這個(gè)方法的具體實(shí)現(xiàn),在方法調(diào)用之后,percent的值還是10。
下面再仔細(xì)地研究一下這種情況。假定一個(gè)方法試圖將一個(gè)參數(shù)值增加至3倍:
public static void tripleValue(double x) // doesn’t work{x = 3 * x;}然后調(diào)用這個(gè)方法:double percent = 10;tripleValue(percent);可以看到,無(wú)論怎樣調(diào)用這個(gè)方法,執(zhí)行之后,percent的值還是10。下面看一下具體的執(zhí)行
過(guò)程:
1)x被初始化為percent值的一個(gè)拷貝(也就是10)。
2)x被乘以3后等于30。但是percent仍然是10(如圖4-6所示)。
3)這個(gè)方法結(jié)束之后,參數(shù)變量x不再使用。
然而,方法參數(shù)共有兩種類(lèi)型:
? 基本數(shù)據(jù)類(lèi)型(數(shù)字、布爾值)。
? 對(duì)象引用。
已經(jīng)看到,一個(gè)方法不可能修改一個(gè)基本數(shù)據(jù)類(lèi)型的參數(shù)。而對(duì)象引用作為參數(shù)就不同了,
我們可以很容易地利用下面這個(gè)方法實(shí)現(xiàn)將一個(gè)雇員的薪金提高兩倍的操作:
public static void tripleSalary(Employee x) // works{x.raiseSalary(200);}當(dāng)調(diào)用harry = new Employee(. . .);tripleSalary(harry);具體的執(zhí)行過(guò)程為:
1)x被初始化為harry值的拷貝,這里是一個(gè)對(duì)象的引用。
2)raiseSalary方法應(yīng)用于這個(gè)對(duì)象引用。x和harry同時(shí)引用的那個(gè)Employee對(duì)象的薪金提高了200%。
3)方法結(jié)束后,參數(shù)變量x不再使用。當(dāng)然,對(duì)象變量harry繼續(xù)引用那個(gè)薪金增至3倍的雇員對(duì)象(如圖4-7所示)。
已經(jīng)看到,實(shí)現(xiàn)一個(gè)改變對(duì)象參數(shù)狀態(tài)的方法并不是一件難事。理由很簡(jiǎn)單,方法得到的是對(duì)象引用的拷貝,對(duì)象引用及其他的拷貝同時(shí)引用同一個(gè)對(duì)象。
很多程序設(shè)計(jì)語(yǔ)言(特別是,C++和Pascal)提供了兩種參數(shù)傳遞的方式:值調(diào)用和引用調(diào)用。有些程序員認(rèn)為Java 程序設(shè)計(jì)語(yǔ)言對(duì)對(duì)象采用的是引用調(diào)用,實(shí)際上,這種理解是不對(duì)的。
由于這種誤解具有一定的普遍性,所以下面給出一個(gè)反例,來(lái)詳細(xì)地闡述一下這個(gè)問(wèn)題。
首先,編寫(xiě)一個(gè)交換兩個(gè)雇員對(duì)象的方法:
public static void swap(Employee x, Employee y) // doesn't work{Employee temp = x;x = y;y = temp;}如果Java程序設(shè)計(jì)語(yǔ)言對(duì)對(duì)象采用的是引用調(diào)用的話,這個(gè)方法應(yīng)該能夠?qū)崿F(xiàn)交換數(shù)據(jù)的效果:
Employee a = new Employee("Alice", . . .);
Employee b = new Employee("Bob", . . .);
swap(a, b);
// does a now refer to Bob, b to Alice?
但是,方法并沒(méi)有改變存儲(chǔ)在變量a和b中的對(duì)象引用。swap方法的參數(shù)x和y被初始化為兩個(gè)對(duì)象引用的拷貝,這個(gè)方法交換的是這兩個(gè)拷貝。
// x refers to Alice, y to Bob
Employee temp = x;
x = y;
y = temp;
// now x refers to Bob, y to Alice
最終,白費(fèi)力氣。在方法結(jié)束時(shí)參數(shù)變量x和y被丟棄了。原來(lái)的變量a和b仍然引用這個(gè)方法調(diào)用之前所引用的對(duì)象(如圖4-8所示)。
這個(gè)過(guò)程說(shuō)明:Java 程序設(shè)計(jì)語(yǔ)言對(duì)對(duì)象采用的不是引用調(diào)用,實(shí)際上,對(duì)象引用進(jìn)行的是值傳遞。
下面總結(jié)一下在Java程序設(shè)計(jì)語(yǔ)言中,方法參數(shù)的使用情況:
? 一個(gè)方法不能修改一個(gè)基本數(shù)據(jù)類(lèi)型的參數(shù)(即數(shù)值型和布爾型值)。
? 一個(gè)方法可以改變一個(gè)對(duì)象參數(shù)的狀態(tài)。
? 一個(gè)方法不能讓對(duì)象參數(shù)引用一個(gè)新的對(duì)象。
例4-4中的程序給出了相應(yīng)的演示。在這個(gè)程序中,首先試圖將一個(gè)值參數(shù)的值提高兩倍,但沒(méi)有成功:
Testing tripleValue:
Before: percent=10.0
End of method: x=30.0
After: percent=10.0
隨后,成功地將一個(gè)雇員的薪金提高了兩倍:
Testing tripleSalary:
Before: salary=50000.0
End of method: salary=150000.0
After: salary=150000.0
方法結(jié)束之后,harry引用的對(duì)象狀態(tài)發(fā)生了改變。這是因?yàn)檫@個(gè)方法可以通過(guò)對(duì)象引用的拷貝修改所引用的對(duì)象狀態(tài)。
最后,程序演示了swap方法的失敗效果:
Testing swap:
Before: a=Alice
Before: b=Bob
End of method: x=Bob
End of method: y=Alice
After: a=Alice
After: b=Bob
可以看出,參數(shù)變量x和y被交換了,但是變量a和b沒(méi)有受到影響。
例4-4 ParamTest.java
對(duì)象構(gòu)造
前面已經(jīng)學(xué)會(huì)了編寫(xiě)簡(jiǎn)單的構(gòu)造器,以便對(duì)定義的對(duì)象進(jìn)行初始化。但是,由于對(duì)象構(gòu)造非常重要,所以Java提供了多種機(jī)制來(lái)編寫(xiě)構(gòu)造器。下面將介紹這些內(nèi)容。
重載
從前面可以看到,GregorianCalendar類(lèi)有多個(gè)構(gòu)造器。我們可以使用:
GregorianCalendar today = new GregorianCalendar( );
或者
GregorianCalendar deadline = new GregorianCalendar(2099, Calendar.DECEMBER, 31);
這種能力叫做重載(overloading)。如果多個(gè)方法(比如,GregorianCalendar構(gòu)造器方法)有相同的名字、不同的參數(shù),便產(chǎn)生了重載。編譯器必須挑選出具體執(zhí)行哪個(gè)方法,它通過(guò)用各個(gè)方法給出的參數(shù)類(lèi)型與特定方法調(diào)用所使用的值類(lèi)型進(jìn)行匹配來(lái)挑選出正確的方法。如果編譯器找不到匹配的參數(shù),或者找出多個(gè)可能的匹配,就會(huì)產(chǎn)生編譯時(shí)錯(cuò)誤(這個(gè)過(guò)程被稱(chēng)為重載解析(overloading resolution)。)
默認(rèn)域初始化
如果在構(gòu)造器中沒(méi)有顯式地給域賦予初值,它就會(huì)被自動(dòng)地賦為默認(rèn)值:數(shù)值為0、布爾值為flase、對(duì)象引用為null。然而,只有缺少程序設(shè)計(jì)經(jīng)驗(yàn)的人才會(huì)這樣做。確實(shí),如果不明確地對(duì)域進(jìn)行初始化,就會(huì)影響程序代碼的可讀性。
默認(rèn)構(gòu)造器
所謂默認(rèn)構(gòu)造器是指沒(méi)有參數(shù)的構(gòu)造器。例如,Employee類(lèi)的默認(rèn)構(gòu)造器:
public Employee( ){name = "";salary = 0;hireDay = new Date( );}如果在編寫(xiě)一個(gè)類(lèi)時(shí)沒(méi)有編寫(xiě)構(gòu)造器,系統(tǒng)就會(huì)提供一個(gè)默認(rèn)構(gòu)造器。這個(gè)默認(rèn)構(gòu)造器將所有的實(shí)例域設(shè)置為默認(rèn)值。于是,實(shí)例域中的數(shù)值型數(shù)據(jù)設(shè)置為0、布爾型數(shù)據(jù)設(shè)置為false、所有對(duì)象變量將設(shè)置為null。
如果類(lèi)中提供了至少一個(gè)構(gòu)造器,但是沒(méi)有提供默認(rèn)的構(gòu)造器,那么在構(gòu)造對(duì)象時(shí)若不提供構(gòu)造參數(shù)就被視為不合法。例如,在例4-2中的Employee類(lèi)提供了一個(gè)簡(jiǎn)單的構(gòu)造器:
Employee(String name, double salary, int y, int m, int d)
對(duì)于這個(gè)類(lèi),構(gòu)造默認(rèn)的雇員屬于不合法。也就是,調(diào)用
e = new Employee( );
將會(huì)產(chǎn)生錯(cuò)誤。
顯式域初始化
由于類(lèi)的構(gòu)造器方法可以重載,所以可以采用多種形式設(shè)置類(lèi)的實(shí)例域的初始狀態(tài)。確保不管怎樣調(diào)用構(gòu)造器,每個(gè)實(shí)例域都可以被設(shè)置為一個(gè)有意義的初值。這是一種很好的設(shè)想。
可以在類(lèi)的定義中,簡(jiǎn)單地將一個(gè)值賦給任何域。例如:
在執(zhí)行構(gòu)造器之前,先執(zhí)行賦值。當(dāng)一個(gè)類(lèi)的所有構(gòu)造器都希望將相同的值賦予某個(gè)特定的實(shí)例域時(shí),這種方式特別有用。
初始值不一定是常量。在下面的例子中,域可以調(diào)用方法來(lái)進(jìn)行初始化。仔細(xì)看一下Employee類(lèi),其中每個(gè)雇員有一個(gè)id域。可以使用下列方式進(jìn)行初始化:
參數(shù)名
在編寫(xiě)很小的構(gòu)造器時(shí)(這是十分常見(jiàn)的),常常在參數(shù)命名上出現(xiàn)錯(cuò)誤。
通常,參數(shù)用單個(gè)字符命名:
然而,這樣做有一個(gè)缺陷:只有閱讀代碼才能夠了解參數(shù)n和參數(shù)s的含義。
于是,有些程序員在每個(gè)參數(shù)前面加上一個(gè)前綴“a”:
這樣很清晰。每一個(gè)讀者一眼就能夠看懂參數(shù)的含義。
還一種常用的技巧,它基于這樣的事實(shí):參數(shù)變量用同樣的名字將實(shí)例域屏蔽起來(lái)。例如,如果將參數(shù)命名為salary,那么salary將引用這個(gè)參數(shù),而不是實(shí)例域。但是,可以采用this.salary的形式訪問(wèn)實(shí)例域。回想一下,this指示隱式參數(shù),也就是被構(gòu)造的對(duì)象。下面是一個(gè)例子:
調(diào)用另一個(gè)構(gòu)造器
關(guān)鍵字this引用方法的隱式參數(shù)。然而,這個(gè)關(guān)鍵字還有另外一個(gè)含義。
如果構(gòu)造器的第一個(gè)語(yǔ)句形如this(...),那么這個(gè)構(gòu)造器將調(diào)用同一個(gè)類(lèi)的另一個(gè)構(gòu)造器。下面是一個(gè)典型的例子:
當(dāng)調(diào)用new Employee(60000) 時(shí),Employee(double) 構(gòu)造器將調(diào)用Employee(String, double) 構(gòu)造器。
采用這種方式使用this關(guān)鍵字非常有用,這樣對(duì)公共的構(gòu)造器代碼部分只編寫(xiě)一次即可。
初始化塊
前面已經(jīng)講過(guò)兩種初始化數(shù)據(jù)域的方法:
? 在構(gòu)造器中設(shè)置值
? 在聲明中賦值
實(shí)際上,Java還有第三種機(jī)制,稱(chēng)為初始化塊。在一個(gè)類(lèi)的聲明中,可以包含多個(gè)代碼塊。
只要構(gòu)造類(lèi)的對(duì)象,這些塊就會(huì)被執(zhí)行。例如,
在這個(gè)例子中,無(wú)論使用哪個(gè)構(gòu)造器構(gòu)造對(duì)象,id域都在對(duì)象初始化塊中被初始化。首先運(yùn)行初始化塊,然后才運(yùn)行構(gòu)造器的主體部分。
這種機(jī)制不是必須的,也不常見(jiàn)。通常,直接將初始化代碼放置在一個(gè)構(gòu)造器的內(nèi)部。
由于初始化數(shù)據(jù)域有多種途徑,所以列出構(gòu)造過(guò)程的所有路徑可能相當(dāng)混亂。下面是調(diào)用構(gòu)造器的具體處理步驟:
1)所有數(shù)據(jù)域被初始化為默認(rèn)值(0、false或null)。
2)按照在類(lèi)聲明中出現(xiàn)的次序依次執(zhí)行所有域初始化語(yǔ)句和初始化塊。
3)如果構(gòu)造器第一行調(diào)用了第二個(gè)構(gòu)造器,則執(zhí)行第二個(gè)構(gòu)造器主體。
4)執(zhí)行這個(gè)構(gòu)造器的主體。
當(dāng)然,應(yīng)該精心地組織好初始化代碼,這樣有利于其他程序員的理解。例如,如果讓類(lèi)的構(gòu)造器行為依賴于數(shù)據(jù)域聲明的順序,就會(huì)顯得很奇怪并且容易引起錯(cuò)誤。
可以通過(guò)提供一個(gè)初始化值,或者使用一個(gè)靜態(tài)的初始化塊來(lái)對(duì)靜態(tài)域進(jìn)行初始化。前面已經(jīng)介紹過(guò)第一種機(jī)制:
static int nextId = 1;
如果對(duì)類(lèi)的靜態(tài)域進(jìn)行初始化的代碼比較復(fù)雜,就可以使用靜態(tài)的初始化塊。
將代碼放置在一個(gè)塊中,并標(biāo)記關(guān)鍵字static。下面是一個(gè)例子。其功能是將雇員ID的起始值賦予一個(gè)小于10 000的隨機(jī)整數(shù)。
在類(lèi)第一次加載的時(shí)候,將會(huì)進(jìn)行靜態(tài)域的初始化。與實(shí)例域一樣,靜態(tài)域的默認(rèn)初值是 0、false或null,除非將它們顯式設(shè)置成其他值。所有的靜態(tài)初始化語(yǔ)句以及靜態(tài)初始化塊都將按照類(lèi)定義中出現(xiàn)的順序執(zhí)行。
例4-5 ConstructorTest.java
java.util.Random 1.0
? Random( )
構(gòu)造一個(gè)新的隨機(jī)數(shù)生成器。
? int nextInt(int n) 1.2
返回一個(gè)0~n-1之間的隨機(jī)數(shù)。
對(duì)象析構(gòu)與finalize方法
有些面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,特別是C++,有顯式的析構(gòu)器方法,其中放置一些當(dāng)對(duì)象不再使用時(shí)所需要用到的清理代碼。在析構(gòu)器中,最常見(jiàn)的操作是回收分配給對(duì)象的存儲(chǔ)空間。
由于Java有自動(dòng)的垃圾回收器,不需要人工回收內(nèi)存,所以Java不支持析構(gòu)器。
當(dāng)然,某些對(duì)象使用了內(nèi)存之外的其他資源,如文件或使用了系統(tǒng)資源的另一個(gè)對(duì)象的句柄。在這種情況下,當(dāng)資源不再需要的時(shí)候,將其回收和再利用十分重要。
可以為任何一個(gè)類(lèi)添加finalize方法。finalize方法將在垃圾回收器清除對(duì)象之前被調(diào)用。在實(shí)際應(yīng)用中,不要使用finalize方法回收任何短缺的資源,這是因?yàn)楹茈y知道這個(gè)方法什么時(shí)候才能夠被調(diào)用。
覺(jué)得有收貨的話,可以轉(zhuǎn)發(fā)關(guān)注小編,之后持續(xù)更新技術(shù)干貨!!!!!
總結(jié)
以上是生活随笔為你收集整理的$emit传递多个参数_10年架构师深解java核心技术:方法参数+对象构造,确定不学?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java.exe指的是什么意思
- 下一篇: count 和列不能一起查am_AM-R