
2007年12月18日
方法調用的綁定:
將方法的調用連到方法本身被稱為“綁定”。當綁定發(fā)生在程序運行之前時,被稱作“前綁定”。
后綁定也稱為“動態(tài)綁定”或“運行時綁定”,指程序運行的時候,根據對象的類型來決定該綁定哪個方法。如果語言實現了后綁定,那它就必須要有能在運行時判斷對象類型,并且調用其合適方法的機制。也就是說編譯器還是不知道對象的類型,但是方法的調用機制會找出,并且調用正確的方法。
除了static和final方法(private方法隱含有final的意思),java的所有的方法都采用后綁定。也就是說,通常情況下你不必考慮是不是應該采用后綁定--它是自動的。 為什么要聲明final方法,上一章指出,這樣可以禁止別人覆寫這個方法,不過更重要的可能還是要“關閉”它的動態(tài)綁定,或者理確切的說,告訴編譯器這里不需要使用后綁定。
shape為circle的基類,下面這句就是在“上傳”:
Shape s = new Circle();
這里先創(chuàng)建了一個Circle對象,接著馬上把它的引用賦給了Shape,看上去這像是一個錯誤(一種類型怎么能賦給另一種);但是由于Circle是由Shape派生出來的,Circle就是一種Shape,因此這種做法是非常正確的。假設你調用了一個基類的方法:s.draw();這里派生類里已經覆寫了此方法,那么可能你會認為,這次應該總調用Shape的draw()了吧,因為畢竟這是Shape的引用,但是由于實現了后綁定(多態(tài)性),實際上它會調用Circle.draw().
posted @
2008-01-05 19:18 仰望者 閱讀(220) |
評論 (0) |
編輯 收藏
1、 float f=1.3;
是不對的,編譯時會出錯,java認為1.3是double型的,所以定義時應寫成:float f=1.3f,或float f= (float)1.3;
2、 byte b = 3; b=b*3;
是不對的,原因是在*運算過程中,java會做類型的提升,將b提升為int型,所以應改為:b=(byte)(b*3);
3、 while(1),if(1)
是不對的,原因是java中布爾型只有true 和false兩個值,這里與C語言不同,只能用while(true)..
4、 數組聲明:int num[3];
這是不對的,java中聲明數組時不應對空間限定,正確的語法應是:
int[] num = new int[3];
或
int[] num;
num = new int[3];
5、數組初始化:int[] num;
num {1,3,4,4};
是不對的,應在定義的時候初始化。如:int[] num={1,3,4,4};
6、int[] num3 =new int[]{1,2,3};
int[] num5 =new int[3]{1,2,3};
int[] num3 =new int[]{1,2,3};是對的。
int[] num5 =new int[3]{1,2,3};是錯的。已經初始化的數組,不應再列明:[3]
posted @
2007-12-23 23:01 仰望者 閱讀(163) |
評論 (0) |
編輯 收藏
合成與繼承
繼承:
super關鍵字的使用:super使用在派生類中,如果派生類中重寫了基類的方法,但在這個被重寫的方法中仍然要調用基類的同名的方法,這就要用到super關鍵字,特別是在創(chuàng)建對象時,在帶參數構造函數中調用基類構造函數的情況。
如:
class Cleanser {
private String s = new String("Cleanser");
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public void print() { System.out.println(s); }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
x.print();
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
// Add methods to the interface:
public void foam() { append(" foam()"); }
// Test the new class:
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
x.print();
System.out.println("Testing base class:");
Cleanser.main(args);
}
} ///:~
可以看到基類Cleanser 中定義了scrub方法,但派生類Detergent 中對scrub方法進行了修改,并用在派生類Detergent 的scrub方法中,要調用基本的scrub方法,那么用super.scrub();
基類的初始化:
當你創(chuàng)建一個派生類的對象的時候,這個對象里面還有一個基類的子對象,這個子對象同基類自己創(chuàng)建的對象沒什么兩樣,只是從外面看來,這個子對象被包裹在派生類的對象里面。
基類子對象的正確初始化是非常重要的,而且只有一個辦法能保證這一點:調用基類的構造函數來進行初始化,因為只有它才能掌握怎么樣才能正確地進行初始化的信息和權限。java會讓派生類的構造函數自動地調用基類的構造函數。
示例:
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} ///:~
輸出結果為:
Art constructor
Drawing constructor
Cartoon constructor
一看結果便一目了然了。
上面的示例是不帶任何參數的情況,
如果構造函數中帶有參數的話,那這里又要用到super的特性了。與上面super的使用涵意一樣,super在這里用作:派生的帶參數構造函數中調用基類的帶參構造函數,只是這里不象上面那樣super.scrub();這里只使用super(i);即可。
class Game {
Game(int i) {
System.out.println("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
System.out.println("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
System.out.println("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} ///:~
輸出結果是:
Game constructor
BoardGame constructor
Chess constructor
合成和繼承一起使用,實現類的復用:
class Plate {
Plate(int i) {
System.out.println("Plate constructor");
}
}
class DinnerPlate extends Plate {
DinnerPlate(int i) {
super(i);
System.out.println(
"DinnerPlate constructor");
}
}
class Utensil {
Utensil(int i) {
System.out.println("Utensil constructor");
}
}
class Spoon extends Utensil {
Spoon(int i) {
super(i);
System.out.println("Spoon constructor");
}
}
class Fork extends Utensil {
Fork(int i) {
super(i);
System.out.println("Fork constructor");
}
}
class Knife extends Utensil {
Knife(int i) {
super(i);
System.out.println("Knife constructor");
}
}
// A cultural way of doing something:
class Custom {
Custom(int i) {
System.out.println("Custom constructor");
}
}
public class PlaceSetting extends Custom {
Spoon sp;
Fork frk;
Knife kn;
DinnerPlate pl;
PlaceSetting(int i) {//把初始化工作都放在構造函數中
super(i + 1);
sp = new Spoon(i + 2);
frk = new Fork(i + 3);
kn = new Knife(i + 4);
pl = new DinnerPlate(i + 5);
System.out.println(
"PlaceSetting constructor");
}
public static void main(String[] args) {
PlaceSetting x = new PlaceSetting(9);
}
} ///:~
盡管編譯器會強迫我們對基礎類進行初始化,并要求我們在構建器最開頭做這一工作,但它并不會監(jiān)視我們是否正確初始化了成員對象。所以對此必須特別加以留意。
FINAL關鍵字:
FINAL關鍵字指“那樣東西是不允許改動”,你可能會出于兩點考慮不想讓別人作改動:設計和效率。由于這兩個原因差別很大,所以很可能會誤用final關鍵字。
final的三種用途:數據(Data)、方法(method)、類(class)。
很多語言通知編譯器:“這段常量(constant)數據”的手段。常量能用下列兩種情況出現:
1、可以是“編譯時的常量”,這樣以后就不能改了;
2、也可以是運行時初始化的值,這個值以后就不想再改了。
如果是編譯時的常量,編譯器會把常量放到算式里面;這樣編譯的時候就能進行計算,因此也就降低了運行時的開銷。在Java 中這種常量必須是primitive 型的,而且要用final 關鍵詞表示。這種常量的賦值必須在定義的時候進行。
一個既是static 又是final 的數據成員會只占據一段內存,并且不可修改。
當final 不是指primitive,而是用于對象的reference 的時候,意思就有點不一樣了。對primitive 來說,final 會將這個值定義成常量,但是對于對象的reference 而言,final 的意思則是這個reference 是常量。初始化的時候,一旦將reference 連到了某個對象,那么它就再也不能指別的對象了。但是這個對象本身是可以修改的;Java 沒有提供將某個對象作成常量的方法。
(但是你可以自己寫一個類,這樣就能把類當做常量了)
這種局限性也體現在數組上,因為它也是一個對象。
注意,通常約定,被初始化為常量值的final static 的primitive 的名字全都用大寫,詞與詞之間用下
劃線分開,如VAL_ONE
Final 方法
使用final 方法的目的有二:
第一,為方法上“鎖”,禁止派生類進行修改。這是出于設計考慮。當你希望某個方法的功能,能在繼承過程中被保留下來,并且不被覆寫,就可以使用這個方法。
第二個原因就是效率。如果方法是final 的,那么編譯器就會把調用轉換成“內聯的(inline)”。它會用方法本身的拷貝來代替方法的調用
final 和private
private 方法都隱含有final 的意思。由于你不能訪問private 的方法,因此你也不能覆寫它。你可以給private 方法加一個final 修飾符,但是這樣做什么意義也沒有。
這個問題有可能會造成混亂,因為即使你覆寫了一個private 方法(它隱含有final 的意思),看上去它還是可以運行的,而且編譯器也不會報錯:
class WithFinals {
// Identical to "private" alone:
private final void f() {
System.out.println("WithFinals.f()");
}
/ / Also automatically "final":
private void g() {
System.out.println("WithFinals.g()");
}
}
class OverridingPrivate extends WithFinals {
private final void f() {
System.out.println("OverridingPrivate.f()");
}
private void g() {
System.out.println("OverridingPrivate.g()");
}
}
只有是基類接口里的東西才能被“覆寫”,如果方法是private 的,那它就不屬于基類的接口。它只能算是被類隱藏起來的,正好有著相同的名字的代碼。如果你在派生類里創(chuàng)建了同名的public 或protected,或package 權限的方法,那么它們同基類中可能同名的方法,沒有任何聯系。你并沒有覆寫那個方法,你只是創(chuàng)建了一個新的方法。由于private 方法是無法訪問的,實際上是看不見的,因此這么作除了會影響類的代碼結構,其它什么意義都沒有。
Final 類
把整個類都定義成final 的(把final 關鍵詞放到類的定義部分的前面)就等于在宣布,你不會去繼承這個類,你也不允許別人去繼承這個類。換言之,出于類的設計考慮,它再也不需要作修改了,或者從安全角度出發(fā),你不希望它再生出子類。
final class Dinosaur{}
注意,final 類的數據可以是final 的,也可以不是final 的,這要由你來決定。無論類是不是final 的,這一條都適用于“將final 用于數據的”場合。但是,由于final 類禁止了繼承,覆寫方法已經不可能了,因
此所有的方法都隱含地變成final 了。你可以為final 類的方法加一個final 修飾符,但是這一樣沒什么意義。
posted @
2007-12-20 17:33 仰望者 閱讀(238) |
評論 (0) |
編輯 收藏
訪問控制符:public 、private、protected、friendly
public包內包外均可訪問。
private只有本類可訪問。
protected針對繼承而使用的:1、包內繼承,因為在包內,聲明為protected不影響它本來的friendly權限。
2、包外繼承,必須聲明為protected。
派生類可以訪問基類的protected成員。
注意不可將類設成private(那樣會使除類之外的其他東西都不能訪問它),也不能設成protected。因此,我們現在對于類的訪問只有兩個選擇:“友好的”或者public。若不愿其他任何人訪問那個類,可將所有構建器設為private,這樣除你之外,沒有可以用類創(chuàng)建的了。而你可以使用static成員創(chuàng)建對象。
package com.access.external;
class Soup{
private Soup(){//構造函數聲明為private,其它類不能用此構造函數創(chuàng)建對象;
System.out.println("sffewe");
}
public static Soup makSoup(){//其它類可通過makSoup來創(chuàng)建對象;
return new Soup();
}
private static Soup ps1 = new Soup();//自己創(chuàng)建對象;
public static Soup access(){//返回對象的引用。
return ps1;
}
public void f(){}
}
class Sandwich{
void f(){
new Lunch();
}
}
public class Lunch {
void test(){
//Soup priv1 = new Soup();
Soup priv2 = Soup.makSoup();
Sandwich f1 = new Sandwich();
Soup.access().f();//不創(chuàng)建對象,但通過Soup中返回的對象引用調用其方法。
}
}
該方法返回一個句柄,它指向類Soup的一個對象。
Soup類向我們展示出如何通過將所有構建器都設為private,從而防止直接創(chuàng)建一個類。請記住,假若不明確地至少創(chuàng)建一個構建器,就會自動創(chuàng)建默認構建器(沒有自變量)。若自己編寫默認構建器,它就不會自動創(chuàng)建。把它變成private后,就沒人能為那個類創(chuàng)建一個對象。但別人怎樣使用這個類呢?上面的例子為我們揭示出了兩個選擇。第一個選擇,我們可創(chuàng)建一個static方法,再通過它創(chuàng)建一個新的Soup,然后返回指向它的一個句柄。如果想在返回之前對Soup進行一些額外的操作,或者想了解準備創(chuàng)建多少個Soup對象(可能是為了限制它們的個數),這種方案無疑是特別有用的。
第二個選擇是采用“設計方案”(Design Pattern)技術,本書后面會對此進行詳細介紹。通常方案叫作“獨子”,因為它僅允許創(chuàng)建一個對象。類Soup的對象被創(chuàng)建成Soup的一個static private成員,所以有一個而且只能有一個。除非通過public方法access(),否則根本無法訪問它。
posted @
2007-12-20 11:09 仰望者 閱讀(248) |
評論 (0) |
編輯 收藏
Eclipse提供了很好的工具:
1、實時運算薄頁面(java scrapbook page)具體的說就是一小段代碼,比如一個for循環(huán),就可以在里面執(zhí)行,無須寫出main函數等。
操作如下:new->other->java->java run/debug->scrapbook page
創(chuàng)建頁面后,輸入代碼:
for (int i = 0; i < 10; i++) {
System.out.println(Integer.toString(i));
}
選擇代碼,右鍵excute即可看到結果。。很方便。。。。。
2、程序代碼產生模板
window->prefrences->java->editor->Templates
添加:name:Sys
context:java
Description:shortcut for System.out.println
pattern:System.out.println(${cursor});
確定后,在程序中輸入s或Sys時再按alt+/會提示語句。。。接著按enter鍵吧。。。
3、產生 getter 與 setter Java 編輯器可以為編譯單元內的類型字段,產生存取元(accessors,也就是getter和setter的method)。 I. 「Source」→「Generate Getter and Setter...」 (或是在Java編輯器按右鍵,「Source」→「Generate Getter and Setter...」)
挑選哪些需要建立getter和setter的method ;
選擇method要建立的地方 ;
排序的方式;
選擇Access modifier ;
選擇是否需要建立批注;
按OK;
4、建立新的 JAR 檔案 如果要在工作臺中建立新 JAR 檔,請執(zhí)行下列動作: I. 在「Package Explorer」中,可以選擇性地預選一或多個要匯出的 Java 元素。(在步驟IV中,這些會在JAR Package Specification精靈頁面中自動選出。) II. 從快速菜單或從菜單列的File菜單,選取Export。 III. 選取JAR file,然后按一下Next。
IV. 在JAR Package Specification頁面的Select the resources to export字段中,選取要匯出的資源。 V. 選取適當的勾選框,以指出想Export generated class files and resourcess或Export java source files and resources。附注:這兩種情況皆會匯出所選的資源。 VI. 在Select the export destination字段中,輸入或按一下Browse以選取 JAR 文件的位置。 VII. 選取或清除Compress the contents of the JAR file勾選框。 VIII. 選取或清除Overwrite existing files without warning勾選框。如果清除這個勾選框,則會提示確認是否要更換每一個將被改寫的檔案。 IX. 附注:在撰寫 JAR 檔、JAR 說明與 Manifest 檔時,會套用改寫選項。 X. 有兩項選擇: ?? 按一下Finish來立即建立 JAR 檔。 ?? 按一下Next,使用「JAR 套裝選項」頁面,以設定進階選項,建立 JAR 說明,或變更預設 manifest。
posted @
2007-12-18 15:53 仰望者 閱讀(179) |
評論 (0) |
編輯 收藏
摘要: 菜單
功能
熱鍵
說明
Edit
Add Block Comment
Ctrl+Shift+/
Editing in Structured Text Editors
...
閱讀全文
posted @
2007-12-18 15:36 仰望者 閱讀(317) |
評論 (0) |
編輯 收藏