Java關鍵字(三)——static

  咱們說Java是一種面向對象編程的語言,而對象是把數據及對數據的操做方法放在一塊兒,做爲一個相互依存的總體,對同類對象抽象出其共性,即是Java中的類,咱們能夠用類描述世間萬物,也能夠說萬物皆對象。可是這裏有個特殊的東西——static,它不屬於對象,那麼爲何呢?html

  static 是Java的一個關鍵字,能夠用來修飾成員變量、修飾成員方法、構造靜態代碼塊、實現靜態導包以及實現靜態內部類,下面咱們來分別介紹。java

一、修飾成員變量

  用 static 修飾成員變量能夠說是該關鍵字最經常使用的一個功能,一般將用 static 修飾的成員變量稱爲類成員或者靜態成員,那麼靜態成員和不用 static 修飾的非靜態成員有什麼區別呢?程序員

  咱們先看看不用 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}

  這兩個對象在內存中的存儲結構以下:jvm

  

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

  下面咱們將 Person 類中的 age 屬性改成由 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 }
View Code

  一樣咱們仍是向上面同樣,建立 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了,這是爲何呢?oop

  

  這是由於用在 jvm 的內存構造中,會在堆中開闢一塊內存空間,專門用來存儲用 static 修飾的成員變量,稱爲靜態存儲區,不管咱們建立多少個對象,用 static 修飾的成員變量有且只有一份存儲在靜態存儲區中,因此該靜態變量的值是以最後建立對象時設置該靜態變量的值爲準,也就是因爲 p1 先設置 age = 21,後來建立了 p2 對象,p2將 age 改成了20,那麼該靜態存儲區的 age 屬性值也被修改爲了20。

  PS:在 JDK1.8 之前,靜態存儲區是存放在方法區的,而方法區不屬於堆,在 JDK1.8 以後,纔將方法區幹掉了,方法區中的靜態存儲區改成到堆中存儲。

  總結:static 修飾的變量被全部對象所共享,在內存中只有一個副本。因爲與對象無關,因此咱們能夠直接經過 類名.靜態變量 的方式來直接調用靜態變量。對應的非靜態變量是對象所擁有的,多少個對象就有多少個非靜態變量,各個對象所擁有的副本不受影響。

二、修飾修飾成員方法

  用 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 }
View Code

  調用靜態方法:

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

三、靜態代碼塊

  用 static 修飾的代碼塊稱爲靜態代碼塊,靜態代碼塊能夠置於類的任意一個地方(和成員變量成員方法同等地位,不可放入方法中),而且一個類能夠有多個靜態代碼塊,在類初次載入內存時加載靜態代碼塊,而且按照聲明靜態代碼塊的順序來加載,且僅加載一次,優先於各類代碼塊以及構造函數。

  關於靜態代碼塊、構造代碼塊、構造函數、普通代碼塊的區別能夠參考個人這篇博客

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

  因爲靜態代碼塊只在類載入內存時加載一次的特性,咱們能夠利用靜態代碼塊來優化程序性能,好比某個比較大配置文件須要在建立對象時加載,這時候爲了節省內存,咱們能夠將該配置文件的加載時機放入到靜態代碼塊中,那麼咱們不管建立多少個對象時,該配置文件也只加載了一次。

四、靜態導包

  用 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章節類的導入有介紹),反而會下降代碼的可讀性,因此實際如何使用須要權衡。

五、靜態內部類

  首先咱們要知道什麼是內部類,定義在一個類的內部的類叫內部類,包含內部類的類叫外部類,內部類用 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();

六、常見問題

  ①、靜態變量能存在於普通方法中嗎?

  能。很明顯,普通方法必須經過對象來調用,靜態變量均可以直接經過類名來調用了,更不用說經過對象來調用,因此是能夠存在於普通方法中的。

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

  不能。由於靜態方法能夠直接經過類名來直接調用,不用建立對象,而普通變量是必須經過對象來調用的。那麼將普通變量放在靜態方法中,在直接經過類來調用靜態方法時就會報錯。因此不能。

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

  不能。首先咱們要明確靜態代碼塊是在類加載的時候自動運行的。

  普通方法須要咱們建立對象,而後手工去調用方法,所靜態代碼塊不能聲明在普通方法中。

  那麼對於用 static 修飾的靜態方法呢?一樣也是不能的。由於靜態方法一樣也須要咱們手工經過類名來調用,而不是直接在類加載的時候就運行了。

  也就是說靜態代碼塊可以自動執行,而不論是普通方法仍是靜態方法都是須要手工執行的。

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

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

  ⑤、static 能夠用來修飾局部變量嗎?

  不能。不論是在普通方法仍是在靜態方法中,static 關鍵字都不能用來修飾局部變量,這是Java的規定。稍微想一想也能明白,局部變量的聲明週期是隨着方法的結束而結束的,由於static 修飾的變量是全局的,不與對象有關的,若是用 static 修飾局部變量容易形成理解上的衝突,因此Java規定 static 關鍵字不能用來修飾局部變量。

相關文章
相關標籤/搜索