日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

不可逆的类初始化过程

發(fā)布時間:2025/3/21 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 不可逆的类初始化过程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

類的加載過程說復雜很復雜,說簡單也簡單,說復雜是因為細節(jié)很多,比如說今天要說的這個,可能很多人都不了解;說簡單,大致都知道類加載有這么幾個階段,loaded->linked->initialized,為了讓大家能更輕松地知道我今天說的這個話題,我不詳細說類加載的整個過程,改天有時間有精力了我將整個類加載的過程和大家好好說說(PS:我對類加載過程慢慢清晰起來得益于當初在支付寶做cloudengine容器開發(fā)的時候,當時引入了標準的osgi,解決類加載的問題幾乎是每天的家常便飯,相信大家如果還在使用OSGI,那估計能體會我當時的那種痛,哈哈)。

本文我想說的是最后一個階段,類的初始化,但是也不細說其中的過程,只圍繞我們今天要說的展開。

我們定義一個類的時候,可能有靜態(tài)變量,可能有靜態(tài)代碼塊,這些邏輯編譯之后會封裝到一個叫做clinit的方法里,比如下面的代碼:

class BadClass{private static int a=100;static{System.out.println("before init");int b=3/0;System.out.println("after init");}public static void doSomething(){System.out.println("do somthing");} }

編譯之后我們通過javap -verbose BadClass可以看到如下字節(jié)碼:

{BadClass();flags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void doSomething();flags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=0, args_size=00: getstatic ? ? #2 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;3: ldc ? ? ? ? ? #3 ? ? ? ? ? ? ? ? ?// String do somthing5: invokevirtual #4 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 10: 0line 11: 8static {};flags: ACC_STATICCode:stack=2, locals=1, args_size=00: bipush ? ? ? ?1002: putstatic ? ? #5 ? ? ? ? ? ? ? ? ?// Field a:I5: getstatic ? ? #2 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;8: ldc ? ? ? ? ? #6 ? ? ? ? ? ? ? ? ?// String before init10: invokevirtual #4 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V13: iconst_314: iconst_015: idiv16: istore_017: getstatic ? ? #2 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;20: ldc ? ? ? ? ? #7 ? ? ? ? ? ? ? ? ?// String after init22: invokevirtual #4 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V25: returnLineNumberTable:line 2: 0line 4: 5line 5: 13line 6: 17line 7: 25 }

我們看到最后那個方法static{},其實就是我上面說的clinit方法,我們看到靜態(tài)字段的初始化和靜態(tài)代碼庫都封裝在這個方法里。

假如我們通過如下代碼來測試上面的類:

public static void main(String args[]){try{BadClass.doSomething();}catch (Throwable e){e.printStackTrace();}BadClass.doSomething();}

大家覺得輸出會是什么?是會打印多次before init嗎?其實不然,輸出結果如下:

before init java.lang.ExceptionInInitializerErrorat ObjectTest.main(ObjectTest.java:7)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:606)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) Caused by: java.lang.ArithmeticException: / by zeroat BadClass.<clinit>(ObjectTest.java:25)... 6 more Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class BadClassat ObjectTest.main(ObjectTest.java:12)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:606)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

也就是說其實是只輸出了一次before init,這是為什么呢?

clinit方法在我們第一次主動使用這個類的時候會觸發(fā)執(zhí)行,比如我們訪問這個類的靜態(tài)方法或者靜態(tài)字段就會觸發(fā)執(zhí)行clinit,但是這個過程是不可逆的,也就是說當我們執(zhí)行一遍之后再也不會執(zhí)行了,如果在執(zhí)行這個方法過程中出現(xiàn)了異常沒有被捕獲,那這個類將永遠不可用,雖然我們上面執(zhí)行BadClass.doSomething()的時候catch住了異常,但是當代碼跑到這里的時候,在jvm里已經(jīng)將這個類打上標記了,說這個類初始化失敗了,下次再初始化的時候就會直接返回并拋出類似的異常java.lang.NoClassDefFoundError: Could not initialize class BadClass,而不去再次執(zhí)行初始化的邏輯,具體可以看下jvm里對類的狀態(tài)定義:

enum ClassState {unparsable_by_gc = 0, ? ? ? ? ? ? ? // object is not yet parsable by gc. Value of _init_state at object allocation.allocated, ? ? ? ? ? ? ? ? ? ? ? ? ?// allocated (but not yet linked)loaded, ? ? ? ? ? ? ? ? ? ? ? ? ? ? // loaded and inserted in class hierarchy (but not linked yet)linked, ? ? ? ? ? ? ? ? ? ? ? ? ? ? // successfully linked/verified (but not initialized yet)being_initialized, ? ? ? ? ? ? ? ? ?// currently running class initializerfully_initialized, ? ? ? ? ? ? ? ? ?// initialized (successfull final state)initialization_error ? ? ? ? ? ? ? ?// error happened during initialization};

如果clinit執(zhí)行失敗了,拋了一個未被捕獲的異常,那將這個類的狀態(tài)設置為initialization_error,并且無法再恢復,因為jvm會認為你這次初始化失敗了,下次肯定也是失敗的,為了防止不斷拋這種異常,所以做了一個緩存處理,不是每次都再去執(zhí)行clinit,因此大家要特別注意,類的初始化過程可千萬不能出錯,出錯就可能只能重啟了哦。

總結

以上是生活随笔為你收集整理的不可逆的类初始化过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。