1. 程式人生 > >java執行順序之深入理解clinit和init

java執行順序之深入理解clinit和init

前言:   

     最近研究了深入理解JVM這本書中的知識,對java中各部分執行的順序有了比較深入的瞭解。首先我們得了解一下java中init和clinit的區別。

概念:

    型別初始化方法<clinit>:JVM通過Classload進行型別載入時,如果在載入時需要進行型別初始化操作時,則會呼叫型別的初始化方法。型別初始化方法主要是對static變數進行初始化操作,對static域和static程式碼塊初始化的邏輯全部封裝在<clinit>方法中。

    java.lang.Class.forName(String name, boolean initialize,ClassLoader loader),其中第二個引數就是是否需要初始化。

    Java型別初始化過程中對static變數的初始化操作依賴於static域和static程式碼塊的前後關係,static域與static程式碼塊宣告的位置關係會導致java編譯器生成<clinit>方法位元組碼。型別的初始化方法<clinit>只在該型別被載入時才執行,且只執行一次。
    物件例項化方法<init>:Java物件在被建立時,會進行例項化操作。該部分操作封裝在<init>方法中,並且子類的<init>方法中會首先對父類<init>方法的呼叫。Java物件例項化過程中對例項域的初始化賦值操作全部在<init>方法中進行,<init>方法顯式的呼叫父類的<init>方法,例項域的宣告以及例項初始化語句塊同樣的位置關係會影響編譯器生成的<init>方法的位元組碼順序,<init>方法以構造方法作為結束。

下面引用自:https://blog.csdn.net/u013309870/article/details/72975536

init和clinit區別:

    
①init和clinit方法執行時機不同

    init是物件構造器方法,也就是說在程式執行 new 一個物件呼叫該物件類的 constructor 方法時才會執行init方法,而clinit是類構造器方法,也就是在jvm進行類載入—–驗證—-解析—–初始化,中的初始化階段jvm會呼叫clinit方法。

②init和clinit方法執行目的不同

init is the (or one of the) constructor(s) for the instance, and non-static field initialization. 
clinit are the static initialization blocks for the class, and static field initialization. 
上面這兩句是Stack Overflow上的解析,很清楚init是instance例項構造器,對非靜態變數解析初始化,而clinit是class類構造器對靜態變數,靜態程式碼塊進行初始化。看看下面的這段程式就很清楚了。

class X {
 
   static Log log = LogFactory.getLog(); // <clinit>
 
   private int x = 1;   // <init>
 
   X(){
      // <init>
   }
 
   static {
      // <clinit>
   }
 
}
clinit詳解

    在準備階段,變數已經賦過一次系統要求的初始值,而在初始化階段,則根據程式設計師通過程式制定的主觀計劃去初始化類變數和其他資源,或者可以從另外一個角度來表達:初始化階段是執行類構造器<clinit>()方法的過程。

    ①<clinit>()方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊(static{}塊)中的語句合併產生的,編譯器收集的順序是由語句在原始檔中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變數,定義在它之後的變數,在前面的靜態語句塊可以賦值,但是不能訪問如下程式碼

public class Test{
static{
i=0;//給變數賦值可以正常編譯通過
System.out.print(i);//這句編譯器會提示"非法向前引用"
}
static int i=1;
}
    ②虛擬機器會保證在子類的<clinit>()方法執行之前,父類的<clinit>()方法已經執行完畢。 因此在虛擬機器中第一個被執行的<clinit>()方法的類肯定是java.lang.Object。由於父類的<clinit>()方法先執行,也就意味著父類中定義的靜態語句塊要優先於子類的變數賦值操作,如下程式碼中,欄位B的值將會是2而不是1。

static class Parent{
    public static int A=1;
    static{
    A=2;}
    static class Sub extends Parent{
    public static int B=A;
    }
    public static void main(String[]args){
    System.out.println(Sub.B);
    }
}
    ③介面中不能使用靜態語句塊,但仍然有變數初始化的賦值操作,因此介面與類一樣都會生成<clinit>()方法。 但介面與類不同的是,執行介面的<clinit>()方法不需要先執行父介面的<clinit>()方法。 只有當父介面中定義的變數使用時,父接口才會初始化。 另外,介面的實現類在初始化時也一樣不會執行介面的<clinit>()方法。 
