天天看點

Java關鍵字(三)——static

  我們說Java是一種面向對象程式設計的語言,而對象是把資料及對資料的操作方法放在一起,作為一個互相依存的整體,對同類對象抽象出其共性,便是Java中的類,我們可以用類描述世間萬物,也可以說萬物皆對象。但是這裡有個特殊的東西——static,它不屬于對象,那麼為什麼呢?

  static 是Java的一個關鍵字,可以用來修飾成員變量、修飾成員方法、構造靜态代碼塊、實作靜态導報以及實作靜态内部類,下面我們來分别介紹。

1、修飾成員變量

  用 static 修飾成員變量可以說是該關鍵字最常用的一個功能,通常将用 static 修飾的成員變量稱為類成員或者靜态成員,那麼靜态成員和不用 static 修飾的非靜态成員有什麼差別呢?

  我們先看看不用 static 修飾的成員變量在記憶體中的構造。

Java關鍵字(三)——static
Java關鍵字(三)——static
1 package com.ys.bean;
 2 
 3 /**
 4  * Create by YSOcean
 5  */
 6 public class Person {
 7     private String name;
 8     private Integer age;
 9 
10     public Person(String name, Integer age) {
11         this.name = name;
12         this.age = age;
13     }
14 
15     @Override
16     public String toString() {
17         return "Person{" +
18                 "name='" + name + '\'' +
19                 ", age=" + age +
20                 '}';
21     }
22     //get和set方法省略
23 }      

View Code

  首先,我們建立一個實體類 Person,有兩個屬性 name 和 age,都是普通成員變量(沒有用 static 關鍵字修飾),接着我們通過其構造方法建立兩個對象:

1 Person p1 = new Person("Tom",21);
2 Person p2 = new Person("Marry",20);
3 System.out.println(p1.toString());//Person{name='Tom', age=21}
4 System.out.println(p2.toString());//Person{name='Marry', age=20}      

  這兩個對象在記憶體中的存儲結構如下:

  

Java關鍵字(三)——static

  由上圖可知,我們建立的兩個對象 p1 和 p2  存儲在堆中,但是其引用位址是存放在棧中的,而且這兩個對象的兩個變量互相獨立,我們修改任何一個對象的屬性值,是不改變另外一個對象的屬性值的。

  下面我們将 Person 類中的 age 屬性改為由 static 關鍵字修飾:

Java關鍵字(三)——static
Java關鍵字(三)——static
1 package com.ys.bean;
 2 
 3 /**
 4  * Create by YSOcean
 5  */
 6 public class Person {
 7     private  String name;
 8     private static Integer age;
 9 
10     public Person(String name, Integer age) {
11         this.name = name;
12         this.age = age;
13     }
14 
15     @Override
16     public String toString() {
17         return "Person{" +
18                 "name='" + name + '\'' +
19                 ", age=" + age +
20                 '}';
21     }
22     //get和set方法省略
23 
24 }      

  同樣我們還是向上面一樣,建立 p1 和 p2 兩個對象,并列印這兩個對象,看看和上面列印的有啥差別呢?

1 Person p1 = new Person("Tom",21);
2 Person p2 = new Person("Marry",20);
3 System.out.println(p1.toString());//Person{name='Tom', age=20}
4 System.out.println(p2.toString());//Person{name='Marry', age=20}      

  我們發現第三行代碼列印的 p1 對象 age 屬性變為 20了,這是為什麼呢?

Java關鍵字(三)——static

  這是因為用在 jvm 的記憶體構造中,會在堆中開辟一塊記憶體空間,專門用來存儲用 static 修飾的成員變量,稱為靜态存儲區,無論我們建立多少個對象,用 static 修飾的成員變量有且隻有一份存儲在靜态存儲區中,是以該靜态變量的值是以最後建立對象時設定該靜态變量的值為準,也就是由于 p1 先設定 age = 21,後來建立了 p2 對象,p2将 age 改為了20,那麼該靜态存儲區的 age 屬性值也被修改成了20。

  PS:在 JDK1.8 以前,靜态存儲區是存放在方法區的,而方法區不屬于堆,在 JDK1.8 之後,才将方法區幹掉了,方法區中的靜态存儲區改為到堆中存儲。

  總結:static 修飾的變量被所有對象所共享,在記憶體中隻有一個副本。由于與對象無關,是以我們可以直接通過 類名.靜态變量 的方式來直接調用靜态變量。對應的非靜态變量是對象所擁有的,多少個對象就有多少個非靜态變量,各個對象所擁有的副本不受影響。

2、修飾修飾成員方法

  用 static 關鍵字修飾成員方法也是一樣的道理,我們可以直接通過 類名.靜态方法名() 的方式來調用,而不用建立對象。

Java關鍵字(三)——static
Java關鍵字(三)——static
1 public class Person {
 2     private  String name;
 3     private static Integer age;
 4 
 5     public static void printClassName(){
 6         System.out.println("com.ys.bean.Person");
 7     }
 8     public Person(String name, Integer age) {
 9         this.name = name;
10         this.age = age;
11     }
12 
13     @Override
14     public String toString() {
15         return "Person{" +
16                 "name='" + name + '\'' +
17                 ", age=" + age +
18                 '}';
19     }
20     //get和set方法省略
21 
22 }      

  調用靜态方法:

1 Person.printClassName();//com.ys.bean.Person      

