摘要:沒有關鍵字修飾的如實例變量非靜態變量非靜態代碼塊初始化實際上是會被提取到類的構造器中被執行的,但是會比類構造器中的代碼塊優先執行到,非靜態實例變量非靜態代碼塊的地位是相等的,它們將按順序被執行。
閱讀原文:Java代碼執行順序
程序中代碼執行的順序非常重要,稍有不慎便會是程序運行出錯,那么我將結合實例來分析代碼中的執行。
名詞解釋首先了解幾個名詞:
非靜態代碼塊直接由 { } 包起來的代碼,稱為非靜態代碼塊靜態代碼塊
直接由 static { } 包起來的代碼,稱為靜態代碼塊形參
比如你定義一個函數void add(int a, int b),這里的a和b就是形參。
當你進行函數調用的時候,add(1, 2),這里的1和2就是實參。
所謂向前引用,就是在定義類、接口、方法、變量之前使用它們。
成員變量在類體里面定義的變量稱為成員變量; 如果該成員變量有 static 關鍵字修飾,則該成員變量稱為 靜態變量 或 類變量; 如果該成員變量沒有 static 關鍵字修飾,則該成員變量被稱為 非靜態變量 或 實例變量。局部變量
形參、方法內定義的變量、代碼塊中定義的變量,都屬于局部變量。類變量 (靜態變量)
可以向前引用 變量屬于類本身 類變量不依賴類的實例,類變量只在初始化時候在方法區中被分配一次空間,無論類的實例被創建幾次,都不再為類變量分配空間 通過類的任意一個實例來訪問類變量,底層都將將其轉為通過類本身來訪問類變量,它們的效果是一樣的 一旦類變量的值被改變,通過類或類的任意一個實例來訪問類變量,得到的都將是被改變后的值 將在類的初始化之前初始化實例變量(非靜態變量)
不能向前引用,如果向前引用,則稱為非法向前引用,這是不允許的 變量屬于類的實例對象 隨著類的實例被創建而分配內存空間實例演示
public class Parent { public int parentNum=0; public static int staticParentNum=0; { System.out.println("Parent---執行非靜態代碼塊了1!"); } { System.out.println("Parent---執行非靜態代碼塊了2!"); } static{ System.out.println("Parent---執行靜態代碼塊了1!"); } static{ System.out.println("Parent---執行靜態代碼塊了2!"); } public Parent(){ System.out.println("Parent---無參構造函數!"); } public Parent(int parentNum){ this.parentNum=parentNum; System.out.println("Parent---有參構造函數!"); } public void ParentMethod(int parentNum){ this.parentNum=parentNum; System.out.println("Parent---非靜態方法/parentNum="+parentNum); } public static void staticParentMethod(int staticParentNum){ Parent.staticParentNum=staticParentNum; System.out.println("Parent---靜態方法/staticParentNum="+staticParentNum); } }
public class Child extends Parent{ public int childNum=0; public static int staticChildNum=0; { System.out.println("Child---執行非靜態代碼塊了1!"); } { System.out.println("Child---執行非靜態代碼塊了2!"); } static{ System.out.println("Child---執行靜態代碼塊了1!"); } static{ System.out.println("Child---執行靜態代碼塊了2!"); } public Child(){ super(); System.out.println("Child---無參構造函數!"); } public Child(int childNum){ super(childNum); System.out.println("Child---有參構造函數!"); } public void childMethod(int childNum){ this.childNum=childNum; System.out.println("Child--非靜態方法/childNum="+childNum); } public static void staticChildMethod(int staticChildNum){ Child.staticChildNum=staticChildNum; System.out.println("Child---靜態方法/staticChildNum="+staticChildNum); } }
package test; public class Test { // static{ // System.out.println("Test---靜態代碼塊!"); // } public static void main(String[] args) { int key=10; switch (key) { case 0: Parent parent=new Parent(); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---無參構造函數! // 說明:先加載靜態代碼塊,后加載非靜態代碼塊 case 1: Child b= new Child(); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---無參構造函數! // Child---執行非靜態代碼塊了1! // Child---執行非靜態代碼塊了2! // Child---無參構造函數! // 說明:創建子類,會先執行父類,先執行父類靜態——>子類靜態——>父類非靜態——>父類構造 //——>子類非靜態——>子類構造 case 2: Child c= new Child(4); //這個構造函數中指明了調用父類的有參構造函數,若不指定,則調用父類無參構造函數 break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---有參構造函數! // Child---執行非靜態代碼塊了1! // Child---執行非靜態代碼塊了2! // Child---有參構造函數! 說明:靜態代碼塊或非靜態代碼塊執行順序,按照代碼前后編寫順序。 case 3: Child d= new Child(); Child e= new Child(4); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---無參構造函數! // Child---執行非靜態代碼塊了1! // Child---執行非靜態代碼塊了2! // Child---無參構造函數! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---有參構造函數! // Child---執行非靜態代碼塊了1! // Child---執行非靜態代碼塊了2! // Child---有參構造函數! 說明:創建多個子類,但父類靜態代碼塊只執行一次。 case 4: Child.staticChildMethod(4); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // Child---靜態方法/staticChildNum=4 說明:靜態方法只可以調用靜態變量。 case 5: Parent.staticParentMethod(5); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Parent---靜態方法/staticParentNum=5 說明:靜態方法可通過 父類名.靜態方法() 調用。 case 6: System.out.println("父類的靜態變量值staticParentNum="+Parent.staticParentNum); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // 父類的靜態變量值staticParentNum=0 說明:調用靜態變量時,靜態代碼塊會執行。 case 7: System.out.println("子類的靜態變量值staticChildNum="+Child.staticChildNum); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // 子類的靜態變量值staticChildNum=0 說明:調用子類靜態變量,父類靜態代碼塊和子類靜態代碼塊會被執行。 case 8: System.out.println("父類的靜態變量值staticParentNum="+Parent.staticParentNum); System.out.println("子類的靜態變量值staticChildNum="+Child.staticChildNum); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // 父類的靜態變量值staticParentNum=0 // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // 子類的靜態變量值staticChildNum=0 case 9: Child f= new Child(); f.ParentMethod(3); break; // Parent---執行靜態代碼塊了1! // Parent---執行靜態代碼塊了2! // Child---執行靜態代碼塊了1! // Child---執行靜態代碼塊了2! // Parent---執行非靜態代碼塊了1! // Parent---執行非靜態代碼塊了2! // Parent---無參構造函數! // Child---執行非靜態代碼塊了1! // Child---執行非靜態代碼塊了2! // Child---無參構造函數! // Parent---非靜態方法/parentNum=3 說明:創建子類,用子類調用父類方法,非靜態方法可以調用靜態變量。 default: break; } } }總結
Java代碼初始化順序
由 static 關鍵字修飾的(如:類變量(靜態變量)、靜態代碼塊)將在類被初始化創建實例對象之前被初始化,而且是按順序從上到下依次被執行。靜態(類變量、靜態代碼塊)屬于類本身,不依賴于類的實例。
沒有 static 關鍵字修飾的(如:實例變量(非靜態變量)、非靜態代碼塊)初始化實際上是會被提取到類的構造器中被執行的,但是會比類構造器中的代碼塊優先執行到,非靜態(實例變量、非靜態代碼塊)的地位是相等的,它們將按順序被執行。
類變量(靜態變量)、實例變量(非靜態變量)、靜態代碼塊、非靜態代碼塊的初始化時機
由 static 關鍵字修飾的(如:類變量[靜態變量]、靜態代碼塊)將在類被初始化創建實例對象之前被初始化,而且是按順序從上到下依次被執行;
沒有 static 關鍵字修飾的(如:實例變量[非靜態變量]、非靜態代碼塊)初始化實際上是會被提取到類的構造器中被執行的,但是會比類構造器中的 代碼塊優先執行到,其也是按順序從上到下依次被執行。
容易混淆的一個知識點
靜態方法只允許直接訪問靜態成員,而實例方法中可以訪問靜態成員和實例成員,原因是類還沒有實例化,所實例成員也沒有被創建,靜態方法中因此也不能用this。
歡迎關注公眾號交流!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73700.html
摘要:并發編程的挑戰并發編程的目的是為了讓程序運行的更快,但是,并不是啟動更多的線程就能讓程序最大限度的并發執行。的實現原理與應用在多線程并發編程中一直是元老級角色,很多人都會稱呼它為重量級鎖。 并發編程的挑戰 并發編程的目的是為了讓程序運行的更快,但是,并不是啟動更多的線程就能讓程序最大限度的并發執行。如果希望通過多線程執行任務讓程序運行的更快,會面臨非常多的挑戰:(1)上下文切換(2)死...
摘要:下面是該程序在兩個內存模型中的執行時序對比圖在順序一致性模型中,所有操作完全按程序的順序串行執行。不保證未同步程序的執行結果與該程序在順序一致性模型中的執行結果一致。 前情提要 深入理解Java內存模型(二)——重排序 數據競爭與順序一致性保證 當程序未正確同步時,就會存在數據競爭。java內存模型規范對數據競爭的定義如下: 在一個線程中寫一個變量, 在另一個線程讀同一個變量,...
摘要:前提深入理解內存模型程曉明著,該書在以前看過一遍,現在學的東西越多,感覺那塊越重要,于是又再細看一遍,于是便有了下面的讀書筆記總結。同步同步是指程序用于控制不同線程之間操作發生相對順序的機制。線程之間的通信由內存模型控制。 showImg(https://segmentfault.com/img/remote/1460000013474312?w=1920&h=1271); 前提 《深...
摘要:前提深入理解內存模型程曉明著,該書在以前看過一遍,現在學的東西越多,感覺那塊越重要,于是又再細看一遍,于是便有了下面的讀書筆記總結。同步同步是指程序用于控制不同線程之間操作發生相對順序的機制。線程之間的通信由內存模型控制。 showImg(https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6RtPu3BNx3zps1JhSmPICRw7QgeOmxOfTb...
摘要:并發編程關鍵字解析解析概覽內存模型的相關概念并發編程中的三個概念內存模型深入剖析關鍵字使用關鍵字的場景內存模型的相關概念緩存一致性問題。事實上,這個規則是用來保證程序在單線程中執行結果的正確性,但無法保證程序在多線程中執行的正確性。 Java并發編程:volatile關鍵字解析 1、解析概覽 內存模型的相關概念 并發編程中的三個概念 Java內存模型 深入剖析volatile關鍵字 ...
摘要:前情提要深入理解內存模型一基礎編譯器運行時會對指令進行重排序。以處理器的猜測執行為例,執行線程的處理器可以提前讀取并計算,然后把計算結果臨時保存到一個名為重排序緩沖的硬件緩存中。請看下篇深入理解內存模型三順序一致性 前情提要 深入理解Java內存模型(一)——基礎 Java編譯器、運行時會對指令進行重排序。這種重排序在單線程和多線程情況下分別有什么影響呢? 數據依賴性 如果兩個操...
閱讀 951·2021-09-27 13:36
閱讀 907·2021-09-08 09:35
閱讀 1077·2021-08-12 13:25
閱讀 1447·2019-08-29 16:52
閱讀 2918·2019-08-29 15:12
閱讀 2737·2019-08-29 14:17
閱讀 2625·2019-08-26 13:57
閱讀 1022·2019-08-26 13:51