一,將本身注入到一個靜態變量中實現靜態類,以下寫法java
以上方法的目的是要實現一個靜態類,方便用類名獲取對象實例,通常狀況下調用普通方法須要對象實例.這對象要麼new出來,要麼spring的注入以下是spring注入對象,spring
將對象賦值給一個靜態變量後,則能夠用類名先調用該類變量獲取實例,再調用方法.安全
通常狀況下static不能直接在類名前修飾,可是靜態內部類能夠,所以static也用於靜態內部類數據結構
如下代碼是內部類的做用域jvm
/** * 1,內部類通常只爲其外部類使用; * 2,內部類提供了某種進入外部類的窗戶; * 3,每一個內部類都能獨立的繼承一個接口,而不管外部類是否已經繼承了某個接口,所以內部類使得多重繼承得以實現 * @Author jisen * @Date 2019/3/5 14:30 */ public class OutClass extends ClassB { //常量,類變量,普通變量 private static final Object OUTREFERENCECONSTANT = new Object();//外部常量 private static Object outStaticRefField = new Object();//外部類變量 private Object outField = new Object();//外部普通變量 //外部類方法 public void outmethod(){}//外部普通方法 public static void outstaticmethod(){}//外部靜態方法 //靜態內部類可繼承可實現 static class InnerStaticClass extends ClassA implements ClassC{ private static final Object INREFERENCECONSTANT2 = new Object();//靜態內部類常量 private static Object inStaticRefField = new Object();//靜態內部類,類變量 private Object inField = new Object();//靜態內部類普通變量 //靜態內部類的靜態方法只能訪問靜態內外成員 public static void innerStaticClassMethod(){ //outmethod(); //innerClassMethod(); outstaticmethod(); innerStaticClassMethod(); System.out.println(OUTREFERENCECONSTANT); System.out.println(INREFERENCECONSTANT2); System.out.println(outStaticRefField); System.out.println(inStaticRefField); //System.out.println(outField); //System.out.println(inField); } ////靜態內部類的普通方法只能訪問外部靜態成員,以及內部全部成員 public void innerClassMethod(){ //outmethod();//靜態方法不能訪問外部非靜態方法 innerClassMethod(); outstaticmethod(); innerStaticClassMethod(); System.out.println(OUTREFERENCECONSTANT); System.out.println(INREFERENCECONSTANT2); System.out.println(outStaticRefField); //System.out.println(outField); System.out.println(inField); } } //普通內部可繼承可實現 class InnerClass extends ClassA implements ClassC{ private final Object INREFERENCECONSTANT3 = new Object();//能夠定義不可變量 //private static final Object INREFERENCECONSTANT4 = new Object();//沒法定義常量 //private static Object inStaticRefField2 = new Object();//不可定義類變量 private Object inField2 = new Object();//可定義普通變量 //普通內部類只能有普通方法,可訪問外部類的全部成員 public void innerMethod(){ System.out.println(OUTREFERENCECONSTANT); System.out.println(outStaticRefField); System.out.println(outField); } //public static void innerMethod2(){} } }
內部類的使用方法ide
/** * @Desc 內部類的使用 * @Author jisen * @Date 2019/3/5 14:34 */ public class TestStatic { public void method(){ //經過外部類對象所能到達的全部操做 OutClass outClass = new OutClass(); outClass.outmethod(); OutClass.InnerClass innerClass = outClass.new InnerClass(); innerClass.innerMethod(); //經過外部類名所能達到的操做 OutClass.outstaticmethod();//訪問外部公共靜態成員 OutClass.InnerStaticClass.innerStaticClassMethod();//訪問公共靜態成員,再訪問靜態成員內部的靜態成員 //靜態內部類對象,靜態內部類能夠不經過外部實例對象,直接new產生 OutClass.InnerStaticClass innerStaticClass = new OutClass.InnerStaticClass(); innerStaticClass.innerClassMethod();//用靜態內部類實例對象訪問成員 //普通靜態內部類,須要外部類的實例對象 OutClass.InnerClass innerClass1 = new OutClass().new InnerClass(); innerClass1.innerMethod(); } }
二,static用於單例函數
//餓漢式,靜態常量,類加載的時候在準備階段這個常量就分配了內存並賦值, //始終只有一個實例,避免了線程安全問題 public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } } //餓漢式靜態代碼塊,原理跟靜態常量同樣,在類加載的準備階段賦初值null, //初始化階段執行<client>(),實例化對象並賦值給靜態成員 public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return instance; } } //靜態內部類,本質上跟第一種是同樣的,內部類常量,在類加載的準備階段就實例化了. //只有一份避免了線程競爭產生多分 public class Singleton { private Singleton() {} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
類加載的過程:this
1,加載是jvm類加載器經過一個類的全限定名獲取定義此類的二進制字節流,將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構,並在內存中idea
生成一個表明這個類的java.lang.Class對象,做爲方法區這個類的各類數據訪問入口.spa
2,驗證,語法,以及class文件的驗證
3,準備階段,
類變量分配內存地址並設置類變量初始值階段,
類變量分配內存地址並設置類變量初始值階段,
類變量分配內存地址並設置類變量初始值階段
重話三,類變量就是被static修飾的變量,不包含普通實例變量,普通實例變量會在對象實例化的時候隨着對象一塊兒分配在java堆中,好比 public static int value=123;它
在準備階段的初始值是0而不是123,由於準備階段還沒有執行任何java方法,而把value賦值爲123的putstatic指令時程序被編譯後存放於類構造器<client>()方法中,執
行java方法是在初始化階段.各種型的零值,int 0,long 0L,short (short)0,char '\u0000',byte (byte)0,boolean false,float 0.0f,double 0.0d,reference null
若是是public static final Object value=new Object();這個類字段還被final修飾被定義爲ConstantValue,因此常量在準備階段就被賦值new Object()的內存地址
4解析階段,將常量池中的符號引用替換爲直接引用的過程
5,初始化,在準備階段類變量已經賦值過一次系統要求的初值,而初始化階段則將執行類構造器<client>()方法,這個不是類的構造函數,<client>()方法是由編譯器
自動收集類中的全部類變量的賦值動做和靜態語句塊中的語句合併產生的,其順序是由語句在源文件中出現的順序所決定的.因此static修飾的語句塊或者類變量,如
果他們前後對一個值賦值那取決於最後賦值的那個.其次<client>()方法執行以前會默認先執行父類的<client>()的方法,所以出如今父類的static修飾的類變量和靜態
語句塊會先執行.
本文經過注入靜態變量,靜態內部類,單例這三種應用場景來展現了static的使用特色,並詳細描述jvm類加載過程,揭示static的內在邏輯
如今作以下總結
1,static是類級別的,被static修飾的變量叫類變量.能夠不須要對象(this)訪問,而可以經過類名直接訪問
2,被static修飾的變量,常量,靜態代碼塊,靜態方法,靜態類.他們的執行順序以下分析:
a,基本數據類型靜態常量在類加載的準備階段就分配內存並賦值最終值,如int類型常量 OUTREFERENCECONSTANTINT=3
b,引用數據類型常量和類變量在類加載的準備階段會賦初值,引用類型爲null int爲0以下圖所示,並在類加載的初始化階段執行<client>()方法會給它賦代碼
值,idea能進入到debug模式表示類加載的初始化階段調用<client>()方法
c.靜態代碼塊,會在類加載的初始化階段,也就是執行<client>()方法的時候執行,這個時候與引用數據類型常量類變量的賦值動做,根據代碼前後順序執行
d, 非靜態類型的字段在類加載完成而且給對象分配內存的時候初始化.
e,說完了字段靜內部態類,它只有在調用的時候纔會被加載,就像正常類同樣被加載,靜態內部類的常量字段,類變量,初始化與正常類的字段同樣一
f.靜態方法只有在調用它的時候纔會執行
文章首發地址:https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554779&idx=1&sn=1d08bcc184c9f4224af14a02d11059c5&chksm=f3f8330dc48fba1b7f87d1fe8487e9d8753cf0e9b8a9df23d6a1ab6f7215fa8845913431abf1&token=2005887224&lang=zh_CN#rd
公衆號: