1. static
關鍵字的用途
static
Think in java 中的解釋
-
英文版
https://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ304_014.htm
-
中文 PDF
連結: https://pan.baidu.com/s/12gkiaJrTubMsUOjaBSkbOw 提取碼: iyvs
P119 中有解釋:

簡而言之,一句話來描述就是:
友善在沒有建立對象的情況下來進行調用(方法/變量)
很顯然,被
static
關鍵字修飾的方法或者變量不需要依賴于對象來進行通路,隻要類被加載了,就可以通過類名去進行通路。
static
可以用來修飾類的成員方法、類的成員變量,另外可以編寫
static
代碼塊來優化程式性能
1.1 static
方法
static
static
方法一般稱作靜态方法,由于靜态方法不依賴于任何對象就可以進行通路,是以對于靜态方法來說,是沒有
this
的,因為它不依附于任何對象,既然都沒有對象,就談不上
this
了。并且由于這個特性,在靜态方法中不能通路類的非靜态成員變量和非靜态成員方法,因為非靜态成員方法/變量都是必須依賴具體的對象才能夠被調用。
但是要注意的是,雖然在靜态方法中不能通路非靜态成員方法和非靜态成員變量,但是在非靜态成員方法中是可以通路靜态成員方法/變量的。舉個簡單的例子:
在上面的代碼中,由于
print2()
方法是獨立于對象存在的,可以直接用過類名調用。假如說可以在靜态方法中通路非靜态方法/變量的話,那麼如果在
main()
方法中有下面一條語句:
MyObject.print2();
此時對象都沒有,
str2
根本就不存在,是以就會産生沖突了。同樣對于方法也是一樣,由于你無法預知在
print1()
方法中是否通路了非靜态成員變量,是以也禁止在靜态成員方法中通路非靜态成員方法。
而對于非靜态成員方法,它通路靜态成員方法/變量顯然是毫無限制的。
是以,如果說想在不建立對象的情況下調用某個方法,就可以将這個方法設定為
static
。我們最常見的
static
方法就是
main
方法,至于為什麼
main
方法必須是static的,現在就很清楚了。因為程式在執行
main
方法的時候沒有建立任何對象,是以隻有通過類名來通路。
另外記住,關于構造器是否是static方法可參考
1.2 static
變量
static
static
變量也稱作 靜态變量,靜态變量和非靜态變量的差別是:
- 靜态變量被所有的對象所共享,在記憶體中隻有一個副本,它當且僅當在 類初次加載時會被初始化。
- 而非靜态變量是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。
-
成員變量的初始化順序按照定義的順序進行初始化。static
1.3 static
變量
static
static
關鍵字還有一個比較關鍵的作用就是 用來 形成靜态代碼塊以優化程式性能。
static
塊可以置于類中的任何地方,類中可以有多個
static
塊。在類初次被加載的時候,會按照
static
塊的順序來執行每個
static
塊,并且隻會執行一次。
為什麼說
static
塊可以用來優化程式性能,是因為它的特性: 隻會在類加載的時候執行一次。下面看個例子:
class Person{
private Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
Date startDate = Date.valueOf("1946");
Date endDate = Date.valueOf("1964");
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
isBornBoomer
是用來這個人是否是1946-1964年出生的,而每次
isBornBoomer
被調用的時候,都會生成
startDate
和
birthDate
兩個對象,造成了空間浪費,如果改成這樣效率會更好:
class Person{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1946");
endDate = Date.valueOf("1964");
}
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
是以,很多時候會将一些隻需要進行一次的初始化操作都放在
static
代碼塊中進行。
2. static
關鍵字的誤區
static
2.1 static
會改變類中成員的通路權限嗎?
static
在Java中能夠影響到通路權限的隻有
private
、
public
、
protected
(包括包通路權限)這幾個關鍵字。看下面的例子就明白了:
示錯誤"Person.age 不可視",這說明
static
關鍵字并不會改變變量和方法的通路權限。
2.2 能通過 this
通路靜态成員變量嗎?
this
雖然對于靜态方法來說沒有
this
,那麼在非靜态方法中能夠通過
this
通路靜态成員變量嗎?先看下面的一個例子,這段代碼輸出的結果是什麼?
public class Main {
static int value = 33;
public static void main(String[] args) throws Exception{
new Main().printValue();
}
private void printValue(){
int value = 3;
System.out.println(this.value);
}
}
> 33
這裡面主要考察隊
this
和
static
的了解。
this
代表什麼?
this
代表目前對象,那麼通過
new Main()
來調用
printValue
的話,目前對象就是通過
new Main()
生成的對象。而
static
變量是被對象所享有的,是以在
printValue
中的
this.value
的值毫無疑問是
33
。在
printValue
方法内部的
value
是局部變量,根本不可能與
this
關聯,是以輸出結果是
33
。在這裡永遠要記住一點:靜态成員變量雖然獨立于對象,但是不代表不可以通過對象去通路,所有的靜态方法和靜态變量都可以通過對象通路(隻要通路權限足夠)
2.3 static
能作用于局部變量麼?
static
但是在Java中切記: static是不允許用來修飾局部變量。不要問為什麼,這是Java文法的規定。
具體原因可以參考這篇博文的讨論:http://www.debugease.com/j2se/178932.html
3. 常見的筆試面試題
3.1 下面這段代碼的輸出結果是什麼?
public class Test extends Base{
static{
System.out.println("test static");
}
public Test(){
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test();
}
}
class Base{
static{
System.out.println("base static");
}
public Base(){
System.out.println("base constructor");
}
}
Result:
base static
test static
base constructor
test constructor
至于為什麼是這個結果,我們先不讨論,先來想一下這段代碼具體的執行過程:
- 在執行開始,先要尋找到
方法,因為main
方法是程式的入口.main
- 但是在執行
方法之前,必須先加載main
類,而在加載Test
類的時候發現Test
類繼承自Test
類,是以會轉去先加載Base
類,在加載Base
類的時候,發現有Base
塊,便執行了static
塊。static
- 在
類加載完成之後,便繼續加載Base
類,然後發現Test
類中也有Test
塊,便執行static
塊。在加載完所需的類之後,便開始執行static
方法。main
- 在
方法中執行main
的時候會先調用父類的構造器,然後再調用自身的構造器。是以,便出現了上面的輸出結果。new Test()
3.2 下面這段代碼的輸出結果是什麼?
public class Test {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
class Person{
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
class MyClass extends Test {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
Result:
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
類似地,我們還是來想一下這段代碼的具體執行過程。
- 首先加載
類,是以會執行Test
類中的Test
塊。static
- 接着執行
,而new MyClass()
類還沒有被加載,是以需要加載MyClass
類。在加載MyClass
類的時候,發現MyClass
類繼承自MyClass
類,但是由于Test
類已經被加載了,是以隻需要加載Test
類,那麼就會執行MyClass
類的中的MyClass
。static塊
- 在加載完之後,就通過構造器來生成對象。而在生成對象的時候,必須先初始化父類的成員變量,是以會執行
中的Test
。Person person = new Person()
- 而
類還沒有被加載過,是以會先加載Person
類并執行Person
類中的Person
塊,接着執行父類的構造器,完成了父類的初始化,然後就來初始化自身了。static
- 是以會接着執行
中的MyClass
,最後執行Person person = new Person()
的構造器。MyClass
參考連結
- Java中的static關鍵字解析
- https://www.jianshu.com/p/388174bf905c