3、靜态代碼塊

  用 static 修飾的代碼塊稱為靜态代碼塊,靜态代碼塊可以置于類的任意一個地方(和成員變量成員方法同等地位,不可放入方法中),并且一個類可以有多個靜态代碼塊,在類初次載入記憶體時加載靜态代碼塊,并且按照聲明靜态代碼塊的順序來加載,且僅加載一次,優先于各種代碼塊以及構造函數。

  關于靜态代碼塊、構造代碼塊、構造函數、普通代碼塊的差別可以參考我的

這篇部落格

1 public class CodeBlock {
2     static{
3         System.out.println("靜态代碼塊");
4     }
5 }      

  由于靜态代碼塊隻在類載入記憶體時加載一次的特性,我們可以利用靜态代碼塊來優化程式性能,比如某個比較大配置檔案需要在建立對象時加載,這時候為了節省記憶體,我們可以将該配置檔案的加載時機放入到靜态代碼塊中,那麼我們無論建立多少個對象時,該配置檔案也隻加載了一次。

4、靜态導包

  用 static 來修飾成員變量,成員方法,以及靜态代碼塊是最常用的三個功能,靜态導包是 JDK1.5以後的新特性,用 import static 包名 來代替傳統的 import 包名 方式。那麼有什麼用呢?

  比如我們建立一個數組,然後用 JDK 自帶的 Arrays 工具類的 sort 方法來對數組進行排序:

1 package com.ys.test;
 2 
 3 import java.util.Arrays;
 4 /**
 5  * Create by YSOcean
 6  */
 7 public class StaticTest {
 8 
 9     public static void main(String[] args) {
10         int[] arrays = {3,4,2,8,1,9};
11         Arrays.sort(arrays);
12     }
13 }      

  我們可以看到,調用 sort 方法時,需要進行 import java.util.Arrays 的導包操作,那麼變為靜态導包呢?

1 package com.ys.test;
 2 
 3 import static java.util.Arrays.*;
 4 /**
 5  * Create by YSOcean
 6  */
 7 public class StaticTest {
 8 
 9     public static void main(String[] args) {
10         int[] arrays = {3,4,2,8,1,9};
11         sort(arrays);
12     }
13 }      

  我們可以看到第三行代碼的 import java.util.Arrays 變為了 import static  java.util.Arrays.*,意思是導入 Arrays 類中的所有靜态方法,當然你也可以将 * 變為某個方法名,也就是隻導入該方法,那麼我們在調用該方法時,就可以不帶上類名,直接通過方法名來調用(第 11 行代碼)。

  靜态導包隻會減少程式員的代碼編寫量,對于性能是沒有任何提升的(也不會降低性能,Java核心技術第10版卷1第148頁4.7.1章節類的導入有介紹),反而會降低代碼的可讀性,是以實際如何使用需要權衡。

5、靜态内部類

  首先我們要知道什麼是内部類,定義在一個類的内部的類叫内部類,包含内部類的類叫外部類,内部類用 static 修飾便是我們所說的靜态内部類。

  定義内部類的好處是外部類可以通路内部類的所有方法和屬性,包括私有方法和私有屬性。

  通路普通内部類,我們需要先建立外部類的對象,然後通過外部類名.new 建立内部類的執行個體。

1 package com.ys.bean;
 2 
 3 /**
 4  * Create by hadoop
 5  */
 6 public class OutClass {
 7 
 8     public class InnerClass{
 9 
10     }
11 }      
1  * OuterClass oc = new OuterClass();
2  * OuterClass.InnerClass in = oc.new InnerClass();      

  通路靜态内部類,我們不需要建立外部類的對象,可以直接通過 外部類名.内部類名 來建立執行個體。

1 package com.ys.bean;
 2 
 3 /**
 4  * Create by hadoop
 5  */
 6 public class OutClass {
 7 
 8     public static class InnerClass{
 9 
10     }
11 }      
1 OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();      

6、常見問題

  ①、靜态變量能存在于普通方法中嗎?

  能。很明顯,普通方法必須通過對象來調用,靜态變量都可以直接通過類名來調用了,更不用說通過對象來調用,是以是可以存在于普通方法中的。

  ②、靜态方法能存在普通變量嗎?

  不能。因為靜态方法可以直接通過類名來直接調用,不用建立對象,而普通變量是必須通過對象來調用的。那麼将普通變量放在靜态方法中,在直接通過類來調用靜态方法時就會報錯。是以不能。

  ③、靜态代碼塊能放在方法體中嗎?

  不能。首先我們要明确靜态代碼塊是在類加載的時候自動運作的。

  普通方法需要我們建立對象,然後手工去調用方法,所靜态代碼塊不能聲明在普通方法中。

  那麼對于用 static 修飾的靜态方法呢?同樣也是不能的。因為靜态方法同樣也需要我們手工通過類名來調用,而不是直接在類加載的時候就運作了。

  也就是說靜态代碼塊能夠自動執行,而不管是普通方法還是靜态方法都是需要手工執行的。

  ④、靜态導包會比普通導包消耗更多的性能?

  不會。靜态導包實際上在編譯期間都會被編譯器進行處理,将其轉換成普通按需導包的形式,是以在程式運作期間是不影響性能的。

  ⑤、static 可以用來修飾局部變量嗎?

  不能。不管是在普通方法還是在靜态方法中,static 關鍵字都不能用來修飾局部變量,這是Java的規定。稍微想想也能明白,局部變量的聲明周期是随着方法的結束而結束的,因為static 修飾的變量是全局的,不與對象有關的,如果用 static 修飾局部變量容易造成了解上的沖突,是以Java規定 static 關鍵字不能用來修飾局部變量。

作者:

YSOcean

出處:

http://www.cnblogs.com/ysocean/

本文版權歸作者所有,歡迎轉載,但未經作者同意不能轉載,否則保留追究法律責任的權利。