這個要從java的內存機制去分析,首先當你New 一個對象的時候,並非先在堆中爲對象開闢內存空間,而是先將類中的靜態方法(帶有static修飾的靜態函數)的代碼加載到一個叫作方法區的地方,而後再在堆內存中建立對象。因此說靜態方法會隨着類的加載而被加載。當你new一個對象時,該對象存在於對內存中,this關鍵字通常指該對象,可是若是沒有new對象,而是經過類名調用該類的靜態方法也能夠。java
程序最終都是在內存中執行,變量只有在內存中佔有一席之地時纔會被訪問,類的靜態成員(靜態變量和靜態方法)屬於類自己,在類加載的時候就會分配內存,能夠經過類名直接去訪問,非靜態成員(非靜態變量和非靜態方法)屬於類的對象,因此只有在類的對象建立(實例化)的時候纔會分配內存,而後經過類的對象去訪問。編程
在一個類的靜態成員中去訪問非靜態成員之因此會出錯是由於在類的非靜態成員不存在的時候靜態成員就已經存在了,訪問一個內存中不存在的東西固然會出錯。函數
在《Java編程思想》P86頁有這樣一段話:性能
「static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是能夠的。並且能夠在沒有建立任何對象的前提下,僅僅經過類自己來調用static方法。這實際上正是static方法的主要用途。」優化
這段話雖然只是說明了static方法的特殊之處,可是能夠看出static關鍵字的基本做用,簡而言之,一句話來描述就是:this
方便在沒有建立對象的狀況下來進行調用(方法/變量)。對象
很顯然,被static關鍵字修飾的方法或者變量不須要依賴於對象來進行訪問,只要類被加載了,就能夠經過類名去進行訪問。內存
static能夠用來修飾類的成員方法、類的成員變量,另外能夠編寫static代碼塊來優化程序性能。作用域
1)static方法io
static方法通常稱做靜態方法,因爲靜態方法不依賴於任何對象就能夠進行訪問,所以對於靜態方法來講,是沒有this的,由於它不依附於任何對象,既然都沒有對象,就談不上this了。而且因爲這個特性,在靜態方法中不能訪問類的非靜態成員變量和非靜態成員方法,由於非靜態成員方法/變量都是必須依賴具體的對象纔可以被調用。
可是要注意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變量,可是在非靜態成員方法中是能夠訪問靜態成員方法/變量的。舉個簡單的例子:
在上面的代碼中,因爲print2方法是獨立於對象存在的,能夠直接用過類名調用。假如說能夠在靜態方法中訪問非靜態方法/變量的話,那麼若是在main方法中有下面一條語句:
MyObject.print2();
此時對象都沒有,str2根本就不存在,因此就會產生矛盾了。一樣對於方法也是同樣,因爲你沒法預知在print1方法中是否訪問了非靜態成員變量,因此也禁止在靜態成員方法中訪問非靜態成員方法。
而對於非靜態成員方法,它訪問靜態成員方法/變量顯然是毫無限制的。
所以,若是說想在不建立對象的狀況下調用某個方法,就能夠將這個方法設置爲static。咱們最多見的static方法就是main方法,至於爲何main方法必須是static的,如今就很清楚了。由於程序在執行main方法的時候沒有建立任何對象,所以只有經過類名來訪問。
另外記住,即便沒有顯示地聲明爲static,類的構造器實際上也是靜態方法。
2)static變量
static變量也稱做靜態變量,靜態變量和非靜態變量的區別是:靜態變量被全部的對象所共享,在內存中只有一個副本,它當且僅當在類初次加載時會被初始化。而非靜態變量是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。
static成員變量的初始化順序按照定義的順序進行初始化。
3)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代碼塊中進行。
二.static關鍵字的誤區
1.static關鍵字會改變類中成員的訪問權限嗎?
有些初學的朋友會將java中的static與C/C++中的static關鍵字的功能混淆了。在這裏只須要記住一點:與C/C++中的static不一樣,Java中的static關鍵字不會影響到變量或者方法的做用域。在Java中可以影響到訪問權限的只有private、public、protected(包括包訪問權限)這幾個關鍵字。看下面的例子就明白了:
提示錯誤"Person.age 不可視",這說明static關鍵字並不會改變變量和方法的訪問權限。
2.能經過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。在這裏永遠要記住一點:靜態成員變量雖然獨立於對象,可是不表明不能夠經過對象去訪問,全部的靜態方法和靜態變量均可以經過對象訪問(只要訪問權限足夠)。
3.static能做用於局部變量麼?
在C/C++中static是能夠做用域局部變量的,可是在Java中切記:static是不容許用來修飾局部變量。不要問爲何,這是Java語法的規定。