1. 程式人生 > 程式設計 >詳解java中的static關鍵字

詳解java中的static關鍵字

Java中的static關鍵字可以用於修飾變數、方法、程式碼塊和類,還可以與import關鍵字聯合使用,使用的方式不同賦予了static關鍵字不同的作用,且在開發中使用廣泛,這裡做一下深入瞭解。

靜態資源(靜態變數與靜態方法)

被static關鍵字修飾的變數和方法統一屬於類的靜態資源,是類例項之間共享的。被static關鍵字修飾的變數、方法屬於類變數、類方法,可以通過【類名.變數名】、【類名.方法名】直接引用,而不需要派生一個類例項出來。

靜態資源分類存放的好處

JDK把不同的靜態資源放在了不同的類中而不是把所有的靜態資源放在一個類裡面,這樣做主要有3點好處:

1.不同的類有自己的靜態資源,就可以實現靜態資源分類。比如,和數學相關的靜態資源就放在了java.lang.Math中,和日曆相關的靜態資源就放在java.util.Calendar中,將組織形式固定為【類>靜態資源】,使得程式碼的邏輯結構變得清晰。

2.因為靜態資源的組織形式固定為了【類>靜態資源】的形式,也就有效避免的靜態資源在全域性重名的問題。比如在A類中有一個name屬性,B類中也有一個name屬性,如果放在一起會重複,但是分類放開則不會重複了,因為實際上這兩個屬性的全名是A.name和B.name。

3.分類有助於避免因為靜態資源都放在一個類中導致該類體積過大的問題,方便了管理與協同維護。

靜態資源容易混淆的三個點

靜態資源的知識點比較簡單,但是還是有三點比較容易混淆:靜態方法能不能引用非靜態資源?靜態方法能不能引用靜態資源?非靜態方法能不能引用靜態資源?要弄明白這三個問題,就要先了解靜態資源在JVM中的載入機制。

實際上,雖然說靜態資源是屬於類的,但在JVM中卻是獨立於類的存在。因為從JVM類載入機制的角度來講,靜態資源是類初始化的時候載入的,而非靜態資源則是派生類的時候才載入的。類的初始化早於類的派生(new)。比如,在Class.forName("xxx")方法中,就是初始化了一個類,但是並不是派生出一個例項,而只是載入了這個類中的靜態資源。因此對於一個靜態資源來說,它是不可能知道一個類中有哪些非靜態資源的。但是對於非靜態資源來說就不一樣了,由於它是派生例項之後才產生的,因此屬於類的這些東西它都能識別得到。至此,上面三個問題的答案已經呼之欲出了:

1.靜態方法能不能引用非靜態資源?答案是不能,非靜態資源是派生例項之後才產生的,對於在初始化階段就存在的靜態資源來說,根本識別不到。

2.靜態方法能不能引用靜態資源?答案是可以,因為靜態資源都是在類初始化的時候一同載入的,自然都能互相識別得到。

3.非靜態方法能不能引用靜態資源?答案是可以,因為非靜態方法就是例項方法,在派生類例項之後產生,而靜態資源已經在類初始化的時候已經存在了,自然能在引用靜態資源的時候成功識別。

靜態塊

靜態塊也是static關鍵字的重要應用之一,作用是初始化一個類的時候做特定的操作。和靜態變數、靜態方法同樣,靜態塊裡面的程式碼只會執行一次,且只在初始化類的時候執行。靜態塊同樣很簡單,只有三個小細節要特別提及:

靜態資源的載入順序是嚴格按照靜態資源的定義順序來載入的。

public class A
{
  private static int a = B();
   
  static
  {
    System.out.println("進入A類的靜態塊");
  }
   
  public static void main(String[] args)
  {
    new A();
  }
   
  public static int B()
  {
    System.out.println("進入A類靜態變數a.B()靜態方法中");
    return 1;
  }
}

在這裡,因為靜態變數a的定義順序在靜態塊之前,因此在a先被初始化的時候靜態方法B先於靜態塊被呼叫執行,列印的結果是:

進入A類靜態變數a.B()靜態方法中
進入A類的靜態塊

靜態程式碼塊對於定義在它之後的靜態變數,可以賦值,但是不能訪問。

public class A
{
  static
  {
    c = 3;
    System.out.println(c);
  }
  
  private static int c;
}

上面這段程式碼會在第6行報錯:Cannot reference a field before it is defined。這個特性理解起來可能比較奇怪,個人的理解是給靜態方法賦值並不是實時的,Java遇到賦值語句的時候會先將這些個賦值語句快取起來,等所有靜態資源都識別完成之後再統一進行賦值。

靜態程式碼塊是嚴格按照父類靜態程式碼塊->子類靜態程式碼塊的順序載入的,且只加載一次。

public class A
{
  static
  {
    System.out.println("A類的靜態程式碼塊");
  }
  
  public A()
  {
    System.out.println("A類的構造器");
  }
}
public class B extends A
{
  static 
  {
    System.out.println("B類的靜態程式碼塊");
  }
  
  public B()
  {
    System.out.println("B類的構造器");
  }
  
  public static void main(String[] args)
  {
    new B();
    new B();
  }
}

上面程式碼的結果是:

A類的靜態程式碼塊
B類的靜態程式碼塊
A類的構造器
B類的構造器
A類的構造器
B類的構造器

靜態內部類

一般情況下,static是關鍵字是不能用於修飾類的,只有在該類是內部類的情況下才能使用static修飾,且只能修飾一個,這樣的內部類被稱為靜態內部類(匿名內部類)。靜態內部類只有在一些特殊的場景中才能用得上,比如像執行緒池ThreadPoolExecutor中的四種拒絕機制CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy就是靜態內部類。

與import關鍵字聯合使用

import static是JDK1.5之後的新特性,這兩個關鍵字聯合使用可以指定匯入某個類中的指定靜態資源,並且不需要使用類名.資源名,可以直接使用資源名。

import static java.lang.Math.*;

public class A
{
  public static void main(String[] args)
  {
    System.out.println(sin(2.2));
  }
}

這麼寫意味著匯入了java.lang.Math包下的所有靜態資源,因此在main函式裡就可以直接使用sin(2,2)而不需要使用Math.sin(2,2)了。另外使用這種語法要特別注意的是,這裡要寫import static java.lang.Math.*,最後的【.*】不可少,有了這兩個字元才意味著匯入的是Math下的所有靜態資源,寫成import static java.lang.Math是有問題的。當然,我們也可以指定只匯入某個靜態資源,比如只匯入Math下sin這個方法而不匯入Math下的所有靜態資源。

import static java.lang.Math.sin;

public class A
{
  public static void main(String[] args)
  {
    System.out.println(sin(2.2));
  }
}

使用import static這樣的語法可以有效簡化一些操作,比如在頻繁使用Math類下靜態資源的地方可以少寫很多【Math.】,但是這樣卻降低了程式碼的可讀性,因為這樣就模糊了該靜態資源的來源,弱化了分類的概念。

以上就是詳解java中的static關鍵字的詳細內容,更多關於java static關鍵字的資料請關注我們其它相關文章!