字節(jié)碼知識(shí)點(diǎn)整理
對(duì)于Java類中的每個(gè)實(shí)例方法(非static方法),其在編譯后所生成的字節(jié)碼當(dāng)中,方法參數(shù)的數(shù)量總是會(huì)比源代碼中方法參數(shù)的數(shù)量多一個(gè)(this),它位于方法的第一個(gè)參數(shù)位置處;這樣,我們就可以在Java的實(shí)例方法中使用this來(lái)去訪問(wèn)當(dāng)前對(duì)象屬性以及其他方法。
這個(gè)操作是在編譯期間完成的,即由javac編譯器在編譯的時(shí)候?qū)?duì)this的訪問(wèn)轉(zhuǎn)化為對(duì)一個(gè)普通實(shí)例方法參數(shù)的訪問(wèn),接下來(lái)在運(yùn)行期間,由JVM在調(diào)用實(shí)例方法時(shí),自動(dòng)向?qū)嵗椒▊魅朐搕his參數(shù)。所以,在實(shí)例方法的局部變量表中,至少會(huì)有一個(gè)指向當(dāng)前對(duì)象的局部變量。
Java字節(jié)碼對(duì)于異常的處理方式:
1.統(tǒng)一采用異常表的方式來(lái)對(duì)異常進(jìn)行處理。
2.在jdk1.4.2之前的版本中,并不是使用異常表的方式來(lái)對(duì)異常進(jìn)行處理的,而是采用特定的指令方式。
3.當(dāng)異常處理存在finally語(yǔ)句塊時(shí),現(xiàn)代化的JVM采取的處理方式是將finally語(yǔ)句塊的字節(jié)碼拼接到每一個(gè)catch塊后面,換句話說(shuō),程序中存在多少個(gè)catch塊,就會(huì)在每一個(gè)catch塊后面重復(fù)多少個(gè)finally語(yǔ)句塊的字節(jié)碼。
棧幀(stack frame)
棧幀是一種用于幫助虛擬機(jī)執(zhí)行方法調(diào)用與方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)。
棧幀本身是一種數(shù)據(jù)結(jié)構(gòu),封裝了方法的局部變量表、動(dòng)態(tài)鏈接信息,方法的返回地址以及操作數(shù)棧等信息。
符號(hào)引用,直接引用
有些符號(hào)引用是在類加載階段或是第一次使用時(shí)就會(huì)轉(zhuǎn)換為直接引用,這種轉(zhuǎn)換叫做靜態(tài)解析;另外一些符號(hào)引用則是在每次運(yùn)行期轉(zhuǎn)換為直接引用,這種轉(zhuǎn)換叫做動(dòng)態(tài)鏈接,這體現(xiàn)為Java的多態(tài)性。
invokeinterface:調(diào)用接口中的方法,實(shí)際上是在運(yùn)行期決定的,決定到底調(diào)用實(shí)現(xiàn)該接口的哪個(gè)對(duì)象的特定方法。
invokestatic:調(diào)用靜態(tài)方法。
invokespecial:調(diào)用自己的私有方法、構(gòu)造方法(<init>)以及父類的方法。
invokevirtual:調(diào)用虛方法,運(yùn)行期動(dòng)態(tài)查找的過(guò)程。
invokedynamic:動(dòng)態(tài)調(diào)用方法。
靜態(tài)解析的4種情形:
1.靜態(tài)方法
2.父類方法
3.構(gòu)造方法
4.私有方法
以上4類方法稱作非虛方法,他們是在類加載階段就可以將符號(hào)引用轉(zhuǎn)換為直接引用。
方法的靜態(tài)分派:
Grandpa g1 = new Grandpa();
以上代碼,g1的靜態(tài)類型是Grandpa,而g1的實(shí)際類型(真正指向的類型)是Father。
我們可以得出這樣一個(gè)結(jié)論:變量的靜態(tài)類型是不會(huì)發(fā)生變化的,而變量的實(shí)際類型則是可以發(fā)生變化的(多態(tài)的一種體現(xiàn)),實(shí)際類型是在運(yùn)行期方可確定。
示例代碼:
public class MyTest5 {
//方法重載,是一種靜態(tài)的行為,編譯器就可以完全確定。
public void test(Grandpa grandpa) {
System.out.println("grandpa");
}
public void test(Father father) {
System.out.println("father");
}
public void test(Son son) {
System.out.println("son");
}
public static void main(String[] args) {
Grandpa g1 = new Father();
Grandpa g2 = new Son();
MyTest5 myTest5 = new MyTest5();
myTest5.test(g1);
myTest5.test(g2);
}
}
class Grandpa {
}
class Father extends Grandpa {
}
class Son extends Father {
}
方法的動(dòng)態(tài)分派
方法的動(dòng)態(tài)分派涉及到一個(gè)重要概念:方法接受者。
invokevirtual字節(jié)碼指令單多態(tài)查找流程
比較方法重載(overload)與方法重寫(overwrite),我們可以得到這樣的結(jié)論:
方法重載是靜態(tài)的,是編譯期行為;方法重寫是動(dòng)態(tài)的,事運(yùn)行期行為。
示例代碼
public class MyTest6 {
public static void main(String[] args) {
Fruit apple = new Apple();
Fruit orange = new Orange();
apple.test();
orange.test();
apple = new Orange();
apple.test();
}
}
class Fruit{
public void test(){
System.out.println("Fruit");
}
}
class Apple extends Fruit{
@Override
public void test() {
System.out.println("Apple");
}
}
class Orange extends Fruit{
@Override
public void test() {
System.out.println("Orange");
}
}
針對(duì)于方法調(diào)用動(dòng)態(tài)分派的過(guò)程,虛擬機(jī)會(huì)在類的方法區(qū)建立一個(gè)虛方法表的數(shù)據(jù)結(jié)構(gòu)(virtual method table, vtable)
針對(duì)于invokeinterface指令來(lái)說(shuō),虛擬機(jī)會(huì)建立一個(gè)叫做接口方法表的數(shù)據(jù)結(jié)構(gòu)(interface method table,itable)
示例代碼:
public class MyTest7 {
public static void main(String[] args) {
Animal animal = new Animal();
Animal dog = new Dog();
animal.test("hello");
dog.test(new Date());
}
}
class Animal {
public void test(String str) {
System.out.println("animal str");
}
public void test(Date date) {
System.out.println("animal date");
}
}
class Dog extends Animal {
@Override
public void test(String str) {
System.out.println("dog str");
}
@Override
public void test(Date date) {
System.out.println("dog date");
}
}