a中static關鍵字詳解。
由於學習需要找了半天找到了這篇文章,發現作者寫的很不錯,就轉載給大家:
static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。
被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴類特定的例項,被類的所有例項共享。只要這個類被載入,Java虛擬機器就能根據類名在執行時資料區的方法區內找到他們。因此,static物件可以在它的任何物件建立之前訪問,無需引用任何物件。
public修飾的static成員變數和成員方法本質是全域性變數和全域性方法,當宣告它類的物件時,不生成static變數的副本,而是類的所有例項共享同一個static變數。
private修飾的staitc成員變數,表示這個變數可以在類的靜態程式碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用),但是不能在其他類中通過類名來直接引用,這一點很重要。而且private修飾的方法,也不能在其他類中通過類名來直接引用。
實際上你需要搞明白,private是訪問許可權限定,static表示不要例項化就可以使用
static修飾的成員變數和成員方法習慣上稱為靜態變數和靜態方法,可以直接通過類名來訪問,訪問語法為:
類名.靜態方法名(引數列表...)
類名.靜態變數名
用static修飾的程式碼塊表示靜態程式碼塊,當Java虛擬機器(JVM)載入類時,就會執行該程式碼塊
1.static變數
宣告為static的變數在定義的地方被初始化,並且只能初始化一次。
如果初始化,就生成類初始化函式<clinit>,否則沒有:a.在它被宣告的時候賦值, b.在靜態或非靜態快裡初始化
按照是否靜態的對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數或類變數;另一種是沒有被static修飾的變數,叫例項變數。兩者的區別是:
在類被載入時static修飾的成員變數被初始化,與類關聯,只要類存在,static變數就存在。對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配,可用類名直接訪問(方便),當然也可以通過物件來訪問(但是這是不推薦的)。一個static變數單獨劃分一塊儲存空間,不與具體的物件繫結在一起,該儲存空間被類的各個物件所共享。也就是說當宣告一個物件時,並不產生static變數的拷貝,而是該類所有的例項物件共用同一個static變數。static修飾的成員變數能在建立任何例項物件之前被訪問,而不必引用任何物件,也就是說不管建立多少物件,static修飾的變數只佔有一塊記憶體。當執行時,在程式空間中,類的所有物件訪問到的靜態變數都是同一個值,當其中一個物件改變了靜態變數的值,其他物件都將受到影響。
對於例項變數,沒建立一個例項,就會為例項變數分配一次記憶體,例項變數可以在記憶體中有多個拷貝,互不影響(靈活)。當某個物件被建立時,它們才真正地存在於記憶體空間之中,而且物件本身對它們的改變,不會影響到其它物件。就好像 Person的一個物件zhangsan,zhangsan的改變不會影響到其它Person物件一樣。
被static修飾而沒有被final修飾的類的屬性變數只能在兩種情況下初始化:(可以不初始化)
如果初始化,就生成類初始化函式<clinit>,否則沒有
a.在它被宣告的時候賦值,例:
public class Test{
public static l int a=8;
private Test(){
}
}
b.在靜態或非靜態快裡初始化
public class Test{
public static l int a;
static{a=50;}
private Test(){
}
}
解釋:
當類的屬性被同時被修飾為static時候,他屬於類的資源(類變數),在類載入後,進行連線時候,分三步: 先驗證;然後準備,準備時,先分配記憶體,接著預設初始化;可以進行解析。最後,進行類初始化,類初始化前,必須保證它的父類已經初始化了,所以最先初始化的是超類,對於介面,不必初始其父介面。類初始化時,它把類變數初始化語句及靜態初始化語句放到類初始化方法中,所以,如果無此兩種語句,也就沒<clinit>類初始化方法,而建構函式是在當類 被例項化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.
看下面這段程式碼:
class Value{
static int c=0;
static void inc(){
c++;
}
}
class Count{
public static void prt(String s){
System.out.println(s);
}
public static void main(String[] args){
Value v1,v2;
v1=new Value();
v2=new Value();
prt("v1.c="+v1.c+" v2.c="+v2.c);
v1.inc();
prt("v1.c="+v1.c+" v2.c="+v2.c);
}
}
結果如下:
v1.c=0 v2.c=0
v1.c=1 v2.c=1
由此可以證明它們共享一塊儲存區。static變數有點類似於C中的全域性變數的概念。值得探討的是靜態變數的初始化問題。我們修改上面的程式:
class Value{
static int c=0;
Value(){
c=15;
}
Value(int i){
c=i;
}
static void inc(){
c++;
}
}
class Count{
public static void prt(String s){
System.out.println(s);
}
Value v=new Value(10);
static Value v1,v2;
static{
prt("v1.c="+v1.c+" v2.c="+v2.c);
v1=new Value(27);
prt("v1.c="+v1.c+" v2.c="+v2.c);
v2=new Value(15);
prt("v1.c="+v1.c+" v2.c="+v2.c);
}
public static void main(String[] args){
Count ct=new Count();
prt("ct.c="+ct.v.c);
prt("v1.c="+v1.c+" v2.c="+v2.c);
v1.inc();
prt("v1.c="+v1.c+" v2.c="+v2.c);
prt("ct.c="+ct.v.c);
}
}
執行結果如下:
v1.c=0 v2.c=0
v1.c=27 v2.c=27
v1.c=15 v2.c=15
ct.c=10
v1.c=10 v2.c=10
v1.c=11 v2.c=11
ct.c=11
這個程式展示了靜態初始化的各種特性。如果你初次接觸Java,結果可能令你吃驚。可能會對static後加大括號感到困惑。首先要告訴你的是,static定義的變數會優先於任何其它非static變數,不論其出現的順序如何。正如在程式中所表現的,雖然v出現在v1和v2的前面,但是結果卻是v1和v2的初始化在v的前面。在static{後面跟著一段程式碼,這是用來進行顯式的靜態變數初始化,這段程式碼只會初始化一次,且在類被第一次裝載時。如果你能讀懂並理解這段程式碼,會幫助你對static關鍵字的認識。在涉及到繼承的時候,會先初始化父類的static變數,然後是子類的,依次類推。
通常一個普通類不允許宣告為靜態的,只有一個內部類才可以。這時這個宣告為靜態的內部類可以直接作為一個普通類來使用,而不需例項一個外部類。如下程式碼所示:
public class StaticCls{
public static void main(String[] args){
OuterCls.InnerCls oi=new OuterCls.InnerCls();
}
}
class OuterCls{
public static class InnerCls{
InnerCls(){
System.out.println("InnerCls");
}
}
}
輸出結果會如你所料:InnerCls
2.static方法
靜態方法可以直接通過類名呼叫,任何的例項也都可以呼叫,因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的例項變數和例項方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。因為static方法獨立於任何例項,因此static方法必須被實現,而不能是抽象的abstract。
示例程式碼:
class Simple{
static void go(){
System.out.println("Go...");
}
}
public class Cal{
public static void main(String[] args){
Simple.go();
}
}
呼叫一個靜態方法就是“類名.方法名”,靜態方法的使用很簡單如上所示。一般來說,靜態方法常常為應用程式中的其它類提供一些實用工具所用,在Java的類庫中大量的靜態方法正是出於此目的而定義的。
宣告為static的方法有以下幾條限制:
◆它們僅能呼叫其他的static 方法。
◆它們只能訪問static資料。
◆它們不能以任何方式引用this 或super
◆靜態方法中不能定義靜態變數
3.static程式碼塊
static程式碼塊也叫靜態程式碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的程式碼塊,如果static程式碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個程式碼塊只會被執行一次。靜態程式碼塊沒有名字,因此不能顯式呼叫,而只有在類載入的時候由虛擬機器來呼叫。它主要用來完成一些初始化操作。
4、static和final一塊用表示什麼
static final用來修飾成員變數和成員方法,可簡單理解為“全域性常量”!
對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。
對於方法,表示不可覆蓋,並且可以通過類名直接訪問。
同時被final和static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)
a.在它被定義的時候,例:
public class Test{
public final static int a=5;
private Test(){
}
}
b.在類的靜態塊裡初始化,例:
public class Test{
public final static int a;
static{
a=0;
}
}
c、特別對於初始化時候呼叫丟擲異常的建構函式,初始時候注意,特別是在實現單例模式時(只能這麼初始化)
如:
class A {
private final static A a;
static {
try {
a=new A();
}catch(Exception e){
throws new RuntimeException(e); //必須有,不然不能完成常量的正確初始化
}
}
private A() throws Exception{}
}
解釋:
當類的屬性被同時被修飾為static和final的時候,他屬於類的資源(類常量),那麼就是類在被載入進記憶體的時候(也就是應用程 序啟動的時候)就要已經為此屬性分配了記憶體,所以此時屬性已經存在,它又被final修飾,所以必須在屬性定義了以後就給其初始化值.而建構函式是在當類 被例項化的時候才會執行,所以用建構函式,這時候這個屬性沒有被初始化.程式就會報錯.而static塊是類被載入的時候執行,且只執行這一次,所以在 static塊中可以被初始化.
static特殊講解:
請先看下面這段程式:
public class Hello{
public static void main(String[] args){ //(1)
System.out.println("Hello,world!"); //(2)
}
}
看過這段程式,對於大多數學過Java 的從來說,都不陌生。即使沒有學過Java,而學過其它的高階語言,例如C,那你也應該能看懂這段程式碼的意思。它只是簡單的輸出“Hello,world”,一點別的用處都沒有,然而,它卻展示了static關鍵字的主要用法。
在1處,我們定義了一個靜態的方法名為main,這就意味著告訴Java編譯器,我這個方法不需要建立一個此類的物件即可使用。你還記得你是怎麼執行這個程式嗎?一般,我們都是在命令列下,打入如下的命令:
javac Hello.java
java Hello
Hello,world!
這就是你執行的過程,第一行用來編譯Hello.java這個檔案,執行完後,如果你檢視當前,會發現多了一個Hello.class檔案,那就是第一行產生的Java二進位制位元組碼。第二行就是執行一個Java程式的最普遍做法。執行結果如你所料。在2中,你可能會想,為什麼要這樣才能輸出。好,我們來分解一下這條語句。(如果沒有安裝Java文件,請到Sun的官方網站瀏覽J2SE API)首先,System是位於java.lang包中的一個核心類,如果你檢視它的定義,你會發現有這樣一行:public static final PrintStream out;接著再進一步,點選PrintStream這個超連結,在METHOD頁面,你會看到大量定義的方法,查詢println,會有這樣一行:
public void println(String x)。
好了,現在你應該明白為什麼我們要那樣呼叫了,out是System的一個靜態變數,所以可以直接使用,而out所屬的類有一個println方法。
通常一個普通類不允許宣告為靜態的,只有一個內部類才可以。這時這個宣告為靜態的內部類可以直接作為一個普通類來使用,而不需例項一個外部類。如下程式碼所示:
public class StaticCls{
public static void main(String[] args){
OuterCls.InnerCls oi=new OuterCls.InnerCls();
}
}
class OuterCls{
public static class InnerCls{
InnerCls(){
System.out.println("InnerCls");
}
}
}
輸出結果會如你所料:InnerCls
static,final初始化總結:
1、被final修飾而沒有被static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)
a.在它被宣告的時候賦值;b.在建構函式裡初始化;c .在非靜態塊裡
2、被static修飾而沒有被final修飾的類的屬性變數只能在兩種情況下初始化:(可以不初始化)
a.在它被宣告的時候賦值;b.在靜態或非靜態快裡初始化;
3、同時被final和static修飾的類的屬性變數只能在兩種情況下初始化:(必須初始化)
a.在它被定義的時候;b.在類的靜態塊裡初始化
1.final變數:
當你在類中定義變數時,在其前面加上final關鍵字,那便是說,這個變數一旦被初始化便不可改變,這裡不可改變的意思對基本型別來說是其值不可變,而對於物件變數來說其引用不可再變。其初始化可以在兩個地方,一是其定義處,也就是說在final變數定義時直接給其賦值,二是在建構函式中。這兩個地方只能選其一,要麼在定義時給值,要麼在建構函式中給值,不能同時既在定義時給了值,又在建構函式中給另外的值。另外也可以在非靜態塊裡初始化
public class Test{
private final int a;
{
a=3; //在非靜態塊裡初始化
}
}
解釋:當這個屬性被修飾為final,而非static的時候,它屬於類的例項物件的資源(例項常量),當類被載入進記憶體的時候這個屬性並沒有給其分配記憶體空間,而只是 定義了一個變數a,只有當類被例項化的時候這個屬性才被分配記憶體空間,而例項化的時候同時執行了建構函式,所以屬性被初始化了,也就符合了當它被分配記憶體 空間的時候就需要初始化,以後不再改變的條件.
當函式引數為final型別時,你可以讀取使用該引數,但是無法改變該引數的值。
另外方法中的內部類在用到方法中的參變數時,此參變也必須宣告為final才可使用
2.final方法
如果一個類不允許其子類覆蓋某個方法,則可以把這個方法宣告為final方法。
使用final方法的原因有二:
第一、把方法鎖定,防止任何繼承類修改它的意義和實現。
第二、高效。編譯器在遇到呼叫final方法時候會轉入內嵌機制,大大提高執行效率。
3.final類
final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,預設都是final的。在設計類時候,如果這個類不需要有子類,類的實現細節不允許改變,並且確信這個類不會載被擴充套件,那麼就設計為final類。
由於學習需要找了半天找到了這篇文章,發現作者寫的很不錯,就轉載給大家