摘要:匿名內(nèi)置類的初始化不能依賴于外部類的初始化表達式中作為主類字節(jié)碼的一部分,需要等待主類初始化完成才能開始執(zhí)行總之,在類的初始化階段,不能出現(xiàn)內(nèi)置類匿名和主類初始化中相互依賴的對象
Qestion
/** * ClassInitializedOrder for : Java Classload Order Test * * @author Isaac.Zhang | 若初 * @since 2019/7/20 */ // CASE 1 public class ClassInitializedOrder { private static boolean initialized = false; static { println("static 代碼塊執(zhí)行。"); Thread thread = new Thread(() -> initialized = true); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { println("main 函數(shù)執(zhí)行。"); System.out.println("initialized = " + initialized); } private static void println(Object o){ System.out.println(o); } } ------------------------------------------------------------------- // CASE 2 public class ClassInitializedOrder { private static boolean initialized = false; static { println("static 代碼塊執(zhí)行。"); Thread thread = new Thread(new Runnable() { @Override public void run() { println("Runnable 代碼塊執(zhí)行。"); initialized = true; } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { println("main 函數(shù)執(zhí)行。"); System.out.println("initialized = " + initialized); } private static void println(Object o){ System.out.println(o); }Answer
A: initialized = true
B: initialized = false
C: 編譯錯誤
D: 以上答案都是錯的
Explain程序執(zhí)行的時候,App Classloader 會首先加載ClassInitializedOrder.class, 按照類的順序依次執(zhí)行。
private static boolean initialized = false;
CASE 1我們都知道,static塊會在類加載的時候初始化,那么下一步會執(zhí)行到Thread thread = new Thread(() -> initialized = true);我們先來看一下當(dāng)前行的字節(jié)碼:
static {}; descriptor: ()V flags: ACC_STATIC Code: stack=3, locals=2, args_size=0 0: iconst_0 1: putstatic #7 // Field initialized:Z 4: new #11 // class java/lang/Thread 7: dup 8: invokedynamic #12, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 13: invokespecial #13 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V 16: astore_0 17: aload_0 18: invokevirtual #14 // Method java/lang/Thread.start:()V 21: aload_0 22: invokevirtual #15 // Method java/lang/Thread.join:()V 25: goto 33 28: astore_1 29: aload_1 30: invokevirtual #17 // Method java/lang/InterruptedException.printStackTrace:()V 33: return
分析#12可以看到當(dāng)前行的處理需要()也就是改匿名類本身來處理,InvokeDynamic指令的在當(dāng)前的執(zhí)行又依賴于當(dāng)前所處的主類,主類并沒有執(zhí)行結(jié)束,因此它需要等待主類執(zhí)行結(jié)束,因此會在此停頓,如下:
CASE 2繼續(xù)查看字節(jié)碼:
static {}; descriptor: ()V flags: ACC_STATIC Code: stack=4, locals=2, args_size=0 0: iconst_0 1: putstatic #1 // Field initialized:Z 4: ldc #14 // String static 代碼塊執(zhí)行。 6: invokestatic #2 // Method println:(Ljava/lang/Object;)V 9: new #15 // class java/lang/Thread 12: dup 13: new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1 16: dup 17: invokespecial #17 // Method com/sxzhongf/daily/question/july/ClassInitializedOrder$1."":()V 20: invokespecial #18 // Method java/lang/Thread." ":(Ljava/lang/Runnable;)V 23: astore_0 24: aload_0 25: invokevirtual #19 // Method java/lang/Thread.start:()V 28: aload_0 29: invokevirtual #20 // Method java/lang/Thread.join:()V 32: goto 40 35: astore_1 36: aload_1 37: invokevirtual #22 // Method java/lang/InterruptedException.printStackTrace:()V 40: return
查看#16,我們可以看到這里變成了new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1,可以明顯看到從之前的invokeDynamic 變成了 new 一個匿名類,那么它的結(jié)果呢?
依然還是block.我們來換一行代碼試試?
public class ClassInitializedOrder { private static boolean initialized = false; static { println("static 代碼塊執(zhí)行。"); Thread thread = new Thread(new Runnable() { @Override public void run() { //println("Runnable 代碼塊執(zhí)行。"); System.out.println("Runnable 代碼塊執(zhí)行。"); //initialized = true; } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }
我們看到我們只是修改了一行代碼System.out.println("Runnable 代碼塊執(zhí)行。");,那么結(jié)果呢?
執(zhí)行成功的返回了。為什么?繼續(xù)查看字節(jié)碼
static {}; descriptor: ()V flags: ACC_STATIC Code: stack=4, locals=2, args_size=0 0: iconst_0 1: putstatic #9 // Field initialized:Z 4: ldc #14 // String static 代碼塊執(zhí)行。 6: invokestatic #3 // Method println:(Ljava/lang/Object;)V 9: new #15 // class java/lang/Thread 12: dup 13: new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1 16: dup 17: invokespecial #17 // Method com/sxzhongf/daily/question/july/ClassInitializedOrder$1."":()V 20: invokespecial #18 // Method java/lang/Thread." ":(Ljava/lang/Runnable;)V 23: astore_0 24: aload_0 25: invokevirtual #19 // Method java/lang/Thread.start:()V 28: aload_0 29: invokevirtual #20 // Method java/lang/Thread.join:()V 32: goto 40 35: astore_1 36: aload_1 37: invokevirtual #22 // Method java/lang/InterruptedException.printStackTrace:()V 40: return
查看#16,看到的還是new了一個匿名類,和上一個是一樣的,為什么就可以成功呢?這個在于當(dāng)前匿名類中沒有依賴主類的代碼信息。不存在上下依賴,那么就不會出現(xiàn)相互等待的情況發(fā)生,當(dāng)然也就不會出現(xiàn)block。
那么就有朋友會問,為什么會相互等待呢?這里和我們join就有關(guān)聯(lián)了,我們來看一下它的實現(xiàn)代碼。
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
我們可以看到,首先它是synchronized關(guān)鍵詞修飾的,那就說明它同時只能被一個線程訪問,再往下看,我們能發(fā)現(xiàn),join的具體實現(xiàn),其實就是wait()來實現(xiàn),當(dāng)子線程中的程序再等待main線程的實現(xiàn)類初始化完成的時候,又依賴了主線程中的某些元素對象。那么就會開始等待主線程初始化完成,這個時候,根據(jù)classloader加載類的執(zhí)行順序,在#16就會開始等待,那么主類無法初始化完成,造成相互等待現(xiàn)相。
Result匿名內(nèi)置類的初始化不能依賴于外部類的初始化
lambda表達式中invokeDynamic作為主類字節(jié)碼的一部分,需要等待主類初始化完成才能開始執(zhí)行
總之,在類的初始化階段,不能出現(xiàn)內(nèi)置類(匿名/Lambda)和主類初始化中相互依賴的對象
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75510.html
摘要:外部類也可以直接訪問內(nèi)部類的所有屬性和方法。匿名內(nèi)部類為局部內(nèi)部類,所以局部內(nèi)部類的所有限制同樣對匿名內(nèi)部類生效。創(chuàng)建內(nèi)部類對象的時刻并不依賴于外圍類對象的創(chuàng)建。內(nèi)部類并沒有令人迷惑的關(guān)系,他就是一個獨立的實體。 基本概念 可以將一個類定義在另一個類里面或者一個方法里面,這樣的類稱為內(nèi)部類。 廣泛意義上的內(nèi)部類一般來說包括這四種: 成員內(nèi)部類 局部內(nèi)部類 靜態(tài)內(nèi)部類 匿名內(nèi)部類 成...
摘要:語言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語言執(zhí)行效率低的問題,同時又保留了解釋型語言可移植的特點。有針對不同系統(tǒng)的特定實現(xiàn),,,目的是使用相同的字節(jié)碼,它們都會給出相同的結(jié)果。項目主要基于捐贈的源代碼。 本文來自于我的慕課網(wǎng)手記:Java編程中那些再熟悉不過的知識點,轉(zhuǎn)載請保留鏈接 ;) 1. 面向?qū)ο蠛兔嫦蜻^程的區(qū)別 面向過程 優(yōu)點: 性能比面向?qū)ο蟾摺R驗轭愓{(diào)用時需要實例...
摘要:典型示例以下結(jié)構(gòu)是比較推薦的組織方式,所有的類和其他都在之下。應(yīng)用主類,該類直接位于下。默認情況下,的應(yīng)用主類會自動掃描以及所有子包下的所有類來進行初始化。 Spring Boot框架本身并沒有對工程結(jié)構(gòu)有特別的要求,但是按照最佳實踐的工程結(jié)構(gòu)可以幫助我們減少可能會遇見的坑,尤其是Spring包掃描機制的存在,如果您使用最佳實踐的工程結(jié)構(gòu),可以免去不少特殊的配置工作。 典型示例 以下結(jié)...
閱讀 2614·2021-11-22 15:25
閱讀 1442·2021-11-15 17:59
閱讀 1141·2021-09-29 09:34
閱讀 1551·2021-09-26 09:46
閱讀 3038·2021-09-02 15:40
閱讀 1194·2019-08-30 15:56
閱讀 3288·2019-08-30 15:55
閱讀 701·2019-08-29 17:08