今天主要學習下Java語言中的static關鍵字。
static關鍵字的意義及使用場景
static是Java50個關鍵字之一。 static關鍵字可以用來修飾程式碼區塊表示靜態程式碼區塊,修飾成員變數表示全域靜態成員變量,修飾方法表示靜態方法。 (注意:不能修飾普通類,除了內部類,這是為什麼?)
class A { static { System.out.println("A : 静态代码块"); } static int i ; // 静态变量 static void method() { System.out.println("A: 静态方法"); } }
簡而言之,被static關鍵字修飾的內容都是靜態的。
靜態是相對於動態的,動態是指Java程式在JVM上執行時,JVM會根據程式的需要動態建立物件並儲存物件(分配記憶體),物件使命結束後,物件會被垃圾回收器銷毀,即記憶體回收由JVM統一管理並分配給其他新建立的物件;靜態是指Java程式還沒運作時,JVM就會為載入的類別分配空間儲存被static關鍵字修飾的內容;如靜態成員變量,Java類別載入到JVM中,JVM會把類別以及類別的靜態成員變數儲存在方法區,我們知道方法區是執行緒共享且很少發生GC的區域,所以被static關鍵字修飾的內容都是全域共享的,且只會為其分配一次儲存空間。
所以當類別的某些內容不屬於對象,而由物件共用即屬於類別的時候,就可以考慮是否要用static關鍵字修飾。
static關鍵字的作用
1 修飾程式碼區塊
類別中用static關鍵字修飾的程式碼區塊稱為靜態程式碼,反之沒有用static關鍵字修飾的程式碼區塊稱為實例程式碼區塊。
實例程式碼區塊會隨著物件的創建而執行,也就是每個物件都會有自己的實例程式碼區塊,表現出來就是實例程式碼區塊的運行結果會影響目前物件的內容,並且隨著物件的銷毀而消失(記憶體回收);而靜態程式碼區塊是當Java類別載入到JVM記憶體中而執行的程式碼區塊,由於類別的載入在JVM運作期間只會發生一次,所以靜態程式碼區塊也只會執行一次。
因為靜態程式碼區塊的主要作用是用來進行一些複雜的初始化工作,所以靜態程式碼區塊跟隨類別儲存在方法區的表現形式是靜態程式碼區塊執行的結果儲存在方法區,也就是初始化量儲存在方法區並被執行緒共用。
2 修飾成員變數
類別中用static關鍵字修飾的成員變數稱為靜態成員變量,因為static無法修飾局部變數(為什麼?),因此靜態成員變數也能稱為靜態變數。靜態變數跟程式碼區塊類似,在類別載入到JVM記憶體中,JVM會把靜態變數放入方法區並分配內存,也由執行緒共享。存取形式是:類別名稱.靜態成員名。
public class StaticTest { public static void main(String[] args) { System.out.println(D.i); System.out.println(new D().i); } } class D { static { i = 2; System.out.println("D : 静态代码块1"); } static int i; }
靜態變數儲存在類別的資訊中,且可以在線程間共享,那麼它當然也屬於該類別的每個對象,因此可以透過對象存取靜態變量,但編譯器並不支援這麼做,且會給予警告。
注意:
一個類別的靜態變數和該類別的靜態程式碼區塊的載入順序。類別會優先載入靜態變量,然後載入靜態程式碼區塊,但有多個靜態變數和多個程式碼區塊時,會按照編寫的順序進行載入。
class D { static { i = 2; System.out.println("D : 静态代码块1"); } static { i = 6; System.out.println("D : 静态代码块2"); } static int i; }
可以想一下運行的結果。
靜態變數可以不用明確的初始化,JVM會預設給其對應的預設值。如基本資料型別的byte為0,short為0,char為\u0000,int為0,long為0L,float為0.0f,double為0.0d,boolean為false,引用型別為null。
靜態變數既然是JVM記憶體中共享的且可以改變,那麼對它的存取會引起線程安全問題(線程A改寫的同時,線程B取得它的值,那麼獲取的是修改前的值還是修改後的值呢?),所以使用靜態變數的同時要考慮多執行緒情況。如果能確保靜態變數不可變,那麼可以用final關鍵字一起使用避免線程安全問題;否則需要採用同步的方式避免線程安全問題,例如與volatile關鍵字一起使用等。
static關鍵無法修飾局部變量,包括實例方法和靜態方法,不然就會與static關鍵字的初衷-共享相違背。
3 修飾方法
用static關鍵字修飾的方法稱為靜態方法,否則稱為實例方法。透過類別名稱.方法名稱調用,但需要注意靜態方法可以直接調用類別的靜態變數和其他靜態方法,不能直接調用成員變數和實例方法(除非透過物件調用)。
class D { static { i = 2; System.out.println("D : 静态代码块"); } static final int i; int j; static void method() { System.out.println(i); System.out.println(new D().j); method1(); new D().method2(); } static void method1() { System.out.println(i); } void method2() { System.out.println(i); } }
注意:既然類別的實例方法需要物件呼叫才能訪問,而靜態方法直接透過類別名稱就能訪問,那麼在不考慮部署伺服器的情況下,一個類別是如何開始執行的呢?最大的可能就是透過「類別名稱.靜態方法」啟動Java,而我定義那麼多靜態方法,JVM又是如何知道主入口呢?
或許,你想到了main方法。
沒錯,就是main方法被Java規格定義成Java類別的主入口。 Java類別的運行都由main方法開啟:
public static void main(String[] args) { for (String arg : args) { // 参数由外部定义 System.out.println(arg); }}
但注意main并不是Java关键字,它只是一个规定的程序入口的方法名字;另外main方法可以重载。
注意:static关键字虽然不能修饰普通类,但可以用static关键字修饰内部类使其变成静态内部类。static关键字本身的含义就是共享,而Java类加载到JVM内存的方法区,也是线程共享的,所以没必要用static关键字修饰普通类。
4 静态导入
在用import导入包或者类时,可以用static修饰包名或者类,表示静态导入。静态导入可以与动态导入放在一起比较来加深理解。
动态导入是当你程序运行时需要new一个不在此包中的类的对象时,才会根据全路径类名加载类;而静态导入则是随着类的加载而加载静态导入的类,所以它是提前导入的。
public class StaticTest { static void method1() { System.out.println("static method1"); } static void method2() { System.out.println("static method2"); } }
静态导入:
import static com.starry.staticImport.StaticTest.method1; public class Client { public static void main(String[] args) { method1(); // StaticTest.method2(); } }
注意method1()是静态导入,所以可以不需要通过类名访问;而method2()没有导入,则需要通过类名调用。那么什么时候需要静态导入呢?
静态导入常用于静态方法以及含有静态方法的类,枚举类等的导入,可以在编译阶段确定导入类的信息或者方法信息。
static关键字的缺点
封装是Java类的三大特性之一,也是面向对象的主要特性。因为不需要通过对象,而直接通过类就能访问类的属性和方法,这有点破坏类的封装性;所以除了Utils类,代码中应该尽量少用static关键字修饰变量和方法
以上是static關鍵字有什麼作用的詳細內容。更多資訊請關注PHP中文網其他相關文章!