溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java中不為人知的特殊方法是什么

發(fā)布時(shí)間:2021-11-30 14:40:14 來源:億速云 閱讀:127 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Java中不為人知的特殊方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java中不為人知的特殊方法是什么”吧!

可見性

當(dāng)你創(chuàng)建一個(gè)嵌套類的時(shí)候,它的私有變量和方法對(duì)上層的類是可見的。這個(gè)在不可變嵌套式Builder模式中用到了。這是Java語言規(guī)范里已經(jīng)定義好的一個(gè)行為。

package synthetic;
//java框架www.1b23.compublic class SyntheticMethodTest1 {private A aObj = new A();public class A {private int i;
    }private class B {private int i = aObj.i;
    }public static void main(String[] args) {
        SyntheticMethodTest1 me = new SyntheticMethodTest1();
        me.aObj.i = 1;
        B bObj = me.new B();
        System.out.println(bObj.i);
    }
}

JVM是如何處理這個(gè)的?它可不知道什么是內(nèi)部類或者嵌套類的。JVM對(duì)所有的類都一視同仁,它都認(rèn)為是頂級(jí)類。所有類都會(huì)被編譯成頂級(jí)類,而那些內(nèi)部類編譯完后會(huì)生成…$… class的類文件。

$ ls -Fart
../                         SyntheticMethodTest2$A.class  MyClass.java  SyntheticMethodTest4.java  SyntheticMethodTest2.java
SyntheticMethodTest2.class  SyntheticMethodTest3.java     ./            MyClassSon.java            SyntheticMethodTest1.java

如果你創(chuàng)建一個(gè)內(nèi)部類的話,它會(huì)被徹底編譯成一個(gè)頂級(jí)類。

那這些私有變量又是如何被外部類訪問的呢?如果它們是個(gè)頂級(jí)類的私有變量(它們的確也是),那為什么別的類還能直接訪問這些變量?

javac是這樣解決這個(gè)問題的,對(duì)于任何private的字段,方法或者構(gòu)造函數(shù),如果它們也被其它頂層類所使用,就會(huì)生成一個(gè)synthetic方法。這些synthetic方法是用來訪問最初的私有變量/方法/構(gòu)造函數(shù)的。這些方法的生成也很智能:只有確實(shí)被外部類用到了,才會(huì)生成這樣的方法。

package synthetic;import java.lang.reflect.Constructor;import java.lang.reflect.Method;
//java框架www.1b23.compublic class SyntheticMethodTest2 {public static class A {private A(){}private int x;private void x(){};
    }public static void main(String[] args) {
        A a = new A();
        a.x = 2;
        a.x();
        System.out.println(a.x);for (Method m : A.class.getDeclaredMethods()) {
            System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getName());
        }
        System.out.println("--------------------------");for (Method m : A.class.getMethods()) {
            System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName());
        }
        System.out.println("--------------------------");for( Constructor<?> c : A.class.getDeclaredConstructors() ){
            System.out.println(String.format("%08X", c.getModifiers()) + " " + c.getName());
        }
    }
}

這些生成的方法的名字取決于具體的實(shí)現(xiàn),最后叫什么也不好說。我只能說在我運(yùn)行的這個(gè)平臺(tái)上,上述程序的輸出是這樣的:

200001008 access$100001008 access$200001008 access$300000002 x
--------------------------00000111 void wait00000011 void wait00000011 void wait00000001 boolean equals00000001 String toString00000101 int hashCode00000111 Class getClass00000111 void notify00000111 void notifyAll
--------------------------00000002 synthetic.SyntheticMethodTest2$A00001000 synthetic.SyntheticMethodTest2$A

在上面這個(gè)程序中,我們給變量x賦值,然后又調(diào)用了一個(gè)同名的方法。這會(huì)觸發(fā)編譯器生成對(duì)應(yīng)的synthetic方法。你會(huì)看到它生成了三個(gè)方法,應(yīng)該是x變量的setter和getter方法,以及x()方法對(duì)應(yīng)的一個(gè)synthetic方法。這些方法并不存在于getMethods方法里返回的列表中,因?yàn)樗鼈兪莝ynthetic方法,是不能直接被調(diào)用的。從這點(diǎn)來看,它們和私有方法差不多。

看一下java.lang.reflect.Modifier里面定義的常量,可以明白這些十六進(jìn)制的數(shù)字代表的是什么:

