1、static介紹 static關鍵字一直是各大企業中面試常常會問到的問題,主要考察面試者的基礎是否扎實,下麵來介紹一下static關鍵字。 Java中static表示“全局”或者“靜態”的意思,可以用來修飾成員變數、成員方法、代碼塊、內部類和導包。在Java中並不存在全局變數的概念,但是我們可以 ...
1、static介紹
static關鍵字一直是各大企業中面試常常會問到的問題,主要考察面試者的基礎是否扎實,下麵來介紹一下static關鍵字。
Java中static表示“全局”或者“靜態”的意思,可以用來修飾成員變數、成員方法、代碼塊、內部類和導包。在Java中並不存在全局變數的概念,但是我們可以通過static來實現一個“偽全局”的概念,被static修飾的成員變數和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共用。只要這個類被載入了,Java虛擬機就能根據類名在運行時數據區的方法區內定找到他們。因此,static對象可以在它的任何對象創建之前訪問,無需引用任何對象,所以被static修飾的成員變數和成員方法可以直接使用類名調用。
class Person{ private static int num=0; public Person() { num++; } public static void plus(){ System.out.println(Person.num); } public static void main(String[] args) { new Person(); new Person(); Person.plus(); plus(); } } //結果:2、2
2、static變數
static修飾的成員變數稱作靜態變數,靜態變數被所有的對象所共用,在記憶體中只有一個,它會隨著類的載入而載入。
另外主要:static是不允許用來修飾局部變數。
提到靜態變數我們來看靜態變數和非靜態變數的區別:
靜態變數(類變數):靜態變數被所有的對象所共用,也就是說我們創建了一個類的多個對象,多個對象共用著一個靜態變數,如果我們修改了靜態變數的值,那麼其他對象的靜態變數也會隨之修改。
非靜態變數(實例變數):如果我們創建了一個類的多個對象,那麼每個對象都有它自己該有的非靜態變數。當你修改其中一個對象中的非靜態變數時,不會引起其他對象非靜態變數值得改變。
class Person{ private static int num; private int num1; public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(); Person.num=10; p1.num1=11; Person.num=100; p2.num1=111; System.out.println(Person.num); System.out.println(p1.num); System.out.println(p2.num); System.out.println(p1.num1); System.out.println(p2.num1); } }
從運行結果來看,static變數的值是相同的,說明是共用的,而非靜態變數他們的值則不相同,說明依賴於實例。雖然static修飾的變數它不依賴類特定的實例,但它畢竟也是類中的一個屬性,也是可以通過類的實例來調用的,只不過屬性的值是共用的而已。但是最好還是用類名調用。
3、static方法
static修飾的成員方法稱作靜態方法,這樣我們就可以通過“類名. 方法名”進行調用。由於靜態方法在類載入的時候就存在了,所以它不依賴於任何對象的實例就可以進行調用,因此對於靜態方法而言,是木有當前對象的概念,即沒有this、super關鍵字的。因為static方法獨立於任何實例,因此static方法必須被實現,而不能是抽象的abstract。
並且由於獨立於任何實例,在靜態方法中不能訪問類的非靜態成員變數和非靜態成員方法,因為非靜態成員方法/變數都是必須依賴具體的對象才能夠被調用。但是要註意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變數,但是在非靜態成員方法中是可以訪問靜態成員方法/變數的。舉個簡單的例子:
class Person{ public static void main(String[] args) { Person p=new Person(); p.show1(); } public void show1(){ System.out.println("非靜態方法show1()..."); show2(); show3(); } public static void show2(){ System.out.println("靜態方法show2()..."); //這裡編譯會報錯Non-static method 'show1()' cannot be referenced from a static context //show1(); } public static void show3(){ System.out.println("靜態方法show3()..."); show2(); } }
4、static代碼塊
被static修飾的代碼塊也叫靜態代碼塊,會隨著JVM載入類的時候而載入這些靜態代碼塊,並且會自動執行。它們可以有多個,可以存在於該類的任何地方。JVM會按照它們的先後順序依次執行它們,而且每個靜態代碼塊只會被初始化一次,不會進行多次初始化。
示例:
public class Person{ static { System.out.println("Person類靜態塊"); } public Person() { System.out.println("Person類構造器"); } public static void main(String[] args) { new Son(); System.out.println("-------"); new Son(); } } class Son extends Person{ static { System.out.println("Son類靜態塊"); } public Son() { System.out.println("Son類構造器"); } }
運行結果:
從運行結果分析:首先運行main()方法,然後JVM就會載入類,因為Son類繼承了Person類,所以會先載入父類Person類,再去載入子類Son。由於靜態代碼塊會隨著類的載入而載入,所以先輸出父類中靜態代碼塊內容"Person類靜態塊",然後輸出子類中靜態代碼塊內容"Son類靜態塊"。載入完類之後執行main()方法內容,先new了第一個Son實例,由於子類構造器中預設調用了super(),所以先輸出父類構造器的內容,再輸出子類構造器的內容。之後又new了第二個Son實例,卻是輸出的構造器的內容,說明static靜態塊只載入了一次。結論:靜態代碼塊是先載入父類的靜態代碼塊,然後再載入子類靜態代碼塊,是隨著類的載入而載入,而且只會載入一次。
補充:因為入口main()是個方法,也需要用類去調用,所以類的載入優先順序>main()方法。
5、static內部類
static修飾內部類的用法很少,畢竟內部類用的就不是很多,一般在源碼中才能看見。但是就是有這麼一個特殊的用法是用static修飾內部類。普通類是不允許聲明為靜態的,只要內部類才可以,被static修飾的內部類可以直接作為一個普通類來使用,而不需先實例一個外部類。
static修飾內部類註意幾點:
- 靜態內部類只能訪問外部類的靜態成員,否則編譯會報錯。
- 不管是靜態方法還是非靜態方法都可以在非靜態內部類中訪問。
- 如果需要調用內部類的非靜態方法,必須先new一個OuterClass的對象outerClass,然後通過outer。new生成內部類的對象,而static內部類則不需要。
簡單舉例:
public class OuterClass { private static int num=6; // 靜態內部類 public static class InnerStaticClass{ public void print() { System.out.println("靜態內部類方法print()=="+num); } } //非靜態內部類 public class InnerClass{ public void display(){ System.out.println("非靜態內部類方法display()=="+num); show(); } } public void show(){ System.out.println("外部類的show()方法=="+num); } public static void main(String[] args) { //非static對象實例 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); innerClass.display(); //static對象實例 OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass(); staticClass.print(); } }
6、static導包
這個知識點非常的冷門,基本上很少的地方會用,我們只需要瞭解一下即可,用static修飾導包的格式是 import static 包名,static不能寫在import前面,這樣可以指定導入某個類中的指定靜態資源,並且不需要使用類名.資源名,可以直接使用資源名。
來看一下案例:
import static java.lang.Math.*; public class StaticTest { public static void main(String[] args) { System.out.println(sqrt(9)); System.out.println(abs(-12)); } } //結果:3、12
從上面的案例看出,使用import static我們導入了Math類下的所有靜態資,所以我們就可以直接使用sqrt(9)、abs(-12)靜態方法了。
這樣在寫代碼的時候確實能省一點代碼,但是會影響代碼可讀性,所以一般情況下不建議這麼使用。
7、總結
static是非常重要的一個關鍵字,它的用法也很豐富,以下總結為:
①、static可以用來修飾成員變數、成員方法、代碼塊、內部類和導包。
②、用來修飾成員變數,將其變為靜態變數,從而實現所有對象對於該成員的共用,通過“類名.變數名”即可調用。
③、用來修飾成員方法,將其變為靜態方法,可以直接使用“類名.方法名”的方式調用,常用於工具類。
④、靜態方法中不能使用關鍵字this和super 。
⑤、靜態塊用法,將多個類成員放在一起初始化,使得程式更加規整,其中理解對象的初始化過程非常關鍵。
⑥、靜態代碼塊是先載入父類的靜態代碼塊,然後再載入子類靜態代碼塊,而且只會載入一次。
⑦、static修飾成員變數、成員方法、代碼塊會隨著類的載入而載入。
⑧、靜態導包用法,將類的方法直接導入到當前類中,從而直接使用“方法名”即可調用類方法,更加方便,但是代碼的可讀性降低。