Java靜態變數 執行個體變數 靜態方法詳解

來源:互聯網
上載者:User

首先文法定義上的區別:靜態變數前要加static關鍵字,而執行個體變數前則不加。

在程式運行時的區別:執行個體變數屬於某個對象的屬性,必須建立了執行個體對象,其中的執行個體變數才會被分配空間,才能使用這個執行個體變數。靜態變數不屬於某個執行個體對象,而是屬於類,所以也稱為類變數,只要程式載入了類的位元組碼,不用建立任何執行個體對象,靜態變數就會被分配空間,靜態變數就可以被使用了。總之,執行個體變數必須建立對象後才可以通過這個對象來使用

 代碼如下 複製代碼


package staticVar;
 
 public class svar {
         public int var = 1000;
         public static void main(String[] args){
         svar svar = new svar();
         System.out.println("var =" + svar.var);
     }
 }

靜態變數則可以直接使用類名來引用

 代碼如下 複製代碼


package staticVar;
 
 public class svar {
         public static int var = 1000;
         public static void main(String[] args){
         System.out.println("var =" + svar.var);
     }
 }

類的靜態變數在記憶體中只有一個,java虛擬機器在載入類的過程中為靜態變數分配記憶體,靜態變數位於方法區,被類的所有執行個體共用。靜態變數可以直接通過類名進行訪問,其生命週期取決於類的生命週期。
而執行個體變數取決於類的執行個體。每建立一個執行個體,java虛擬機器就會為執行個體變數分配一次記憶體,執行個體變數位於堆區中,其生命週期取決於執行個體的生命週期。

靜態變數生命週期較長,而且不易被系統回收,因此如果不能合理地使用靜態變數,就會適得其反,造成大量的記憶體浪費,所謂過猶不及。因此,建議在具備下列全部條件的情況下,盡量使用靜態變數:

(1)變數所包含的對象體積較大,佔用記憶體較多。

(2)變數所包含的對象生命週期較長。

(3)變數所包含的對象資料穩定。

(4)該類的對象執行個體有對該變數所包含的對象的共用需求。

 

你可以將方法和變數都聲明為static。static 成員的最常見的 例子是main( ) 。因為在程式開始執行時必須調用main() ,所以它被聲明為static。 聲明為static的變數實質上就是全域變數。聲明為static的方法有以下幾條限制:  ·

A,它們僅能調用其他的static 方法

B,它們只能訪問static資料

C,它們不能以任何方式引用this 或super(this涉及到對象,super 與繼承有關)

  樣本:如果你需要通過計算來初始化你的static變數,你可以聲明一個static塊。Static 塊僅在該類被載入時執行一次。下面的例子顯示的類有一個static方法,一些static變
量,以及一個static 初始化塊。

 代碼如下 複製代碼


public class TestNew { 
    static int a = 3; 
    static int b; 
    static void meth(int x){ 
        System.out.println("x = "+x); 
        System.out.println("a = "+a); 
        System.out.println("b = "+b); 
    } 
    static { 
        System.out.println("static block initialized"); 
        b = a*4; 
    } 
    public static void main(String[] args) { 
        // TODO Auto-generated method stub 
        meth(42); 
    } 
}

執行結果是:

static block initialized
x = 42
a = 3
b = 12

上述class TestNew的執行順序是:首先,a被設定為3,接著static 塊執行(列印一條訊息),最後b被初始化為a*4 成12。然後調用main(),main () 調用meth() ,把值42傳遞給x。3個println ( ) 語句引用兩個static變數a和b,以及局部變數x 。

外部使用靜態變數或者靜態方法

  在定義它們的類的外面,static 方法和變數能獨立於任何對象而被使用,你只要在類的名字後面加點號運算子即可。可以看到,這種格式與通過對象引用變數調用非static方法或者變數的格 式類似。這就是Java 如何?全域功能和全域變數的一個控製版本。樣本:

 代碼如下 複製代碼

class StaticDemo{ 
    static int a = 42; 
    static int b = 99; 
    static void callme(){ 
        System.out.println("a = "+a); 
    } 

public class TestNew { 
    public static void main(String[] args) { 
        // TODO Auto-generated method stub 
        StaticDemo.callme(); 
        System.out.println("b = "+StaticDemo.b); 
    } 
}

執行結果:

a = 42
b = 99

靜態變數和執行個體變數到底有什麼區別

/**
 *
 */
package com.b510.test;

 代碼如下 複製代碼

/**
 * 在程式運行時的區別:執行個體變數屬於某個對象的屬性,必須建立了執行個體對象,<br>
 * 其中的執行個體變數才會被分配空間,才能使用這個執行個體變數。靜態變數不屬於某<br>
 * 個執行個體對象,而是屬於類,所以也稱為類變數,只要程式載入了類的位元組碼,<br>
 * 不用建立任何執行個體對象,靜態變數就會被分配空間,靜態變數就可以被使用了。<br>
 * 總之,執行個體變數必須建立對象後才可以通過這個對象來使用,靜態變數則可以<br>
 * 直接使用類名來引用。例如,對於下面的程式,無論建立多少個執行個體對象,<br>
 * 永遠都只分配了一個<code>staticInt</code>變數,並且每建立一個執行個體對象,<br>
 * 這個<code>staticInt</code>就會加1;但是,每建立一個執行個體對象,就會分配一個<code>random</code>,<br>
 * 即可能分配多個<code>random</code>,並且每個<code>random</code>的值都只自加了1次。<br>
 *
 * @author <a href="mailto:hongtenzone@foxmail.com">hongten</a>
 * @date 2013-3-2
 */
public class StaticTest {

    private static int staticInt = 2;
    private int random = 2;
   
    public StaticTest() {
        staticInt++;
        random++;
        System.out.println("staticInt = "+staticInt+"  random = "+random);
    }

    public static void main(String[] args) {
        StaticTest test = new StaticTest();
        StaticTest test2 = new StaticTest();
    }
}

安全執行緒問題之靜態變數、執行個體變數

靜態變數:線程非安全。
靜態變數即類變數,位於方法區,為所有對象共用,共用一份記憶體,一旦靜態變數被修改,其他對象均對修改可見,故線程非安全。

執行個體變數:單例模式(只有一個對象執行個體存在)線程非安全,非單例安全執行緒。

執行個體變數為對象執行個體私人,在虛擬機器的堆中分配,若在系統中只存在一個此對象的執行個體,在多線程環境下,“猶如”靜態變數那樣,被某個線程修改後,其他線程對修改均可見,故線程非安全;如果每個線程執行都是在不同的對象中,那對象與對象之間的執行個體變數的修改將互不影響,故安全執行緒。

 

 代碼如下 複製代碼
/** 
  * 安全執行緒問題類比執行 
  *  ------------------------------ 
  *       線程1      |    線程2 
  *  ------------------------------ 
  *   static_i = 4;  | 等待 
  *   static_i = 10; | 等待 
  *    等待          | static_i = 4; 
  *   static_i * 2;  | 等待 
  *  -----------------------------
 * */ 
public class Test implements Runnable 

    private static int static_i;//靜態變數  
     
    public void run() 
    { 
        static_i = 4; 
        System.out.println("[" + Thread.currentThread().getName() 
                + "]擷取static_i 的值:" + static_i); 
        static_i = 10; 
        System.out.println("[" + Thread.currentThread().getName() 
                + "]擷取static_i*3的值:" + static_i * 2); 
    } 
     
    public static void main(String[] args) 
    { 
        Test t = new Test(); 
        //啟動盡量多的線程才能很容易的類比問題  
        for (int i = 0; i < 3000; i++) 
        { 
            //t可以換成new Test(),保證每個線程都在不同的對象中執行,結果一樣  
            new Thread(t, "線程" + i).start(); 
        } 
    } 

 

 
根據代碼注釋中類比的情況,當線程1執行了static_i = 4;  static_i = 10; 後,線程2獲得執行權,static_i = 4; 然後當線程1獲得執行權執行static_i * 2;  必然輸出結果4*2=8,按照這個類比,我們可能會在控制台看到輸出為8的結果。
寫道
[線程27]擷取static_i 的值:4
[線程22]擷取static_i*2的值:20
[線程28]擷取static_i 的值:4
[線程23]擷取static_i*2的值:8
[線程29]擷取static_i 的值:4
[線程30]擷取static_i 的值:4
[線程31]擷取static_i 的值:4
[線程24]擷取static_i*2的值:20
 看紅色標註的部分,確實出現了我們的預想,同樣也證明了我們的結論。
 
執行個體變數安全執行緒問題類比:
----------------------------------------------------------------------------------

 代碼如下 複製代碼
Java代碼 
public class Test implements Runnable 

    private int instance_i;//執行個體變數 
     
    public void run() 
    { 
        instance_i = 4; 
        System.out.println("[" + Thread.currentThread().getName() 
                + "]擷取instance_i 的值:" + instance_i); 
        instance_i = 10; 
        System.out.println("[" + Thread.currentThread().getName() 
                + "]擷取instance_i*3的值:" + instance_i * 2); 
    } 
     
    public static void main(String[] args) 
    { 
        Test t = new Test(); 
        //啟動盡量多的線程才能很容易的類比問題  
        for (int i = 0; i < 3000; i++) 
        { 
            //每個線程對在對象t中運行,類比單例情況 
            new Thread(t, "線程" + i).start(); 
        } 
    } 

 


 
按照本文開頭的分析,猶如靜態變數那樣,每個線程都在修改同一個對象的執行個體變數,肯定會出現安全執行緒問題。
寫道
[線程66]擷取instance_i 的值:10
[線程33]擷取instance_i*2的值:20
[線程67]擷取instance_i 的值:4
[線程34]擷取instance_i*2的值:8
[線程35]擷取instance_i*2的值:20
[線程68]擷取instance_i 的值:4
 
看紅色字型,可知單例情況下,執行個體變數線程非安全。
 
將new Thread(t, "線程" + i).start();改成new Thread(new Test(), "線程" + i).start();類比非單例情況,會發現不存線上程安全問題

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.