slf4j 桥接与被桥接_合成和桥接方法
slf4j 橋接與被橋接
如果您曾經(jīng)玩過反射并執(zhí)行了getDeclaredMethods()您可能會(huì)感到驚訝。 您可能會(huì)獲得源代碼中不存在的方法。 或者,也許您看了一些方法的修飾符,發(fā)現(xiàn)其中一些特殊方法是易變的。 順便說(shuō)一句:對(duì)于Java采訪來(lái)說(shuō),這是一個(gè)令人討厭的問題,“當(dāng)方法易變時(shí),這意味著什么?” 正確的答案是方法不能是易變的。 同時(shí),在getDeclaredMethods()甚至getMethods()返回的方法中可能存在某些方法,對(duì)于這些方法, Modifier.isVolatile(method.getModifiers())為true。
這是項(xiàng)目轉(zhuǎn)換器的用戶之一發(fā)生的 。 他意識(shí)到,交換器(本身會(huì)深入挖掘Java的黑暗細(xì)節(jié))生成的Java源代碼無(wú)法使用關(guān)鍵字volatile作為方法的修飾符進(jìn)行編譯。 結(jié)果,它也不起作用。
那里發(fā)生了什么事? 橋接和合成方法是什么?
能見度
創(chuàng)建嵌套或嵌入式類時(shí),可以從頂級(jí)類訪問嵌套類的私有變量和方法。 這由不可變的嵌入式構(gòu)建器模式使用 。 這是語(yǔ)言規(guī)范中定義的Java的明確定義的行為。
JLS7,6.6.1確定可訪問性
…如果成員或構(gòu)造函數(shù)被聲明為私有,則訪問為
當(dāng)且僅當(dāng)它出現(xiàn)在頂級(jí)類的主體中時(shí)才允許(第7.6節(jié))
包含成員或構(gòu)造函數(shù)的聲明…
JVM如何處理它? JVM不知道內(nèi)部或嵌套類。 對(duì)于JVM,所有類都是頂級(jí)外部類。 所有類都被編譯為頂級(jí)類,這就是那些不錯(cuò)的方法...$. .class ...$. .class文件已創(chuàng)建。
$ ls -Fart ../ SyntheticMethodTest2$A.class MyClass.java SyntheticMethodTest4.java SyntheticMethodTest2.java SyntheticMethodTest2.class SyntheticMethodTest3.java ./ MyClassSon.java SyntheticMethodTest1.java如果創(chuàng)建嵌套或內(nèi)部類,它將被編譯為完整的頂級(jí)類。
外層如何提供私有字段? 如果這些人進(jìn)入了真正的頂級(jí)階層并且是私人的,那么他們?nèi)绾螐耐獠侩A層得到呢?
javac解決此問題的方式是,對(duì)于任何私有字段但從頂級(jí)類使用的字段,方法或構(gòu)造函數(shù),它都會(huì)生成綜合方法。 這些合成方法用于到達(dá)原始私有字段/方法/構(gòu)造函數(shù)。 這些方法的生成以巧妙的方式完成:僅生成真正需要并從外部使用的那些方法。
package synthetic;import java.lang.reflect.Constructor; import java.lang.reflect.Method;public 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)方式,因此不能保證對(duì)上述程序的輸出最多的是,在我執(zhí)行該程序的特定平臺(tái)上,它會(huì)產(chǎn)生以下輸出:
2 00001008 access$1 00001008 access$2 00001008 access$3 00000002 x -------------------------- 00000111 void wait 00000011 void wait 00000011 void wait 00000001 boolean equals 00000001 String toString 00000101 int hashCode 00000111 Class getClass 00000111 void notify 00000111 void notifyAll -------------------------- 00000002 synthetic.SyntheticMethodTest2$A 00001000 synthetic.SyntheticMethodTest2$A在上面的程序中,我們?yōu)樽侄蝬賦值,并且還調(diào)用了相同名稱的方法。 這些是觸發(fā)編譯器生成綜合方法所必需的。 您可以看到它生成了三種方法,大概是字段x的setter和getter以及方法x()的綜合方法。 但是,這些綜合方法未在getMethods()返回的下一個(gè)列表中列出,因?yàn)樗鼈兪蔷C合方法,因此不適用于通用調(diào)用。 從這個(gè)意義上講,它們是私有方法。
十六進(jìn)制數(shù)字可以用作解釋器,查看類java.lang.reflect.Modifier定義的常量:
00001008 SYNTHETIC|STATIC 00000002 PRIVATE 00000111 NATIVE|FINAL|PUBLIC 00000011 FINAL|PUBLIC 00000001 PUBLIC 00001000 SYNTHETIC列表中有兩個(gè)構(gòu)造函數(shù)。 有一個(gè)私人的和一個(gè)合成的。 私有存在,因?yàn)槲覀兌x了它。 另一方面,合成的存在是因?yàn)槲覀儚耐獠空{(diào)用了私有的。 到目前為止,橋接方法還沒有。
泛型和繼承
到目前為止,還不錯(cuò),但是我們?nèi)匀粵]有看到任何“易變”的方法。
查看java.lang.reflec.Modifier的源代碼,您可以看到常量0x00000040被定義了兩次。 一次是VOLATILE ,一次是BRIDGE (后者是私有程序包,不用于一般用途)。
要擁有這樣一種方法,一個(gè)非常簡(jiǎn)單的程序就可以做到:
package synthetic;import java.lang.reflect.Method; import java.util.LinkedList;public class SyntheticMethodTest3 {public static class MyLink extends LinkedList<String> {@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è)鏈表,該鏈表的方法get(int)返回String 。 我們不要討論干凈的代碼問題。 這是演示該主題的示例代碼。 干凈的代碼中也會(huì)出現(xiàn)同樣的問題,盡管更復(fù)雜,更難在出現(xiàn)問題時(shí)解決。
輸出顯示:
00000001 String get 00001041 Object get我們有兩個(gè)get()方法。 一個(gè)出現(xiàn)在源代碼中,另一個(gè)出現(xiàn)在源代碼中并且是合成的。 反編譯器javap表示生成的代碼是:
public java.lang.String get(int);Code:Stack=1, Locals=2, Args_size=20: ldc #2; //String2: areturnLineNumberTable:line 12: 0public java.lang.Object get(int);Code:Stack=2, Locals=2, Args_size=20: aload_01: iload_12: invokevirtual #3; //Method get:(I)Ljava/lang/String;5: areturn有趣的是,這兩種方法的簽名是相同的,只是返回類型不同。 JVM允許這樣做,即使Java語(yǔ)言無(wú)法做到這一點(diǎn)。 bridge方法不執(zhí)行其他任何操作,而是調(diào)用原始方法。
為什么需要這種合成方法? 誰(shuí)來(lái)使用它。 例如,想要使用類型MyLink的變量來(lái)調(diào)用方法get(int)的MyLink :
List<?> a = new MyLink();Object z = a.get(0);它不能調(diào)用返回String的方法,因?yàn)長(zhǎng)ist沒有這樣的方法。 為了使其更具說(shuō)明性,讓我們重寫方法add()而不是get() :
package synthetic;import java.util.LinkedList; import java.util.List;public class SyntheticMethodTest4 {public static class MyLink extends LinkedList<String> {@Overridepublic boolean add(String s) {return true;}}public static void main(String[] args) {List a = new MyLink();a.add("");a.add(13);} }我們可以看到橋接方法
public boolean add(java.lang.Object);Code:Stack=2, Locals=2, Args_size=20: aload_01: aload_12: checkcast #2; //class java/lang/String5: invokevirtual #3; //Method add:(Ljava/lang/String;)Z8: ireturn不僅叫原版。 它還檢查類型轉(zhuǎn)換是否正確。 這是在運(yùn)行時(shí)完成的,而不是由JVM本身完成的。 如您所料,它確實(shí)出現(xiàn)在第18行中:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat synthetic.SyntheticMethodTest4$MyLink.add(SyntheticMethodTest4.java:1)at synthetic.SyntheticMethodTest4.main(SyntheticMethodTest4.java:18) 下次在面試中遇到關(guān)于不穩(wěn)定方法的問題時(shí),您可能比面試官了解的更多。
翻譯自: https://www.javacodegeeks.com/2014/03/synthetic-and-bridge-methods.html
slf4j 橋接與被橋接
總結(jié)
以上是生活随笔為你收集整理的slf4j 桥接与被桥接_合成和桥接方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 短信转发设置(短信转发设置怎么设置)
- 下一篇: 使用Apache POI插入内容