在平時編碼時,咱們可能只注意到static、final、volatile等關鍵字的使用,而忽略了它們更深層次的意義。html
當static修飾類的屬性或者方法時,那麼就能夠在沒有建立對象的狀況下使用該屬性或方法。java
靜態塊也是static的一個應用,用於初始化類時的一些操做。react
被static修飾後的屬性或者方法,使用時不須要new一個類,用類.屬性名或方法名訪問。dom
好比java.lang.Math就存放了不少靜態資源,能夠直接使用Math.random()來獲取隨機數。工具
注:this
一、非靜態方法是能夠直接訪問靜態資源的,靜態方法是不能直接引用非靜態資源的。編碼
二、.靜態屬性和方法能夠經過類.屬性名或方法名,並且,該類的對象也是訪問靜態屬性和變量的。spa
三、Java的語法規定,static不能修飾局部變量。沒有爲何,這就是規定。線程
示例:code
編譯時就會報錯,main方法是靜態方法,變量i是非靜態的。解決方案:一是將變量i加上static修飾,二是如上採用實例訪問非靜態屬性。
爲何非靜態方法能夠訪問靜態資源,而靜態方法不能訪問非靜態資源呢?
從類加載機制上講,靜態資源是類初始化的時候加載的,而後非靜態資源是new一個該類的對象的時候加載的。
這就帶來一個疑問:
加載類時默認先加載靜態資源的,當new一個對象以後,纔會加載其餘資源,因此在new對象以前,靜態資源是不知道類有哪些非靜態資源的,
可是當對象new出來以後,該類的全部屬性和方法都知道。
靜態塊也是在類加載的時候執行,並且只執行一次。
靜態塊注意要點:
示例:
package main.keyword; public class StaticDemoTest2 extends ParentClass{ public static String subStaticField = "子類靜態變量"; public String subField = "子類非靜態變量"; public static StaticClass staticClass = new StaticClass("子類"); static { System.out.println("子類 靜態塊初始化"); } { System.out.println("子類 [非]靜態塊初始化"); } public StaticDemoTest2(){ System.out.println("子類構造器初始化"); } public static void main(String[] args) { new StaticDemoTest2(); } } class ParentClass{ public static String parentStaticField = "父類靜態變量"; public String parentField = "父類[非]驚天變量"; public static StaticClass staticClass = new StaticClass("父類"); static { System.out.println("父類 靜態塊初始化"); } { System.out.println("父類 [非]靜態塊初始化"); } public ParentClass(){ System.out.println("父類 構造器初始化"); } } class StaticClass{ public StaticClass(String name){ System.out.println(name+" 靜態變量加載"); } }
執行結果:
父類 靜態變量加載 父類 靜態塊初始化
子類 靜態變量加載
子類 靜態塊初始化
父類 [非]靜態塊初始化 父類 構造器初始化
子類 [非]靜態塊初始化
子類構造器初始化
下面是總結類加載流程,能夠對照着這個流程,能夠再從新看一下上面的例子,會有新的理解。
1. 加載父類靜態
1.1 爲靜態屬性分配存儲空間並賦初始值
1.2 執行靜態初始化塊和靜態初始化語句(從上至下)
2. 加載子類靜態
2.1 爲靜態屬性分配存儲空間
2.2 執行靜態初始化塊和靜態初始化語句(從上至下)
3. 加載父類非靜態
3.1 爲非靜態塊分配空間
3.2 執行非靜態塊
4. 加載子類非靜態
4.1 爲非靜態塊分配空間
4.2 執行非靜態塊
5. 加載父類構造器
5.1 爲實例屬性分配存數空間並賦初始值
5.2 執行實例初始化塊和實例初始化語句
5.3 執行構造器內容
6. 加載子類構造器
6.1 爲實例屬性分配存數空間並賦初始值
6.2 執行實例初始化塊和實例初始化語句
6.3 執行構造器內容
示例2:
package main.keyword; public class StaticDemoTest3 { Person person = new Person("TestStaticLoad");//4 static{ System.out.println("TestStaticLoad static");//1 } public StaticDemoTest3() { System.out.println("TestStaticLoad constructor");//5 } public static void main(String[] args) { new God(); } } class Person{ static{ System.out.println("person static");//3 } public Person(String str) { System.out.println("person "+str);//4 -TestStaticLoad 6-God } } class God extends StaticDemoTest3 { Person person = new Person("God"); //6 static{ System.out.println("God static");//2 } public God() { System.out.println("God constructor");//7 } }
執行結果:
TestStaticLoad static God static person static person TestStaticLoad TestStaticLoad constructor person God God constructor
一步一步地解析:
static關鍵字總結:
總結:
示例:
public class TestFinal { public static void main(String args[]){ String a = "test1"; final String b = "test"; String d = "test"; String c = b + 1; String e = d + 1; System.out.println((a == c)); System.out.println((a.equals(e))); } }
執行結果:
true true
由於final變量是基本類型以及String時,在編譯期的時候就把它當作常量來使用,不須要在運行時候使用。「==」是對比兩個對象基於內存引用,若是兩個對象的引用徹底相同,則返回true,因此這裏b是用訪問常量的方式去訪問,d是連接的方式,因此a的內存引用和c的內存引用是相等的,因此結果爲true,a和e兩個對象的值是相等的,因此結果爲true。
示例:
在編譯的時候,或報錯, 不能指向一個final對象。
具體定義請看這裏
應用
線程池(ThreadPoolExecutor)中一些變量的定義:
private volatile ThreadFactory threadFactory; private volatile RejectedExecutionHandler handler; private volatile long keepAliveTime; private volatile boolean allowCoreThreadTimeOut; private volatile int corePoolSize; private volatile int maximumPoolSize;
能夠看到線程工廠threadFactory,拒絕策略handler,沒有任務時的活躍時間keepAliveTime,keepAliveTime的開關allowCoreThreadTimeOut,核心池大小corePoolSize,最大線程數maximumPoolSize
都是被volatile修飾中,由於在線程池中有若干個線程,這些變量必需保持對線程可見性,否則會引發線程池運行不正確。
assert斷言
在目前的java編碼中,是不推薦使用的,這裏只是稍微瞭解一下:
使用方式:
public class AssertDemoTest { public static void main(String[] args) { assert true; System.out.println("斷言1成功執行"); System.out.println("-----------"); assert false:"error"; System.out.println("斷言2成功執行"); } }
執行結果:
斷言1成功執行 ----------- 斷言2成功執行
關於鎖關鍵字,有如下幾個總結:
Java中,一個類想要序列化,能夠經過實現Serilizable接口的方式來實現,實現該接口以後,該類全部屬性和方法都會自動序列化。
可是若是屬性或方法被transient修飾,那麼將不會被序列化。