注意:介面中的屬性都是static final型別的常量,因此在準備階段就已經初始化。
--------------------- 
作者:MrBoringBigFish 
來源:CSDN 
原文:https://blog.csdn.net/qq_36522306/article/details/80582758 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

相關推薦

java執行順序深入理解clinitinit

前言:         最近研究了深入理解JVM這本書中的知識,對java中各部分執行的順序有了比較深入的瞭解。首先我們得了解一下java中init和clinit的區別。 概念:     型別初始化方法<clinit>:JVM通過Classload進行型別載

Java執行深入解析ThreadLocalThreadLocalMap

ThreadLocal概述 ThreadLocal是執行緒變數,ThreadLocal中填充的變數屬於當前執行緒,該變數對其他執行緒而言是隔離的。ThreadLocal為變數在每個執行緒中都建立了一個副本,那麼每個執行緒可以訪問自己內部的副本變數。 它具有3個特性: 執行緒併發:在多執行緒併發場景下使用。

小師妹學JVM:深入理解JIT編譯優化-你看不懂系列

[toc] # 簡介 小師妹已經學完JVM的簡單部分了,接下來要進入的是JVM中比較晦澀難懂的概念,這些概念是那麼的枯燥乏味,甚至還有點惹人討厭,但是要想深入理解JVM,這些概念是必須的,我將會盡量嘗試用簡單的例子來解釋它們,但一定會有人看不懂,沒關係,這個系列本不是給所有人看的。 更多精彩內容且看:

Java執行緒小結 深入理解JVM—JVM記憶體模型 Java Integer(-128~127)值的==equals比較產生的思考

  相關資料 -------------------------------------------------------------------------------------  Java多執行緒demo https://github.com/Beerkay/JavaMul

Android開發深入理解泛型extendssuper的區別

我想 lis dataset 文檔 cnblogs extend 擦除 選擇 提前 摘要: 什麽是泛型?什麽是擦除邊界?什麽是上界限定或下界限定(子類型限定或超類型限定)?什麽是類型安全?泛型extends關和super關鍵字結合通配符?使用的區別,兩種泛型在實際Andro

Java多線程深入理解synchronize關鍵字

tracking 而不是 方法 獲得 content cal art track () synchronize鎖重入: 關鍵字synchronize擁有鎖重入的功能,也就是在使用synchronize時,當一個線程的得到了一個對象的鎖後,再次請求此對象是可以再次得到

Java執行semaphoreExchanger

Semaphore是Java執行緒的一個計數訊號量。我們可用於多執行緒的併發訪問控制。 就像我們常見的執行緒池,資料庫連線池就可以使用Semaphore進行邏輯的實現。Semaphore中我們就介紹兩個最常用的兩個方法。 acquire() 從Semaphore獲取許可,如果計數不小於0

讀書筆記深入理解Java虛擬機器

深入理解Java虛擬機器 2.2 執行時資料區域 2.2.1.程式計數器 可以看做是當前執行緒所執行的位元組碼的 行號指示器。 每個執行緒都需要一個 獨立的程式計數器。(這類記憶體區域為"執行緒私有"的記憶體

Java 乾貨深入理解String

可以證明,字串操作是計算機程式設計中最常見的行為,尤其是在Java大展拳腳的Web系統中更是如此。 ---《Thinking in Java》 提到Java中的String,總是有說不完的知識點,它對於剛接觸Java的人來說,有太多太多的值得研究的東西了,可是為什麼Java中的String這麼獨特呢?今天我

Java 乾貨深入理解Java內部類

可以將一個類定義在另一個類或方法中,這樣的類叫做內部類 --《Thinking in Java》 說起內部類,大家並不陌生,並且會經常在例項化容器的時候使用到它。但是內部類的具體細節語法,原理以及實現是什麼樣的可以不少人都還挺陌生,這裡作一篇總結,希望通過這篇總結提高對內部類的認識。 內部類是什麼? 由文章

Java執行緒池的理解認識

什麼是程式,什麼是程序,什麼是執行緒,他們有什麼區別? 程式是指令和資料的有序集合,其本身並沒有任何執行的含義,是一個靜態的概念。 程序是一個動態的過程,是一個活動的實體。簡單來說,一個應用程式得到執行就可以看作是一個程序。程序可以包含多個同時執行的執行緒 執行緒是

Java 乾貨深入理解Java泛型

一般的類和方法,只能使用具體的型別,要麼是基本型別,要麼是自定義的類。如果要編寫可以應用多中型別的程式碼,這種刻板的限制對程式碼得束縛會就會很大。 ---《Thinking in Java》 泛型大家都接觸的不少,但是由於Java 歷史的原因,Java 中的泛型一直被稱為偽泛型,因此對Java中的泛型,有

Java基礎深入理解Class物件與反射機制

深入理解Class物件 RRIT及Class物件的概念 RRIT(Run-Time Type Identification)執行時型別識別。在《Thinking in Java》一書第十四章中有提到,它的功能是在執行時識別物件的型別和類資訊。有兩種主要方式:“傳統的”RTTI(它假定我們在編譯時

Java執行Condition實現原理原始碼分析(四)

章節概覽、 1、概述 上面的幾個章節我們基於lock(),unlock()方法為入口,深入分析了獨佔鎖的獲取和釋放。這個章節我們在此基礎上,進一步分析AQS是如何實現await,signal功能。其功能上和synchronize的wait,notify一樣。

Java併發程式設計深入執行緒池原理及實現

Java執行緒池在實際的應用開發中十分廣泛。雖然Java1.5之後在JUC包中提供了內建執行緒池可以拿來就用,但是這之前仍有許多老的應用和系統是需要程式設計師自己開發的。因此,基於執行緒池的需求背景、技術要求瞭解執行緒池原理和實現,一方面可以更為深刻理解Java多執行緒開發,有助於解決業務系統中因為執行緒問題

[Django高階]理解django中的中介軟體機制執行順序 [Django高階]理解django中的中介軟體機制執行順序

[Django高階]理解django中的中介軟體機制和執行順序 原文來自 Understanding Django Middlewares, 這篇文章從整體上介紹了

二、JAVA執行緒:深入理解Thread建構函式(Thread、Runnable、守護執行緒、ThreadGroup)

本章主要介紹了所有與Thread有關的建構函式,執行緒的父子關係(並非繼承關係,而是一種包含關係),Thread和ThreadGroup之間的關係,Thread與虛擬機器棧的關係(學習這部分內容需要讀者有JVM的相關基礎,尤其是對棧記憶體要有深入的理解),最後還介紹了守護執行緒的概念、特點和使用場景

Java執行生產者消費者

Java執行緒的作用是可以使一個程式中的執行緒可以並行執行,這樣可以大大縮短程式執行所需要的時間。但是當這些執行緒都對同一個變數或者記憶體進行操作的時候如果不加以控制就會出現許多不可預見的錯誤,而且在不同時間執行也會產生不同的錯誤,並且很難排查。 對於生產者和消

Java執行記憶體可見性原子性:SynchronizedVolatile的比較

在刷題時,碰到一題:關於volatile關鍵字的說法錯誤的是: A. 能保證執行緒安全 B volatile關鍵字用在多執行緒同步中,可保證讀取的可見性  C JVM保證從主記憶體載入到執行緒工做記憶體的值是最新的 D volatile能禁止指令進行指令重排序 答案:A 處

Java基礎深入理解介面(interface)意義

在學習介面的時候,一致沒有理解透徹,再次學習時,發現要學習介面,必須要理解其運用場景。理解面向介面程式設計對理解介面非常有幫助。 首先面向介面程式設計和麵向物件程式設計並不是平級的,它並不是比面向物件程式設計更先進的一種獨立的程式設計思想,而是附屬於面向物件思想體系,屬於其