1. 程式人生 > >static修飾符詳解

static修飾符詳解

static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。

被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴類特定的例項,被類的所有例項共享。

只要這個類被載入,Java虛擬機器就能根據類名在執行時資料區的方法區內定找到他們。因此,static物件可以在它的任何物件建立之前訪問,無需引用任何物件。

用public修飾的static成員變數和成員方法本質是全域性變數和全域性方法,當宣告它類的物件市,不生成static變數的副本,而是類的所有例項共享同一個static變數。

static變數前可以有private修飾,表示這個變數可以在類的靜態程式碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上private是訪問許可權限定,static表示不要例項化就可以使用,這樣就容易理解多了。static前面加上其它訪問許可權關鍵字的效果也以此類推。

static修飾的成員變數和成員方法習慣上稱為靜態變數和靜態方法,可以直接通過類名來訪問,訪問語法為:

類名.靜態方法名(引數列表...)

類名.靜態變數名

用static修飾的程式碼塊表示靜態程式碼塊,當Java虛擬機器(JVM)載入類時,就會執行該程式碼塊,用處非常大。

1、static變數

按照是否靜態的對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數類變數,static成員變數的初始化順序按照定義的順序進行初始化;另一種是沒有被static修飾的變數,叫例項變數

兩者的區別是:

對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配,可用類名直接訪問(方便),當然也可以通過物件來訪問(但是這是不推薦的)。

對於例項變數,每建立一個例項,就會為例項變數分配一次記憶體,例項變數可以在記憶體中有多個拷貝,互不影響(靈活)。

所以一般在需要實現以下兩個功能時使用靜態變數:

(1)在物件之間共享值時

(2)方便訪問變數時

2、靜態方法

靜態方法可以直接通過類名呼叫,任何的例項也都可以呼叫,因此靜態方法中不能用this和super關鍵字不能直接訪問所屬類的例項變數和例項方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。因為例項成員與特定的物件關聯。但是要注意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變數,但是在非靜態成員方法中是可以訪問靜態成員方法/變數的。

static方法獨立於任何例項,因此static方法必須被實現,而不能是抽象的abstract。

例如為了方便方法的呼叫,Java API中的Math類中所有的方法都是靜態的,而一般類內部的static方法也是方便其它類對該方法的呼叫。

靜態方法是類內部的一類特殊方法,只有在需要時才將對應的方法宣告成靜態的,一個類內部的方法一般都是非靜態的

因此,如果說想在不建立物件的情況下呼叫某個方法,就可以將這個方法設定為static。我們最常見的static方法就是main方法,至於為什麼main方法必須是static的,現在就很清楚了。因為程式在執行main方法的時候沒有建立任何物件,因此只有通過類名來訪問。

另外記住,即使沒有顯示地宣告為static,類的構造器實際上也是靜態方法

3、static程式碼塊

static程式碼塊也叫靜態程式碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的程式碼塊,如果static程式碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個程式碼塊只會被執行一次

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程式碼塊中進行。

4、final static

static final用來修飾成員變數和成員方法,可簡單理解為“全域性常量”。

對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。

對於方法,表示不可覆蓋,並且可以通過類名直接訪問。

對於被static和final修飾過的例項常量,例項本身不能再改變了,但對於一些容器型別(比如,ArrayList、HashMap)的例項變數,不可以改變容器變數本身,但可以修改容器中存放的物件,這一點在程式設計中用到很多。

public class TestStaticFinal {
        private static final StringstrStaticFinalVar = "aaa";
        private static String strStaticVar =null;
        private final String strFinalVar =null;
        private static final intintStaticFinalVar = 0;
        private static final IntegerintegerStaticFinalVar = new Integer(8);
        private static final ArrayList<String> alStaticFinalVar = new ArrayList<String>();
 