00001008 SYNTHETIC|STATIC00000002 PRIVATE00000111 NATIVE|FINAL|PUBLIC00000011 FINAL|PUBLIC00000001 PUBLIC00001000 SYNTHETIC

列表中有兩個(gè)是構(gòu)造方法。還有一個(gè)私有方法以及一個(gè)synthetic方法。存在這個(gè)私有方法是因?yàn)槲覀兇_實(shí)定義了它。而synthetic方法的出現(xiàn)是因?yàn)槲覀儚耐獠款愓{(diào)用了它內(nèi)部的私有成員。到目前為止,還沒有出現(xiàn)過bridge方法。

泛型和繼承

到目前為止,看起來還不錯(cuò)。不過我們還沒有看到”volatile”方法。

看一下java.lang.reflect.Modifier的源碼你會(huì)發(fā)現(xiàn)0x00000040這個(gè)常量被定義了兩次。一次是定義成VOLATILE,還有一次是BRIDGE(后者是包內(nèi)部私有的,并不對(duì)外開放)。

想出現(xiàn)volatile方法的話,寫個(gè)簡(jiǎn)單的程序就行了:

package synthetic;import java.lang.reflect.Method;import java.util.LinkedList;
//java框架www.1b23.compublic class SyntheticMethodTest3 {public static class MyLink extends LinkedList {@Overridepublic String get(int i) {return "";
        }
    }public static void main(String[] args) {for (Method m : MyLink.class.getDeclaredMethods()) {
            System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName());
        }
    }
}

這個(gè)鏈表有一個(gè)返回String的get(int)方法。先別討論代碼整不整潔的問題了。這只是段示例代碼而已。整潔的代碼當(dāng)然也會(huì)出現(xiàn)同樣的問題,不過越復(fù)雜的代碼越難定位問題罷了。

輸出的結(jié)果是這樣的:

00000001 String get00001041 Object get

這里有兩個(gè)get方法。一個(gè)是代碼里的那個(gè),另外一個(gè)是synthetic和bridge方法。用javap反編譯后會(huì)是這樣的:

public java.lang.String get(int);
  Code:
   Stack=1, Locals=2, Args_size=2
   0:   ldc     #2; //String
   2:   areturn
  LineNumberTable:
   line 12: 0public java.lang.Object get(int);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0   1:   iload_1   2:   invokevirtual   #3; //Method get:(I)Ljava/lang/String;
   5:   areturn

有趣的是,兩個(gè)方法的簽名是一模一樣的,只有返回類型不同。這個(gè)在JVM里面是合法的,不過在Java語言里可不允許。bridge的這個(gè)方法不干別的,就只是去調(diào)用了下原始的那個(gè)方法。

為什么我們需要這個(gè)synthetic方法呢,誰會(huì)調(diào)用它?比如現(xiàn)在有段代碼想要調(diào)用一個(gè)非MyLink類型變量的get(int)方法:

List<?> a = new MyLink();
        Object z = a.get(0);

它不能調(diào)用返回String的方法,因?yàn)長(zhǎng)ist里沒這樣的方法。為了解釋的更清楚一點(diǎn),我們重寫下add方法而不是get方法:

package synthetic;import java.util.LinkedList;import java.util.List;public class SyntheticMethodTest4 {public static class MyLink extends LinkedList {@Overridepublic boolean add(String s) {return true;
        }
    }public static void main(String[] args) {
        List a = new MyLink();
        a.add("");
        a.add(13);
    }
}

我們會(huì)發(fā)現(xiàn)這個(gè)bridge方法

public boolean add(java.lang.Object);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0   1:   aload_1   2:   checkcast       #2; //class java/lang/String
   5:   invokevirtual   #3; //Method add:(Ljava/lang/String;)Z
   8:   ireturn

它不僅調(diào)用了原始的方法,它還進(jìn)行了類型檢查。這個(gè)檢查是在運(yùn)行時(shí)進(jìn)行的,并不是由JVM自己來完成。正如你所想,在18行的地方會(huì)拋出一個(gè)異常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at synthetic.SyntheticMethodTest4$MyLink.add(SyntheticMethodTest4.java:1)
    at synthetic.SyntheticMethodTest4.main(SyntheticMethodTest4.java:18)

到此,相信大家對(duì)“Java中不為人知的特殊方法是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI