Java 關鍵字static詳解

一、static基本描述

對於關鍵字static ,大家應該不會陌生,因爲一直編寫:

  public static void main(String args[]) {...}

那static表示什麼呢?static表示“全局”或者“靜態”的意思,用來修飾成員變量和成員方法,也可以形成靜態static代碼塊,但是要注意一點:Java語言中沒有全局變量的概念。

二、使用static定義屬性

我們先來看一個表示圖書的操作類,其中所有書的出版社都是清華出版社。

package com.wz.thisdemo;

class Book {
    private String title;
    private double price;
    String pub = "清華大學出版社"; // 此處暫時不封裝

    public Book(String title, double price) {
        super();
        this.title = title;
        this.price = price;
    }


    public String getInfo() {
        return "書名:" + this.title + ",價格:" + this.price + ",出版社:" + this.pub;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Book ba = new Book("Java開發",10.2);
        Book bb = new Book("Android開發",11.2);
        Book bc = new Book("Oracle開發",12.2);

        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

運行結果:

書名:Java開發,價格:10.2,出版社:清華大學出版社
書名:Android開發,價格:11.2,出版社:清華大學出版社
書名:Oracle開發,價格:12.2,出版社:清華大學出版社

內存分配圖如下:

1

那麼問題來了,既然這個類中所有的圖書的出版社都是同一個,那麼每個圖示對象還有必要佔有重複的屬性信息麼?

假設有1000萬個Book類對象,要求將所有對象的出版社名稱更換,意味着什麼?
我們看代碼,在主方法中添加如下:

public class TestDemo {
    public static void main(String args[]) {
        Book ba = new Book("Java開發",10.2);
        Book bb = new Book("Android開發",11.2);
        Book bc = new Book("Oracle開發",12.2);

        ba.pub = "北京大學出版社";
        bb.pub = "浙江大學出版社";

        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

運行結果:

書名:Java開發,價格:10.2,出版社:北京大學出版社
書名:Android開發,價格:11.2,出版社:浙江大學出版社
書名:Oracle開發,價格:12.2,出版社:清華大學出版社

這意味着要修改1000萬個對象的內容!所以,如果將對象的屬性定義爲普通屬性,這就意味着每一塊堆內存空間都將保存着各自的信息。

既然每個Book類對象的pub內容是一樣的,那麼應該將其定義爲一個共享的屬性,即所有的對象都共享pub屬性。於是,在這種情況下爲了描述共享的概念,可以使用static來定義需要共享的這個屬性。
在Book類的屬性pub上使用static修飾:

    static String pub = "清華大學出版社"; 

主方法爲:

public class TestDemo {
    public static void main(String args[]) {
        Book ba = new Book("Java開發",10.2);
        Book bb = new Book("Android開發",11.2);
        Book bc = new Book("Oracle開發",12.2);

        ba.pub = "北京大學出版社";

        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

運行結果:

書名:Java開發,價格:10.2,出版社:北京大學出版社
書名:Android開發,價格:11.2,出版社:北京大學出版社
書名:Oracle開發,價格:12.2,出版社:北京大學出版社

我們發現,一旦屬性定義上使用了static 之後,只要有一個對象修改了屬性的內容,那麼所有的對象的這個static屬性的內容都一起進行修改。此時,pub屬性就成了一個公共屬性。
於是,內存分配圖也變成了如下這樣:

2

static聲明的屬性與普通屬性(非static)最大的區別就是:保存內存區域的不同。

還有一個問題:既然static定義的屬性是公共屬性,那麼如果只是簡單的由一個對象去修改這個屬性的做法是不合適的。合適的做法就是由所有對象的公共代表 — 類來修改,通過“類名稱.static屬性”的方式來完成。
所以以上的:

ba.pub = "北京大學出版社";

最好用

Book.pub = "北京大學出版社";

來代替。

通過以上說明, 我們應該清楚以下幾點:
(1)使用static定義的屬性不在堆內存之中保存,保存在全局數據區;
(2)使用static定義的屬性表示類屬性,類屬性可以由類名稱直接進行調用;
(3)static屬性雖然定義在類中,但是其可以在沒有實例化對象的時候進行調用(普通屬性保存在堆內存裏,而static屬性保存在全局數據區之中);

那麼在定義屬性的時候,什麼時候使用static定義?什麼時候不使用static定義呢?
在開發中,儘量不使用static定義屬性,如果需要描述共享信息的時候,才使用static定義()主要是爲了方便集體修改,且不重複分配內存空間)。

三、使用static定義方法

除了使用static定義屬性之外,方法上也可以使用static進行定義,那麼很明顯,使用static定義的方法也可以在沒有實例化對象產生的情況下由類名稱直接進行調用。
來看一段代碼:

package com.wz.thisdemo;

class Book {
    private String title;
    private double price;
    private static String pub = "清華大學出版社"; 

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    //static方法
    public static void setPub(String p){
        pub = p;
    }

    public String getInfo() {
        return "書名:" + this.title + ",價格:" + this.price + ",出版社:" + this.pub;
    }
}

public class TestDemo {
    public static void main(String args[]) {

        //在對象產生前進行調用
        Book.setPub("北京大學出版社");

        Book ba = new Book("Java開發",10.2);
        Book bb = new Book("Android開發",11.2);
        Book bc = new Book("Oracle開發",12.2);

        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

運行結果:

書名:Java開發,價格:10.2,出版社:北京大學出版社
書名:Android開發,價格:11.2,出版社:北京大學出版社
書名:Oracle開發,價格:12.2,出版社:北京大學出版社

但是問題來了,類中的方法有兩種,一種是static方法,一種是非static方法,這兩種方法間的訪問將受到限制!
(1) static方法不能直接調用非static的方法或屬性;
(2)非static方法可以調用static的屬性或方法,不受限制。

可是爲什麼會有這樣的限制呢?
使用static定義的屬性和方法,可以在沒有實例化對象的時候使用;而非static定義的屬性和方法,必須實例化對象之後纔可以進行調用。

那麼,我們時候使用static方法?什麼時候使用static方法呢?
如果一個類中沒有任何屬性,只有方法,建議可以將所有的方法定義爲static方法,這樣每次調用方法的時候就可以不進行對象實例化了。在實際開發中,建議優先考慮非static方法和非static屬性。

四、Java 主方法

Java方法定義時,如果一個方法在主類之中定義,並且由主方法直接調用的時候,那麼前面必須有public static,格式如下:

public static 返回值類型 方法名稱 (參數列表) {
         [return [返回值] ;]
}

觀察以下代碼:

package com.wz.thisdemo;

public class TestDemo {
    public static void main(String args[]) {
              print() ;       // 直接調用
    }
    public static void print() {
              System.out.println("Hello World .") ;
    }
}

運行結果:

Hello World .

此時,表示的是一個static方法調用其他的static方法,但是如果這個print()方法上沒有static呢?

package com.wz.thisdemo;

public class TestDemo {
    public static void main(String args[]) {
              print() ;       // 直接調用
    }
    public void print() {
              System.out.println("Hello World .") ;
    }
}

運行結果:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Cannot make a static reference to the non-static method print() from the type TestDemo

    at com.wz.thisdemo.TestDemo.main(TestDemo.java:5)

所有的非static方法幾乎都有一個特點:方法要由實例化對象調用。正確寫法如下:

package com.wz.thisdemo;

public class TestDemo {
    public static void main(String args[]) {
              new TestDemo().print() ;       // 對象調用
    }
    public void print() {      // 非static方法
              System.out.println("Hello World .") ;
    }
}

運行結果:

Hello World .

現在,來觀察主方法的組成:

    public static void main(String args[]) {...}

可以發現裏面有很多的關鍵字:
(1)public:是一種訪問權限,表示公共;
(2)static:此方法由類名稱直接調用;
(3)void:主方法是程序執行的開始;
(4)main:系統規定的一個方法名稱,執行類的時候默認找到此名稱,不能修改;
(5)String args[]:表示程序運行時傳遞的參數,通過字符串接收。

五、static使用實例

統計一個類產生的實例化對象個數
分析: 一個類肯定可以有多個實例化對象產生,增加一個統計操作,可以統計出一個類之中的所產生的實例化對象的個數。如果現在要產生新的實例化對象,則一定會調用類中的構造方法,所以可以在構造方法中增加統計,而且這個統計的變量是所有對象共享的,應該將其定義爲static屬性。

package com.wz.thisdemo;

class Obj{
    private static int count = 0;

    public Obj(){
        System.out.println("count = " + ++count);
    }
}

public class TestDemo {
    public static void main(String args[]) {

        new Obj();
        new Obj();
        new Obj();
        new Obj();
        new Obj();
    }
}

運行結果:

count = 1
count = 2
count = 3
count = 4
count = 5
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章