門戶網(wǎng)站建設(shè)ppt方案seo公司費(fèi)用
理解方法調(diào)用
- 首先
- 什么是隱式參數(shù) --->隱式參數(shù)是調(diào)用該方法的對(duì)象本身。
- 接下來
- 方法的名稱和參數(shù)列表被稱為方法的簽名(signature)。
- 在Java中,方法的簽名由方法的名稱和參數(shù)列表組成,用于唯一標(biāo)識(shí)一個(gè)方法。
- 返回類型不是簽名的一部分。不過在覆蓋一個(gè)方法時(shí),需要保證返回類型的兼容性:--->允許子類將覆蓋方法的返回類型改為原返回類型的子類型。
- 具體的例子:
- 詳細(xì):
- "協(xié)變"(covariant)是一種類型關(guān)系 --->子類型的某些屬性或方法可以比父類型更具體(特化)"
- 協(xié)變(covariance)是指子類型的返回類型可以是父類型的子類型,而逆變(contravariance)是指子類型的返回類型可以是父類型的超類型。
- 注意
- 1.具體說明是怎么實(shí)現(xiàn)_協(xié)變
- 2.具體說明是怎么實(shí)現(xiàn)_逆變
- 靜態(tài)綁定(Static Binding)和動(dòng)態(tài)綁定(Dynamic Binding)
- 1. **靜態(tài)綁定(Static Binding)**:編譯時(shí)期確定的方法或函數(shù)【可共享的】
- 靜態(tài)綁定適用于以下情況:
- 舉例代碼:
- 靜態(tài)常量和靜態(tài)綁定是兩個(gè)不同的概念。
- 在Java中,`static` 和 `final` 都是關(guān)鍵字,用于定義變量的特性。它們有不同的含義和用途。
- 需要注意的是,嘗試修改 `final` 和 `static final` 變量的值會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)樗鼈儽宦暶鳛椴豢尚薷牡某A俊?/li>
- 總結(jié):
- - Math類中的定義:
- 2. **動(dòng)態(tài)綁定(Dynamic Binding)**:
- 動(dòng)態(tài)綁定適用于以下情況:
- 代碼舉例:
- 總結(jié):
假設(shè)要針對(duì)x.f(args),其中 隱式參數(shù) x 是一個(gè)聲明為類 C 對(duì)象的隱式參數(shù)。以下是詳細(xì)描述方法調(diào)用的過程:
首先
編譯器會(huì)檢查對(duì)象在聲明過程中具備的聲明類型以及所調(diào)用的方法名 f。由于可能存在重名的情況,可能存在多個(gè)名為 f 但參數(shù)類型不同的方法。例如,可能同時(shí)存在 f(int) 和 f(String) 這樣的方法。編譯器會(huì)逐一列舉在類 C 中所有名為 f 的方法,還會(huì)查找超類中所有可訪問的名為 f 的方法 [ 注意:超類的私有方法除外 ]。這是編譯器獲得了所有可能被調(diào)用的備用的方法?!驹诖?#xff0c;形成方法表1或候選方法列表,接下來會(huì)進(jìn)一步篩選出最終應(yīng)該調(diào)用的方法。】
什么是隱式參數(shù) —>隱式參數(shù)是調(diào)用該方法的對(duì)象本身。
在Java中,隱式參數(shù)是指在方法調(diào)用過程中自動(dòng)傳遞的參數(shù),而不需要顯式地在方法調(diào)用中提供。隱式參數(shù)通常是通過對(duì)象的方法調(diào)用來使用的。
示例代碼:
public class Example {private int value;public Example(int value) {this.value = value;}public void printValue() {System.out.println(value);}public static void main(String[] args) {Example example = new Example(10);example.printValue();}
}
在上面的代碼中,printValue()
方法是一個(gè)實(shí)例方法,它沒有顯式的參數(shù)。然而,它可以訪問隱式參數(shù) value
,該參數(shù)是通過對(duì)象 example
的方法調(diào)用來傳遞的。在 example.printValue()
這行代碼中,example
就是隱式參數(shù)。
與隱式參數(shù)相對(duì)應(yīng)的是顯示參數(shù)。顯示參數(shù)是在方法調(diào)用時(shí)顯式地提供的參數(shù)。例如,以下是一個(gè)使用顯示參數(shù)的示例:
public class Example {public static void printValue(int value) {System.out.println(value);}public static void main(String[] args) {int value = 10;printValue(value);}
}
在上面的代碼中,printValue()
方法接受一個(gè)顯式參數(shù) value
。在 printValue(value)
這行代碼中,value
就是顯示參數(shù)。
總結(jié)一下,隱式參數(shù)是在方法調(diào)用中自動(dòng)傳遞的參數(shù),而不需要顯式提供,通常是通過對(duì)象的方法調(diào)用來使用的。相比之下,顯示參數(shù)是在方法調(diào)用時(shí)顯式地提供的參數(shù)。
接下來
編譯器會(huì)分析方法調(diào)用中提供的參數(shù)類型。它會(huì)匹配這些參數(shù)類型與所有候選的方法參數(shù)類型,尋找一個(gè)與提供參數(shù)類型完全匹配的方法。這一步驟被稱為重載解析(overloading resolution)。
假設(shè)我們調(diào)用 x.f(“Hello_world_work0806”),編譯器會(huì)選擇參數(shù)類型為 String 的 f 方法,而不是參數(shù)類型為 int 的。
需要注意的是,由于允許進(jìn)行類型轉(zhuǎn)換(例如,int 可以轉(zhuǎn)換成 double,Manager 可以轉(zhuǎn)換成 Employee 等),這個(gè)過程可能會(huì)變得相當(dāng)復(fù)雜。
【如果編譯器無(wú)法找到與提供參數(shù)類型完全匹配的方法,或者在進(jìn)行類型轉(zhuǎn)換后有多個(gè)方法與之匹配,編譯器將會(huì)報(bào)告錯(cuò)誤?!康竭@一步,編譯器已經(jīng)成功 確定了需要調(diào)用的方法名和參數(shù)類型.這意味著在方法調(diào)用的過程中,編譯器通過上述步驟精確地確定了應(yīng)該調(diào)用的方法。這個(gè)過程確保了方法調(diào)用的準(zhǔn)確性和可靠性。
public class Mains {public static void main(String[] args) {printNumber(5); // 輸出 "Printing int: 5"printNumber(3.14); // 輸出 "Printing double: 3.14"}static void printNumber(int num) {System.out.println("Printing int: " + num);}static void printNumber(double num) {System.out.println("Printing double: " + num);}
}
如果存在多個(gè)名為f的方法,它們的參數(shù)類型都可以接受你提供的參數(shù),那么編譯器會(huì)選擇參數(shù)類型與你提供的參數(shù)類型最接近的那個(gè)方法。例如,如果你調(diào)用f(5),并且存在一個(gè)參數(shù)類型為int的f方法和一個(gè)參數(shù)類型為double的f方法,那么編譯器會(huì)選擇參數(shù)類型為int的f方法,因?yàn)閕nt類型比double類型更接近你提供的參數(shù)類型。
這個(gè)過程可能會(huì)變得復(fù)雜,因?yàn)镴ava允許進(jìn)行類型轉(zhuǎn)換。例如,int可以轉(zhuǎn)換成double,Manager可以轉(zhuǎn)換成Employee等。所以,如果你提供的參數(shù)類型和任何一個(gè)候選方法的參數(shù)類型都不完全匹配,編譯器會(huì)嘗試進(jìn)行類型轉(zhuǎn)換,以找到一個(gè)可以接受你提供的參數(shù)的方法。
方法的名稱和參數(shù)列表被稱為方法的簽名(signature)。
方法的參數(shù)列表包括參數(shù)的數(shù)量、類型和順序。方法的簽名不包括方法的返回類型和訪問修飾符。
在Java中,方法的簽名由方法的名稱和參數(shù)列表組成,用于唯一標(biāo)識(shí)一個(gè)方法。
public void calculateSum(int a, int b)
在上面的示例中,方法的名稱是calculateSum,參數(shù)列表是int a和int b,因此方法的簽名是calculateSum(int, int)。
f(int) 和 f(String) 是兩個(gè)有著相同名字但參數(shù)不同的方法,則他們的簽名是不同的。
在子類里,如果你定義了一個(gè)和超類有著相同簽名的方法,那這個(gè)子類方法會(huì)“覆蓋”(override)超類中同簽名的方法。
返回類型不是簽名的一部分。不過在覆蓋一個(gè)方法時(shí),需要保證返回類型的兼容性:—>允許子類將覆蓋方法的返回類型改為原返回類型的子類型。
允許子類在覆蓋(重寫)父類方法時(shí),將方法的返回類型修改為父類方法返回類型的子類型。換句話說,子類可以返回更具體的子類型,而不必僅僅返回與父類方法完全相同的類型。
在Java中,方法的簽名由方法的名稱和參數(shù)列表組成,而不包括方法的返回類型。這意味著,如果兩個(gè)方法具有相同的名稱和參數(shù)列表,但返回類型不同,它們的方法簽名是相同的。這種情況下,編譯器無(wú)法區(qū)分這兩個(gè)方法。
然而,當(dāng)涉及到子類覆蓋(重寫)父類的方法時(shí),確實(shí)需要考慮返回類型的兼容性。盡管方法的簽名相同,但返回類型必須滿足協(xié)變性的原則,即子類方法的返回類型必須是父類方法返回類型的子類型。這樣做是為了確保子類對(duì)象可以被正確地視為父類對(duì)象,并且在使用父類方法時(shí)能夠處理返回值2。如果不滿足這個(gè)條件,編譯器會(huì)報(bào)錯(cuò),因?yàn)檫@可能會(huì)導(dǎo)致類型不匹配的錯(cuò)誤。
例如,假設(shè)有一個(gè)父類A和一個(gè)子類B,它們分別定義了一個(gè)同名方法foo(),父類方法的返回類型是A,子類方法的返回類型是B。子類B可以重寫父類A的方法,并將返回類型改為B,(1)因?yàn)锽是A的子類,所以B對(duì)象可以被視為A對(duì)象。(2)那么當(dāng)我們通過子類對(duì)象調(diào)用foo()方法時(shí),返回的是B類型的對(duì)象。
子類型返回的是父類型的子類型。也就是說,如果在子類中重寫父類的方法,并將返回類型改為父類返回類型的子類型,那么子類方法的返回值將是子類型的對(duì)象。
class A {public A foo() {return new A();}
}
class B extends A {@Overridepublic B foo() {return new B();}
}
這樣,當(dāng)我們通過父類引用調(diào)用子類對(duì)象的foo()方法時(shí),返回的是子類對(duì)象B。這種方式稱為協(xié)變【后面有解釋】返回類型,它允許我們?cè)谧宇愔蟹祷馗唧w的類型,提供了更好的靈活性和可讀性。
因?yàn)?B 是 A 的子類,所以 B 繼承了 A 的方法。這包括方法名和參數(shù)列表。
當(dāng)你在子類 B 中重寫父類 A 的方法時(shí),你可以將返回類型更改為 B。這是合法的,因?yàn)?B 是 A 的子類,所以 B 的對(duì)象可以被視為 A 的對(duì)象。
當(dāng)你創(chuàng)建一個(gè)子類 B 的對(duì)象并調(diào)用重寫后的方法時(shí),實(shí)際上你可以將返回的 B 對(duì)象視為 A 對(duì)象的一種擴(kuò)展。這是多態(tài)性的體現(xiàn),你可以通過父類引用調(diào)用子類方法,而不必了解具體的子類類型。
具體的例子:
class Animal {public Animal reproduce() {return new Animal();}
}
class Dog extends Animal {@Overridepublic Dog reproduce() {return new Dog();}
}
在上面的代碼中,有一個(gè)父類Animal和一個(gè)子類Dog。父類Animal定義了一個(gè)方法reproduce()
,返回類型為Animal。子類Dog重寫了父類Animal的方法,并將返回類型改為Dog。
當(dāng)我們使用子類Dog的對(duì)象調(diào)用reproduce()
方法時(shí):
Dog dog = new Dog(); // 創(chuàng)建一個(gè)新的 Dog 對(duì)象,賦值給 dog 變量
Dog newDog = dog.reproduce(); // 調(diào)用 dog 對(duì)象的 reproduce() 方法,返回一個(gè)新的 Dog 對(duì)象,賦值給 newDog 變量
通過子類重寫父類方法,我們可以更具體地表達(dá)子類對(duì)象的行為。在這個(gè)例子中,子類Dog重寫了父類Animal的reproduce()
方法,并確保返回的是Dog類型的對(duì)象。這樣,在使用子類Dog對(duì)象調(diào)用reproduce()
方法時(shí),我們可以直接獲得一個(gè)新的Dog對(duì)象。
詳細(xì):
當(dāng)一個(gè)父類有一個(gè)方法返回某種類型,子類可以覆蓋這個(gè)方法并返回該類型的子類型。
假設(shè)有一個(gè)父類 Animal
和一個(gè)子類 Dog
,并且在 Animal
類中有一個(gè)方法 makeSound()
,返回類型是 String
,表示動(dòng)物發(fā)出的聲音。在子類 Dog
中,可以覆蓋 makeSound()
方法并返回 Bark
類的實(shí)例,表示狗的吠聲。
class Animal {public String makeSound() {return "Some generic animal sound";}
}class Dog extends Animal {public Bark makeSound() {return new Bark();}
}class Bark {public String sound() {return "Woof woof!";}
}
在這個(gè)例子中,子類 Dog
覆蓋了父類 Animal
的 makeSound()
方法,并且返回類型從 String
改變?yōu)榱?Bark
類的實(shí)例,這是父類方法返回類型的子類型。這樣,你可以調(diào)用 Dog
類的 makeSound()
方法得到一個(gè)更具體的結(jié)果,而不僅僅是通用的動(dòng)物聲音。
“協(xié)變”(covariant)是一種類型關(guān)系 —>子類型的某些屬性或方法可以比父類型更具體(特化)"
“協(xié)變”(covariant)是一種類型關(guān)系,指的是子類型與父類型之間的關(guān)系,在這種關(guān)系下,子類型的某些屬性或方法可以比父類型更具體(特化)。在你提到的情況中,"協(xié)變的返回類型"指的是子類方法的返回類型可以是原始方法返回類型的子類型。這意味著,子類可以返回更具體的類型,而不違反方法簽名的規(guī)則。
協(xié)變返回類型主要體現(xiàn)在子類覆蓋(override)了父類方法并改變了方法的返回類型,使其返回比父類更具體的類型。具體來說,以下部分展示了協(xié)變返回類型的使用:
class Publication {private String title;public Publication(String title) {this.title = title;}public String getTitle() {return title;}
}class Book extends Publication {private String author;public Book(String title, String author) {super(title);this.author = author;}public String getAuthor() {return author;}
}class Magazine extends Publication {private int issueNumber;public Magazine(String title, int issueNumber) {super(title);this.issueNumber = issueNumber;}public int getIssueNumber() {return issueNumber;}
}class Library {public Publication getRecommendation() {// 返回一個(gè) Publication 對(duì)象作為推薦讀物return new Publication("Generic Recommendation");}
}class BookLibrary extends Library {@Overridepublic Book getRecommendation() {// 返回一個(gè) Book 對(duì)象作為推薦讀物return new Book("The Catcher in the Rye", "J.D. Salinger");}
}class MagazineLibrary extends Library {@Overridepublic Magazine getRecommendation() {// 返回一個(gè) Magazine 對(duì)象作為推薦讀物return new Magazine("National Geographic", 123);}
}
在這個(gè)例子中,Library
類的 getRecommendation
方法返回類型是 Publication
,而BookLibrary
和 MagazineLibrary
分別覆蓋了這個(gè)方法,并將返回類型改為更具體的 Book
和 Magazine
。這種方式允許子類方法返回比父類更具體的類型,而仍然保持方法簽名的一致性。
所以,協(xié)變返回類型在這里的體現(xiàn)就是子類方法覆蓋了父類方法并返回更具體的類型,這符合"子類型與父類型之間的關(guān)系,在這種關(guān)系下,子類型的某些屬性或方法可以比父類型更具體(特化)"的解釋。
協(xié)變(covariance)是指子類型的返回類型可以是父類型的子類型,而逆變(contravariance)是指子類型的返回類型可以是父類型的超類型。
逆變表示一個(gè)泛型類型的參數(shù)類型在繼承關(guān)系中變得更加具體(即變窄)。逆變通常在方法參數(shù)中使用,允許傳遞更通用的類型。
協(xié)變表示一個(gè)泛型類型的返回類型在繼承關(guān)系中變得更加通用(即變寬)。協(xié)變通常在方法返回值中使用,允許返回更具體的類型。
-
協(xié)變:如果類型B是類型A的子類型,那么在某些上下文中,我們可以使用類型B的對(duì)象替代類型A的對(duì)象。這通常應(yīng)用于方法的返回類型。例如,如果一個(gè)方法返回一個(gè)Animal類型的對(duì)象,那么它也可以返回一個(gè)Dog類型的對(duì)象(假設(shè)Dog是Animal的子類)。
-
逆變:如果類型B是類型A的子類型,那么在某些上下文中,我們可以使用類型A的對(duì)象替代類型B的對(duì)象。這通常應(yīng)用于方法的參數(shù)類型。例如,如果一個(gè)方法接受一個(gè)Dog類型的對(duì)象作為參數(shù),那么它也可以接受一個(gè)Animal類型的對(duì)象(假設(shè)Dog是Animal的子類)。
-
協(xié)變和逆變的主要目的是提供更大的靈活性和類型安全。它們?cè)试S我們?cè)诒3诸愋桶踩耐瑫r(shí),編寫更通用和可重用的代碼。例如,如果我們有一個(gè)處理Animal對(duì)象的方法,通過逆變,我們可以將這個(gè)方法應(yīng)用于Dog對(duì)象,而無(wú)需編寫專門處理Dog對(duì)象的方法。
class Animal {public void eat() {System.out.println("Animal eats");}
}class Dog extends Animal {@Overridepublic void eat() {System.out.println("Dog eats");}
}class AnimalHelper {// 協(xié)變:返回類型是協(xié)變的public Dog getAnimal() {return new Dog();}// 逆變:參數(shù)類型是逆變的public void feedAnimal(Animal animal) {animal.eat();}
}public class Mains {public static void main(String[] args) {AnimalHelper helper = new AnimalHelper();// 協(xié)變:我們可以將Dog賦值給AnimalAnimal animal = helper.getAnimal();// 逆變:我們可以將Dog傳遞給接受Animal的方法helper.feedAnimal(new Dog());}
}
在這個(gè)例子中,AnimalHelper 類中的兩個(gè)方法展示了協(xié)變和逆變的概念:
getAnimal() 方法使用協(xié)變:返回類型 Dog 是協(xié)變的。這意味著你可以將一個(gè)返回類型為 Dog 的方法賦值給類型為 Animal 的引用變量。因?yàn)?Dog 是 Animal 的子類,所以這個(gè)協(xié)變操作是安全的。
feedAnimal(Animal animal) 方法使用逆變:參數(shù)類型 Animal 是逆變的。這意味著你可以將一個(gè)類型為 Dog 的對(duì)象傳遞給接受 Animal 類型參數(shù)的方法。由于 Dog 是 Animal 的子類,所以逆變操作也是安全的。
在 main 方法中,你展示了如何使用這些協(xié)變和逆變的特性:
協(xié)變:你調(diào)用 helper.getAnimal() 方法并將返回值賦給一個(gè)類型為 Animal 的引用變量 animal。這是因?yàn)槟憧梢詫?Dog 類型賦值給 Animal 類型,利用了返回類型的協(xié)變特性。
逆變:你調(diào)用 helper.feedAnimal(new Dog()) 方法,將 Dog 對(duì)象傳遞給接受 Animal 類型參數(shù)的方法。這是因?yàn)槟憧梢詫?Dog 類型傳遞給接受 Animal 類型參數(shù)的方法,利用了參數(shù)類型的逆變特性。
注意
協(xié)變:
在協(xié)變中,我們可以將派生類(如 Dog)的實(shí)例分配給基類(如 Animal)的引用。這是因?yàn)榕缮愂腔惖囊粋€(gè)特殊類型,具有基類的所有屬性和方法,但可能還有額外的特性。
協(xié)變涉及返回類型。例如,在協(xié)變中,你可以在方法返回類型為 Dog 的情況下,將返回值賦給類型為 Animal 的引用變量。
逆變
在逆變中,我們可以將基類(如 Animal)的實(shí)例傳遞給接受派生類(如 Dog)類型參數(shù)的方法。這是因?yàn)榛愂桥缮惖耐ㄓ妙愋?#xff0c;基類對(duì)象中包含了派生類對(duì)象的通用行為。
逆變涉及方法參數(shù)類型。例如,在逆變中,你可以將類型為 Dog 的參數(shù)傳遞給接受 Animal 類型參數(shù)的方法。
區(qū)別
在協(xié)變中,“賦值” 涉及將一個(gè)具體類型(如 Dog)賦值給更通用的類型(如 Animal)的引用變量。這是由于返回類型的協(xié)變特性,可以保證派生類對(duì)象的特性在基類引用中仍然有效。
在逆變中,“傳遞” 涉及將一個(gè)基類類型的對(duì)象傳遞給接受派生類類型參數(shù)的方法。這是由于參數(shù)類型的逆變特性,允許基類對(duì)象的通用屬性在派生類方法中得到正確的處理。
通過你提供的代碼,你確實(shí)展示了這兩種情況的應(yīng)用。通過 AnimalHelper 類的 getAnimal() 方法,你展示了協(xié)變,因?yàn)槟憧梢詫?Dog 類型的實(shí)例分配給類型為 Animal 的引用變量。通過 feedAnimal(Animal animal) 方法,你展示了逆變,因?yàn)槟憧梢詫?Dog 類型的對(duì)象傳遞給接受 Animal 類型參數(shù)的方法。這兩種特性一起允許你在泛型方法中更靈活地操作不同類型的對(duì)象。
1.具體說明是怎么實(shí)現(xiàn)_協(xié)變
具體來說,getAnimal方法的返回類型是Dog,但在main方法中,我們將這個(gè)Dog對(duì)象賦值給了一個(gè)Animal類型的變量。這是因?yàn)镈og是Animal的子類型,所以我們可以使用Dog對(duì)象替代Animal對(duì)象。這就是協(xié)變的概念。
class AnimalHelper {// 協(xié)變:返回類型是協(xié)變的public Dog getAnimal() {return new Dog();}
}public class Contravariance {public static void main(String[] args) {AnimalHelper helper = new AnimalHelper();// 協(xié)變:我們可以將Dog賦值給AnimalAnimal animal = helper.getAnimal();animal.eat();}
}
在這段代碼中,Dog對(duì)象被賦值給了Animal類型的變量,這是協(xié)變的一個(gè)例子。
2.具體說明是怎么實(shí)現(xiàn)_逆變
在AnimalHelper類的feedAnimal方法中,參數(shù)類型是Animal。這意味著你可以傳遞任何Animal對(duì)象或其子類的對(duì)象給這個(gè)方法。因此,你可以將Dog對(duì)象傳遞給這個(gè)方法,即使它期望的是一個(gè)Animal對(duì)象。這就是逆變的概念。
在main方法中,你創(chuàng)建了一個(gè)Dog對(duì)象,并將其傳遞給了feedAnimal方法。盡管feedAnimal方法期望的是一個(gè)Animal對(duì)象,但由于Dog是Animal的子類,因此你可以將Dog對(duì)象傳遞給它。這就是逆變的一個(gè)具體應(yīng)用。
這段代碼的運(yùn)行結(jié)果將是輸出"Dog eats",因?yàn)閒eedAnimal方法調(diào)用了傳入對(duì)象的eat方法,而傳入的對(duì)象是一個(gè)Dog對(duì)象,所以調(diào)用的是Dog類中覆寫的eat方法。
靜態(tài)綁定(Static Binding)和動(dòng)態(tài)綁定(Dynamic Binding)
總的來說,靜態(tài)綁定在編譯時(shí)就確定方法調(diào)用,速度快但不夠靈活,而動(dòng)態(tài)綁定在運(yùn)行時(shí)根據(jù)實(shí)際對(duì)象類型確定方法調(diào)用,更加靈活但速度相對(duì)較慢。在Java中,大部分方法調(diào)用使用的是動(dòng)態(tài)綁定,因?yàn)樗軌蛑С侄鄳B(tài)性和繼承等特性。
1. 靜態(tài)綁定(Static Binding):編譯時(shí)期確定的方法或函數(shù)【可共享的】
靜態(tài)綁定是在**編譯時(shí)期確定方法或函數(shù)的調(diào)用**,不需要等到運(yùn)行時(shí)期才決定。它適用于private方法、static方法、final方法和構(gòu)造器的調(diào)用。
靜態(tài)綁定(static binding)是指在編譯時(shí)期(compile time)就能確定方法或函數(shù)的調(diào)用,不需要等到運(yùn)行時(shí)期才決定。在靜態(tài)綁定中,方法或函數(shù)的調(diào)用是根據(jù)引用類型(也稱為編譯時(shí)類型)來決定的。當(dāng)編譯器在編譯代碼時(shí),會(huì)根據(jù)引用類型確定調(diào)用該類型定義的方法或函數(shù)。因此,靜態(tài)綁定的方法或函數(shù)調(diào)用是在編譯時(shí)期就確定的,不會(huì)受到運(yùn)行時(shí)期對(duì)象的實(shí)際類型的影響。
- 發(fā)生在編譯時(shí)(compile time)。
- 適用于private、static、final方法以及構(gòu)造器等情況。
- 在編譯時(shí)就能確定要調(diào)用的方法,因?yàn)榉椒ǖ倪x擇是基于引用變量的聲明類型。
- 編譯器可以在編譯階段直接解析方法調(diào)用,因此速度較快。
靜態(tài)綁定適用于以下情況:
- Private 方法:由于private方法在同一類中是不可繼承的,編譯器可以直接確定要調(diào)用的方法。
private方法是不能被子類重寫的,所以調(diào)用private方法時(shí),編譯器可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)方法。
- Static 方法:靜態(tài)方法屬于類而不是實(shí)例,因此不需要實(shí)例化對(duì)象就可以調(diào)用。編譯器可以根據(jù)聲明的類來確定要調(diào)用的靜態(tài)方法。
static方法是屬于類而不是對(duì)象的,所以調(diào)用static方法時(shí),編譯器可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)方法。
- Final 方法:final修飾的方法不能被子類重寫,因此編譯器可以準(zhǔn)確知道要調(diào)用的是聲明的類中的final方法。
final方法是不能被子類重寫的,所以調(diào)用final方法時(shí),編譯器可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)方法。
- 構(gòu)造器:在創(chuàng)建對(duì)象時(shí),編譯器根據(jù)構(gòu)造器的參數(shù)列表來準(zhǔn)確地選擇合適的構(gòu)造器。
構(gòu)造器是用于創(chuàng)建對(duì)象的特殊方法,調(diào)用構(gòu)造器時(shí),編譯器可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)構(gòu)造器。
靜態(tài)綁定的一個(gè)特點(diǎn)是,它在編譯時(shí)就能夠解析方法調(diào)用,因此執(zhí)行速度較快。但是,靜態(tài)綁定缺乏動(dòng)態(tài)繼承和多態(tài)性的特性,因?yàn)樗粫?huì)考慮對(duì)象的實(shí)際類型。相比之下,動(dòng)態(tài)綁定會(huì)在運(yùn)行時(shí)根據(jù)實(shí)際對(duì)象類型來確定方法調(diào)用,提供了更大的靈活性和多態(tài)性。
舉例代碼:
class Animal {public static void printType() {System.out.println("This is an animal.");}
}class Dog extends Animal {public static void printType() {System.out.println("This is a dog.");}
}public class Main {public static void main(String[] args) {Animal animal = new Animal();Animal dog = new Dog();Dog realUnderDogClassDog = new Dog();animal.printType(); // 靜態(tài)綁定,輸出:"This is an animal."dog.printType(); // 靜態(tài)綁定,輸出:"This is an animal.",因?yàn)殪o態(tài)方法不會(huì)被子類重寫realUnderDogClassDog.printType();// 靜態(tài)綁定,輸出:"This is a dog."}
}
由于printType()方法是靜態(tài)方法,靜態(tài)方法的調(diào)用是根據(jù)引用類型來確定的,不會(huì)受到運(yùn)行時(shí)期對(duì)象的實(shí)際類型的影響。所以無(wú)論animal變量引用的是Animal對(duì)象還是dog變量引用的是Dog對(duì)象,調(diào)用printType()方法時(shí)都會(huì)直接調(diào)用Animal類中定義的printType()方法。因此,輸出結(jié)果都是"This is an animal."。
這就是靜態(tài)綁定的特點(diǎn),方法調(diào)用在編譯時(shí)期就已經(jīng)確定,不會(huì)根據(jù)對(duì)象的實(shí)際類型來決定。
靜態(tài)常量和靜態(tài)綁定是兩個(gè)不同的概念。
靜態(tài)常量是指在編譯時(shí)期就確定并且不能被修改的常量。它通常使用關(guān)鍵字final
來聲明,并且在聲明時(shí)就必須初始化。靜態(tài)常量在類加載時(shí)被初始化,并且可以通過類名直接訪問。靜態(tài)常量是類級(jí)別的,意味著它在整個(gè)類中都是共享的,所有對(duì)象共享同一個(gè)靜態(tài)常量的值。
靜態(tài)綁定是指在編譯時(shí)期就確定方法或函數(shù)的調(diào)用。它發(fā)生在靜態(tài)方法、靜態(tài)變量、靜態(tài)常量的訪問以及使用類名訪問靜態(tài)成員時(shí)。靜態(tài)綁定是根據(jù)引用類型來決定方法或函數(shù)的調(diào)用,不會(huì)受到對(duì)象的實(shí)際類型的影響。這是因?yàn)殪o態(tài)成員和靜態(tài)方法是與類關(guān)聯(lián)的,不依賴于對(duì)象的創(chuàng)建。因此,無(wú)論使用哪個(gè)對(duì)象引用去調(diào)用靜態(tài)成員或靜態(tài)方法,都會(huì)調(diào)用到類中定義的靜態(tài)成員或靜態(tài)方法。
總結(jié)起來,靜態(tài)常量是在編譯時(shí)期確定并且不能被修改的常量,而靜態(tài)綁定是在編譯時(shí)期確定方法或函數(shù)的調(diào)用。靜態(tài)常量是類級(jí)別的,可以通過類名直接訪問,而靜態(tài)綁定是根據(jù)引用類型來決定方法或函數(shù)的調(diào)用,并且不受對(duì)象的實(shí)際類型的影響。
在Java中,static
和 final
都是關(guān)鍵字,用于定義變量的特性。它們有不同的含義和用途。
首先,我們分別來看看 static
、final
和 static final
的含義:
-
static
:用于表示變量或方法是類級(jí)別的,不依賴于對(duì)象的創(chuàng)建,可以通過類名直接訪問。但是static
并不代表不可修改,靜態(tài)變量在程序運(yùn)行期間是可以被修改的。 -
final
:用于表示常量,一旦賦值后就不能再修改。可以用于變量、方法、類等地方。 -
static final
:將static
和final
結(jié)合在一起,表示一個(gè)類級(jí)別的不可修改的常量。
現(xiàn)在,我們將上述概念應(yīng)用于代碼,并逐步思考輸出結(jié)果:
public class StaticFinalExample {public static String staticVariable = "Static Variable";public final String finalVariable = "Final Variable";public static final String staticFinalVariable = "Static Final Variable";public static void main(String[] args) {StaticFinalExample obj1 = new StaticFinalExample();// 輸出1:訪問靜態(tài)變量System.out.println("Static Variable (obj1): " + obj1.staticVariable);obj1.staticVariable = "hello"; // 修改靜態(tài)變量的值System.out.println("Static Variable (obj1): " + obj1.staticVariable);// 輸出2:訪問 final 變量System.out.println("Final Variable (obj1): " + obj1.finalVariable);// 嘗試修改 final 變量的值不會(huì)導(dǎo)致編譯錯(cuò)誤,但運(yùn)行時(shí)會(huì)報(bào)錯(cuò)// obj1.finalVariable = "Modified Final Variable"; // 輸出3:訪問 static final 變量System.out.println("Static Final Variable (obj1): " + obj1.staticFinalVariable);// 嘗試修改 static final 變量的值會(huì)導(dǎo)致編譯錯(cuò)誤// obj1.staticFinalVariable = "Modified Static Final Variable"; // 輸出4:輸出修改后的值System.out.println("Static Variable (obj1): " + obj1.staticVariable);}
}
需要注意的是,嘗試修改 final
和 static final
變量的值會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)樗鼈儽宦暶鳛椴豢尚薷牡某A俊?/h6>
總結(jié):
static
表示類級(jí)別的,可以通過類名直接訪問的變量或方法。final
表示常量,一旦賦值后不能再修改。static final
表示類級(jí)別的不可修改的常量。static
和final
可以獨(dú)立使用,也可以結(jié)合在一起使用。
- Math類中的定義:
/*** The {@code double} value that is closer than any other to* <i>e</i>, the base of the natural logarithms.*/public static final double E = 2.7182818284590452354;/*** The {@code double} value that is closer than any other to* <i>pi</i>, the ratio of the circumference of a circle to its* diameter.*/public static final double PI = 3.14159265358979323846;/*** Constant by which to multiply an angular value in degrees to obtain an* angular value in radians.*/private static final double DEGREES_TO_RADIANS = 0.017453292519943295;/*** Constant by which to multiply an angular value in radians to obtain an* angular value in degrees.*/private static final double RADIANS_TO_DEGREES = 57.29577951308232;
2. 動(dòng)態(tài)綁定(Dynamic Binding):
動(dòng)態(tài)綁定(Dynamic Binding)是一種方法調(diào)用的綁定方式,它發(fā)生在運(yùn)行時(shí)(runtime)。在動(dòng)態(tài)綁定中,方法的選擇是基于對(duì)象的實(shí)際類型,而不僅僅是引用變量的聲明類型。它適用于非私有、非靜態(tài)、非final的實(shí)例方法和接口方法的調(diào)用,實(shí)現(xiàn)了多態(tài)性。
動(dòng)態(tài)綁定(dynamic binding)是指在運(yùn)行時(shí)期(runtime)根據(jù)對(duì)象的實(shí)際類型來決定方法或函數(shù)的調(diào)用。在動(dòng)態(tài)綁定中,方法或函數(shù)的調(diào)用是根據(jù)實(shí)際類型(也稱為運(yùn)行時(shí)類型)來決定的。當(dāng)程序在運(yùn)行時(shí)期調(diào)用方法或函數(shù)時(shí),會(huì)根據(jù)對(duì)象的實(shí)際類型來確定調(diào)用該類型定義的方法或函數(shù)。因此,動(dòng)態(tài)綁定的方法或函數(shù)調(diào)用是在運(yùn)行時(shí)期根據(jù)對(duì)象的實(shí)際類型決定的。
- 發(fā)生在運(yùn)行時(shí)(runtime)。
- 適用于方法調(diào)用依賴于隱式參數(shù)的實(shí)際類型的情況。
- 在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來確定要調(diào)用的方法,方法選擇是基于對(duì)象的實(shí)際類型。
- 需要在每次方法調(diào)用時(shí)進(jìn)行實(shí)際的查找,因此速度相對(duì)較慢,但提供了更大的靈活性和多態(tài)性。
動(dòng)態(tài)綁定適用于以下情況:
- 調(diào)用非私有、非靜態(tài)、非final的實(shí)例方法:這些方法可以被子類重寫,所以在調(diào)用這些方法時(shí),會(huì)根據(jù)對(duì)象的實(shí)際類型來確定調(diào)用哪個(gè)方法。
- 調(diào)用接口方法:接口方法是抽象的方法定義,實(shí)現(xiàn)類需要實(shí)現(xiàn)接口方法,因此在調(diào)用接口方法時(shí),會(huì)根據(jù)實(shí)現(xiàn)類的實(shí)際類型來確定調(diào)用哪個(gè)方法。
代碼舉例:
動(dòng)態(tài)綁定是指在運(yùn)行時(shí)期根據(jù)對(duì)象的實(shí)際類型來確定方法或函數(shù)的調(diào)用。
class Animal {public void makeSound() {System.out.println("動(dòng)物發(fā)出聲音");}
}class Dog extends Animal {public void makeSound() {System.out.println("狗發(fā)出汪汪聲");}
}class Cat extends Animal {public void makeSound() {System.out.println("貓發(fā)出喵喵聲");}
}public class DynamicBindingExample {public static void main(String[] args) {Animal animal1 = new Animal();Animal animal2 = new Dog();Animal animal3 = new Cat();animal1.makeSound(); // 輸出:"動(dòng)物發(fā)出聲音"animal2.makeSound(); // 輸出:"狗發(fā)出汪汪聲"animal3.makeSound(); // 輸出:"貓發(fā)出喵喵聲"}
}
我們定義了用 Animal
類和它的兩個(gè)子類 Dog
和 Cat
。
Animal
類中有一個(gè) makeSound()
方法,而子類 Dog
和 Cat
分別覆蓋了該方法并提供了它們自己的實(shí)現(xiàn)。
在 main
方法中,我們創(chuàng)建了一個(gè) Animal
對(duì)象 animal1
,以及兩個(gè)子類對(duì)象 animal2
和 animal3
。
當(dāng)我們調(diào)用 `makeSound()` 方法時(shí),由于動(dòng)態(tài)綁定的作用,實(shí)際調(diào)用的方法是根據(jù)對(duì)象的實(shí)際類型來確定的。因此,
animal1.makeSound()
調(diào)用的是 Animal
類的 makeSound()
方法,
animal2.makeSound()
調(diào)用的是 Dog
類的 makeSound()
方法,
animal3.makeSound()
調(diào)用的是 Cat
類的 makeSound()
方法。
這就是動(dòng)態(tài)綁定的特性,它允許程序在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來確定調(diào)用的方法,而不是根據(jù)引用類型來確定。
總結(jié):
動(dòng)態(tài)綁定的特性允許子類對(duì)象在運(yùn)行時(shí)根據(jù)實(shí)際類型來調(diào)用適當(dāng)?shù)姆椒?#xff0c;而不是根據(jù)編譯時(shí)類型。這使得代碼可以更具適應(yīng)性,當(dāng)引入新的子類時(shí),無(wú)需修改現(xiàn)有的代碼就可以調(diào)用新子類特有的方法。
為確保在子類中覆蓋(重寫)超類方法時(shí),子類方法的訪問權(quán)限不能低于超類方法的訪問權(quán)限。如果超類方法是 public,那么子類方法也必須聲明為 public。如果違反了這一規(guī)則,編譯器將會(huì)報(bào)錯(cuò),因?yàn)檫@可能導(dǎo)致在某些情況下無(wú)法正常訪問繼承的方法。
Para_1:Java虛擬機(jī)(JVM)負(fù)責(zé)在運(yùn)行時(shí)解析和執(zhí)行Java程序【根據(jù)編譯后的字節(jié)碼文件生成和維護(hù)的。】。在Java程序啟動(dòng)時(shí),JVM會(huì)加載字節(jié)碼文件,并將類的方法信息存儲(chǔ)在方法區(qū)(Method Area)中。包括創(chuàng)建方法表【也稱為虛方法表__vtable】。
方法表用于存儲(chǔ)類的實(shí)例方法信息和調(diào)用地址,以便在運(yùn)行時(shí)進(jìn)行動(dòng)態(tài)綁定和多態(tài)調(diào)用。因此,形成方法表是由JVM在程序運(yùn)行時(shí)完成的?!痉椒ū淼慕Y(jié)構(gòu)和存儲(chǔ)方式是由JVM定義的,因此不同的JVM實(shí)現(xiàn)可能會(huì)有一些細(xì)節(jié)上的差異?!?br /> Para_2:方法表(method table)是虛擬機(jī)為每個(gè)類預(yù)先計(jì)算【存儲(chǔ)方法信息】的數(shù)據(jù)結(jié)構(gòu),用于記錄類中所有方法的簽名和要調(diào)用的實(shí)際方法【它包含了類中定義的所有方法的信息,包括方法的名稱、參數(shù)類型、返回類型、訪問修飾符等?!?。
當(dāng)程序運(yùn)行并且采用動(dòng)態(tài)綁定調(diào)用方法時(shí),虛擬機(jī)會(huì)根據(jù)對(duì)象的實(shí)際類型在方法表中查找對(duì)應(yīng)的方法。如果找到了與實(shí)際類型對(duì)應(yīng)的方法,則調(diào)用該方法;如果沒有找到,則會(huì)在父類中尋找,直到找到對(duì)應(yīng)的方法或者到達(dá)頂層父類為止。【方法表的主要作用是在運(yùn)行時(shí)支持動(dòng)態(tài)綁定和多態(tài)性?!?br /> Para_3:在程序運(yùn)行時(shí),當(dāng)調(diào)用一個(gè)方法時(shí),JVM會(huì)根據(jù)方法表中的信息來確定要調(diào)用的具體方法。由于方法表記錄了方法的實(shí)際地址或偏移量,因此可以實(shí)現(xiàn)多態(tài)特性,即在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來確定調(diào)用哪個(gè)具體的方法。
需要注意的是:方法表是在編譯時(shí)生成的,而不是在運(yùn)行時(shí)動(dòng)態(tài)生成的。因此,對(duì)于動(dòng)態(tài)添加的方法或者通過反射機(jī)制生成的方法,方法表中是不會(huì)包含這些方法的信息的。 ??Java類的返回值類型可以是任何有效的數(shù)據(jù)類型,包括原始數(shù)據(jù)類型 (如int、double.char等)、對(duì)象類型(如自定義類、String等)、數(shù)組類型等。具體的返回值類型取決于方法的定義和實(shí)現(xiàn)。 ??