        private void test() {
              
 
                strStaticFinalVar="哈哈哈哈";  //錯誤,final表示終態,不可以改變變數本身.
                strStaticVar = "哈哈哈哈";       //正確,static表示類變數,值可以改變.
                strFinalVar="呵呵呵呵";        //錯誤, final表示終態,在定義的時候就要初值(哪怕給個null),一旦給定後就不可再更改。
                intStaticFinalVar=2;           //錯誤, final表示終態,在定義的時候就要初值(哪怕給個null),一旦給定後就不可再更改。
                integerStaticFinalVar=newInteger(8);  //錯誤, final表示終態,在定義的時候就要初值(哪怕給個null),一旦給定後就不可再更改。
                alStaticFinalVar.add("aaa");      //正確,容器變數本身沒有變化,但存放內容發生了變化。這個規則是非常常用的,有很多用途。
                alStaticFinalVar.add("bbb");      //正確,容器變數本身沒有變化,但存放內容發生了變化。這個規則是非常常用的,有很多用途。
               
        } 

5、執行順序

靜態程式碼塊、靜態方法、構造方法等在類載入、例項化的時候的執行順序是怎樣的呢?下面通過一個例子來驗證:有這樣兩個類:

class Father{
     
      static int a = before();
            
      static{
             System.out.println("Fatherstatic");
      }
     
      static int b = after();
     
      public Father(){
             System.out.println("Fatherconstructor");
      }
     
     
      static int before(){
             System.out.println("Fatherstatic before");
             return 1;
      }
     
      static int after(){
             System.out.println("Fatherstatic after");
             return 2;
      }
}
 
class Son extends Father{
     
      int a = fun();
      int b = fun2();
      static{
             System.out.println("Sonstatic");
      }
     
      public Son(){
             System.out.println("Sonconstructor");
      }
     
      static int fun(){
             System.out.println("Son staticfunction");
             return 1;
      }
     
      int fun2(){
             System.out.println("Sonnon-static function");
             return 1;
      }
}


用下面的程式碼測試:

Class s =Class.forName("Son");

列印結果如下:

Father staticbefore

Father static

Father staticafter

Son static

Class.forName是將類載入到JVM,可見載入子類之前,需要先載入父類,並按照出現的順序執行其中的靜態程式碼塊、靜態方法(如果有呼叫)。

改用下面的程式碼測試:

Son son = newSon();

列印結果如下:

Father staticbefore

Father static

Father staticafter

Son static

Fatherconstructor

Son staticfunction

Son non-staticfunction

Son constructor

上面的程式碼直接將Son例項化,同樣需要先將Class檔案載入至虛擬機器,因此前四行的列印結果與上例相同。

可見,程式碼的執行順序為:先執行靜態程式碼,再執行構造方法;先執行父類,在執行子類。



相關推薦

Java 基礎之 static 修飾

        static修飾成員不能訪問沒有static修飾的成員,因為static修飾的成員是所有物件所共有的,也就是屬於這個類,所以在沒有例項化任何一個物件的時候就可以用類來訪問這個靜態屬性, 同時,任何一個物件都可以訪問修改這個靜態屬性,比如:一個靜態方法,它不可以

static修飾

static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴類特定的例項,被類的所有例項共享。只要這個類被載

Koltin——最詳細的可見性修飾

如果 子類 但是 同一文件 文章 f2c -o 系列 star 在Kotlin中,不管是類,對象,接口,構造函數,函數,屬性及其設置器都具有可見性修飾符。Kotlin中的可見性修飾符共四種。即public、protected、private、internal。在不同的場景

c#中4個訪問修飾和8個宣告修飾

[1]In GrandClass.Constructor [2]In ParentClass.Constructor [3]In ParentClass.Method() use override [4]In GrandClass.Constructor [5]In NewParentClass.Constr

Java訪問許可權修飾

Java中的訪問許可權修飾符(Java關鍵字)今天很皮的發現,自己現在竟然不會玩兒Java基礎了,就翻看了一下自己的筆記,寫一篇文章複習一下!測試環境:Java執行環境,myEclipse。測試前準備:實體類包(Student類,Teacher類),測試類包(TestPubl

Java介面修飾-可以修飾介面的修飾

介面是構建鬆耦合軟體系統的重要法寶。 A、介面用於描述系統對外提供的所有服務,因此介面中的成員常量和方法都必須是公開(public)型別的,確保外部使用者能訪問它們; B、介面僅僅描述系統能做什麼,但不指明如何去做,所以介面中的方法 都是抽象(abstract)方法; C、介面不涉及和任何

vue事件修飾

        vue提倡的是在方法中只有對資料的處理,所以提供了事件修飾符用於DOM的事件處理,常用的事件修飾符有以下幾個:(1). stop:阻止冒泡(通俗講就是阻止事件向上級DOM元素傳遞)                        點選內層div的結果:     

vue中事件修飾(stop, prevent, self, once, capture, passive)

==.stop== 是阻止冒泡行為,不讓當前元素的事件繼續往外觸發,如阻止點選div內部事件,觸發div事件 ==.prevent== 是阻止事件本身行為,如阻止超連結的點選跳轉,form表單的點選提交 ==.self== 是隻有是自己觸發的自己才會執行,如果接受到內部的冒泡事件傳遞訊號觸發,會忽略掉這個訊

java中4種修飾訪問許可權的區別及全過程,final修飾 ,static修飾 ,abstract修飾

 訪問許可權         類   包  子類  其他包     public          ∨   ∨   ∨     ∨     protected     ∨   ∨   ∨     ×     default         ∨   ∨   ×    

c++ cout<< cin>> 註釋

嵌套 技術 設備 main brush 註釋符 str mage inux std::cout是在#include<iostream>庫中的ostream類型中的對象 std::表示命名空間,標準庫定義的所有名字都在命名空間std中 std::cout是在#in

Java的位運算實例——與(&)、非(~)、或(|)、異或(^)(僅作記錄)

out 位運算符 異或運算 pri stat 運算 data 操作 amp 位運算符主要針對二進制,它包括了:“與”、“非”、“或”、“異或”。從表面上看似乎有點像邏輯運算符,但邏輯運算符是針對兩個關系運算符來進行邏輯運算,而位運算符主要針對兩個二進制數的位進行邏輯運算。下

泛型通配

object 指向 string類型 類型參數 tlist i++ 泛型 通配符 如果 一、基本概念:在學習Java泛型的過程中, 通配符是較難理解的一部分. 主要有以下三類:1. 無邊界的通配符(Unbounded Wildcards), 就是<?>, 比如L

【轉載】 c++中static的用法

ostream 並不會 style 轉載 程序員 都是 note 每次 reference 出處: http://blog.csdn.net/majianfei1023/article/details/45290467 C 語言的 static 關鍵字有三種(具體來說是

8-2正則表達式元字 1

shell 正則表達式 元字符 表示前面的字符出現0到多次正則當中有幾個符號是為別人活著的.+表示前面的字符至少出現一次,但是”+”是擴展元字符,需要使用egrep,不能使用greprm -rf .pdf//刪除以.pdf結尾的所有文件或目錄grep ‘abc*’ /etc/passwd 在vi

位運算

返回 mage 分享圖片 clas AS nbsp 左移 不能 str 位運算符比一般的算術運算符速度要快,而且可以實現一些算術運算符不能實現的功能。如果要開發高效率程序,位運算符是必不可少的。位運算符用來對二進制位進行操作,包括:按位與(&)、按位或(|)、按位異

python學習--運算

命令行 .exe opera 地址 span als div 等於 nbsp 一、簡介 1.python 運算符分為 算術運算符 比較(關系)運算符 賦值運算符 邏輯運算符 位運算符 成員運算符 身份運算符 運算符優先級 2.運算優先級    **

C# 之 static的用法

c const 一次 訪問 分配 focus rgs 改變 class 數據 一、靜態類   靜態類與非靜態類的重要區別在於靜態類不能實例化,也就是說,不能使用 new 關鍵字創建靜態類類型的變量。在聲明一個類時使用static關鍵字,具有兩個方面的意義:首先,它防止程序員

Java學習——使用Static修飾

pri img style stub oid .get 修飾 eth 增加 這是原來的 class StaticDemo { static int x; int y; public static int getX() { return x;//靜態方法中可以訪

C/C++——static修飾

  1. static變數   2. static函式 當一個源程式由多個原始檔組成時,C語言根據函式能否被其它原始檔中的函式呼叫,將函式分為內部函式和外部函式。  內部函式(又稱靜態函式)  如果在一個原始檔中定義的函式,只能被本檔案中的函式呼叫,而不能被同一程式其它檔案中的函式

Android Gradle manifestPlaceholders 佔位

https://www.cnblogs.com/zhaohongtian/p/6808962.html   在實際專案中,AndroidManifest裡十幾個地方的值是需要動態的改變(生成apk檔案的時候).如果每次去改也可以,但是累啊,在我之前他們打包是用手動替換,但我