Java中的 static 關鍵字,確實是一個關鍵的字(key word),今天就來總結一下它的用法,說說為什么關鍵。
Java中的 static 關鍵字主要是用來做內存管理的。理解了這句話才能夠比較深入地理解static。
static 可以修飾:
- 變量(所謂 class variable)
- 方法(所謂 class method)
- 代碼塊(所謂 block)
- 內部類(所謂 nested class)
凡是被 static 修飾的這四種元素,都屬於class的元素,即類的,而不是類的實例的。
1) 靜態變量
在聲明變量的時候加上 static ,該變量即是靜態變量。
- 什么時候該用 static 來修飾變量呢?該變量被該類的所有實例所共享。
- 靜態變量在類被加載的時候初始化,且僅分配一次內存。
這樣做的好處就是內存利用率高,看下下面兩個demo:
1 // Understanding problem without static variable 2 class Student{ 3 int stuNo; 4 String name; 5 String college="ITS"; 6 }
假設這個"niubility my brother" 的學校有500000000個學生,那么當每一個學生被創建的時候,都會初始化學號、姓名、學校,每個學生都有自己的學號和姓名,這樣做沒問題;但是每個學生的college字段都相同,如果每次都聲明一遍的話,是比較耗內存的。這里的college變量其實是被該類的所有實例所共享的,因此可以將它聲明為 static 的。
1 //Program of static variable 2 class Student8{ 3 int rollno; 4 String name; 5 static String college ="ITS"; 6 7 Student8(int r,String n){ 8 rollno = r; 9 name = n; 10 } 11 void display (){System.out.println(rollno+" "+name+" "+college);} 12 13 public static void main(String args[]){ 14 Student8 s1 = new Student8(111,"Karan"); 15 Student8 s2 = new Student8(222,"Aryan"); 16 17 s1.display(); 18 s2.display(); 19 } 20 }
Output:111 Karan ITS
222 Aryan ITS
看下實際內存分配情況:
靜態變量分配在了方法區,堆中該類的所有實例共享方法區中的college。
再看一下下面這個栗子:Counter 類中聲明了一個count變量,在構造函數中對其進行++操作,因為實例變量在對象被創建的時候分配內存,所有每一個對象都有一份自己的count副本,每個對象對各自count的++操作不會反應到其他對象上。
1 class Counter{ 2 int count=0;//will get memory when instance is created 3 4 Counter(){ 5 count++; 6 System.out.println(count); 7 } 8 9 public static void main(String args[]){ 10 11 Counter c1=new Counter(); 12 Counter c2=new Counter(); 13 Counter c3=new Counter(); 14 15 } 16 }
Output:1 1 1
因為靜態變量僅僅在類加載的時候分配一次內存,所以如果將count修飾為static,那么該類的所有對象將會共享該變量,每一個對象對count的操作都會反應到其他對象上。
1 class Counter2{ 2 static int count=0;//will get memory only once and retain its value 3 4 Counter2(){ 5 count++; 6 System.out.println(count); 7 } 8 9 public static void main(String args[]){ 10 11 Counter2 c1=new Counter2(); 12 Counter2 c2=new Counter2(); 13 Counter2 c3=new Counter2(); 14 15 } 16 }
Output:1 2 3
2)靜態方法
在聲明方法的時候加上 static 關鍵字,即靜態方法:
- 靜態方法屬於類而不是對象。
- 靜態方法可以直接通過類名調用,而不需要創建類的對象。
- 靜態方法可以修改靜態變量,而非靜態方法不可以。
一個靜態方法的栗子:
1 //Program of changing the common property of all objects(static field). 2 class Student9{ 3 int rollno; 4 String name; 5 static String college = "ITS"; 6 7 static void change(){ 8 college = "BBDIT"; 9 } 10 11 Student9(int r, String n){ 12 rollno = r; 13 name = n; 14 } 15 16 void display (){System.out.println(rollno+" "+name+" "+college);} 17 18 public static void main(String args[]){ 19 Student9.change(); 20 21 Student9 s1 = new Student9 (111,"Karan"); 22 Student9 s2 = new Student9 (222,"Aryan"); 23 Student9 s3 = new Student9 (333,"Sonoo"); 24 25 s1.display(); 26 s2.display(); 27 s3.display(); 28 } 29 }
Output:111 Karan BBDIT 222 Aryan BBDIT 333 Sonoo BBDIT
靜態方法的第二顆栗子:
1 //Program to get cube of a given number by static method 2 class Calculate{ 3 static int cube(int x){ 4 return x*x*x; 5 } 6 7 public static void main(String args[]){ 8 int result=Calculate.cube(5); 9 System.out.println(result); 10 } 11 }
靜態方法的兩個注意點:
- 靜態方法不能操作非靜態變量,也不能調用非靜態方法。(這個可以這樣理解:靜態方法屬於類,直接通過類名就可以調用,而此時可能沒有任何實例,更談不上操作實例變量和調用實例方法了。)
- 靜態方法中不能使用 this 和 super 關鍵字。(道理同上)
1 class A{ 2 int a=40;//non static 3 4 public static void main(String args[]){ 5 System.out.println(a); 6 } 7 }
Output:Compile Time Error
Q)為什么Java的main方法是static的?
Ans)為了使得在調用main方法之前不需要創建任何實例對象。
3)靜態代碼塊
為什么要有這個東西?干嘛用呢?
- 用來初始化靜態變量。
- 在類加載時,在執行main方法之前執行相關操作。
栗子:
1 class A2{ 2 static{System.out.println("static block is invoked");} 3 public static void main(String args[]){ 4 System.out.println("Hello main"); 5 } 6 }
Output:static block is invoked Hello main
Q)沒有main方法的程序可以執行嗎?
Ans)可以,在JDK1.7之前執行如下代碼是可以的:
1 class A3{ 2 static{ 3 System.out.println("static block is invoked"); 4 System.exit(0); 5 } 6 }
Output:static block is invoked (if not JDK7)
但是在JDK1.7會報如下錯誤:
Output:Error: Main method not found in class A3, please define the main method as: public static void main(String[] args)
4)靜態內部類
被static修飾的類,並且處於某個類的內部。
- 它可以訪問外部類的靜態成員,包括private成員。
- 它不能訪問外部類的非靜態成員。(原因前面說過)
那么為什么要有靜態內部類呢?看下面的栗子:
1 class TestOuter1{ 2 static int data=30; 3 static class Inner{ 4 void msg(){System.out.println("data is "+data);} 5 } 6 public static void main(String args[]){ 7 TestOuter1.Inner obj=new TestOuter1.Inner(); 8 obj.msg(); 9 } 10 }
Output:data is 30
main方法中創建了一個靜態內部類的實例,並且調用了其msg() 方法。但是這里並沒有創建外部類的實例,因為這里的Inner類是static的,並且可以訪問外圍類的static成員。如果把Inner之前的static去掉,那么這里要這樣寫:
TestStaticInnerClass.Inner obj=new TestStaticInnerClass().new Inner();
需要先創建外一個部類實例,然后才能創建內部類實例。靜態內部類不僅高效利用內存,而且使得代碼簡潔。下面是static 內部類編譯后的樣子:
1 // Internal class generated by the compiler 2 import java.io.PrintStream; 3 static class TestOuter1$Inner 4 { 5 TestOuter1$Inner(){} 6 void msg(){ 7 System.out.println((new StringBuilder()).append("data is ") 8 .append(TestOuter1.data).toString()); 9 } 10 }
上例中,如果msg() 方法也是靜態的,那么內部類的實例也不需要創建了:
1 class TestOuter2{ 2 static int data=30; 3 static class Inner{ 4 static void msg(){System.out.println("data is "+data);} 5 } 6 public static void main(String args[]){ 7 TestOuter2.Inner.msg();//no need to create the instance of static nested class 8 } 9 }
Output:data is 30
以上是關於Java 中 static 關鍵字的理解,參考了:http://www.javatpoint.com/