教你一招,快速掌握java static關鍵字的用法

[java]static關鍵字的四種用法

在java的關鍵字中,staticfinal是兩個我們必須掌握的關鍵字。不同於其他關鍵字,他們都有多種用法,而且在一定環境下使用,可以提高程序的運行性能,優化程序的結構。下面我們先來了解一下static關鍵字及其用法。

static關鍵字

1.修飾成員變量

在我們平時的使用當中,static最常用的功能就是修飾類的屬性和方法,讓他們成為類的成員屬性和方法,我們通常將用static修飾的成員稱為類成員或者靜態成員,這句話挺起來都點奇怪,其實這是相對於對象的屬性和方法來說的。請看下面的例子:(未避免程序太過臃腫,暫時不管訪問控制)

教你一招,快速掌握java  static關鍵字的用法

public class Person {
String name;
int age;

public String toString() {
return "Name:" + name + ", Age:" + age;
}

public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Name:zhangsan, Age:10
* Name:lisi, Age:12
*///~
}
教你一招,快速掌握java  static關鍵字的用法

上面的代碼我們很熟悉,根據Person構造出的每一個對象都是獨立存在的,保存有自己獨立的成員變量,相互不會影響,他們在內存中的示意如下:

教你一招,快速掌握java  static關鍵字的用法

從上圖中可以看出,p1和p2兩個變量引用的對象分別存儲在內存中堆區域的不同地址中,所以他們之間相互不會干擾。但其實,在這當中,我們省略了一些重要信息,相信大家也都會想到,對象的成員屬性都在這了,由每個對象自己保存,那麼他們的方法呢?實際上,不論一個類創建了幾個對象,他們的方法都是一樣的:

教你一招,快速掌握java  static關鍵字的用法

從上面的圖中我們可以看到,兩個Person對象的方法實際上只是指向了同一個方法定義。這個方法定義是位於內存中的一塊不變區域(由jvm劃分),我們暫稱它為靜態存儲區。這一塊存儲區不僅存放了方法的定義,實際上從更大的角度而言,它存放的是各種類的定義,當我們通過new來生成對象時,會根據這裡定義的類的定義去創建對象。多個對象僅會對應同一個方法,這裡有一個讓我們充分信服的理由,那就是不管多少的對象,他們的方法總是相同的,儘管最後的輸出會有所不同,但是方法總是會按照我們預想的結果去操作,即不同的對象去調用同一個方法,結果會不盡相同。

我們知道,static關鍵字可以修飾成員變量和方法,來讓它們變成類的所屬,而不是對象的所屬,比如我們將Person的age屬性用static進行修飾,結果會是什麼樣呢?請看下面的例子:

教你一招,快速掌握java  static關鍵字的用法

public class Person {
String name;
static int age;

/* 其餘代碼不變... */
/**Output
* Name:zhangsan, Age:12
* Name:lisi, Age:12
*///~

}
教你一招,快速掌握java  static關鍵字的用法

我們發現,結果發生了一點變化,在給p2的age屬性賦值時,干擾了p1的age屬性,這是為什麼呢?我們還是來看他們在內存中的示意:

教你一招,快速掌握java  static關鍵字的用法

我們發現,給age屬性加了static關鍵字之後,Person對象就不再擁有age屬性了,age屬性會統一交給Person類去管理,即多個Person對象只會對應一個age屬性,一個對象如果對age屬性做了改變,其他的對象都會受到影響。我們看到此時的age和toString()方法一樣,都是交由類去管理。

雖然我們看到static可以讓對象共享屬性,但是實際中我們很少這麼用,也不推薦這麼使用。因為這樣會讓該屬性變得難以控制,因為它在任何地方都有可能被改變。如果我們想共享屬性,一般我們會採用其他的辦法:

教你一招,快速掌握java  static關鍵字的用法

public class Person {
private static int count = 0;
int id;
String name;
int age;

public Person() {
id = ++count;
}

public String toString() {
return "Id:" + id + ", Name:" + name + ", Age:" + age;
}

public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Id:1, Name:zhangsan, Age:10
* Id:2, Name:lisi, Age:12
*///~
}
教你一招,快速掌握java  static關鍵字的用法

上面的代碼起到了給Person的對象創建一個唯一id以及記錄總數的作用,其中count由static修飾,是Person類的成員屬性,每次創建一個Person對象,就會使該屬性自加1然後賦給對象的id屬性,這樣,count屬性記錄了創建Person對象的總數,由於count使用了private修飾,所以從類外面無法隨意改變。

2.修飾成員方法

static的另一個作用,就是修飾成員方法。相比於修飾成員屬性,修飾成員方法對於數據的存儲上面並沒有多大的變化,因為我們從上面可以看出,方法本來就是存放在類的定義當中的。static修飾成員方法最大的作用,就是可以使用"類名.方法名"的方式操作方法,避免了先要new出對象的繁瑣和資源消耗,我們可能會經常在幫助類中看到它的使用:

教你一招,快速掌握java  static關鍵字的用法

public class PrintHelper {
public static void print(Object o){
System.out.println(o);
}

public static void main(String[] args) {
PrintHelper.print("Hello world");
}
}
教你一招,快速掌握java  static關鍵字的用法

上面便是一個例子(現在還不太實用),但是我們可以看到它的作用,使得static修飾的方法成為類的方法,使用時通過“類名.方法名

”的方式就可以方便的使用了,相當於定義了一個全局的函數(只要導入該類所在的包即可)。不過它也有使用的侷限,一個static修飾的類中,不能使用非static修飾的成員變量和方法,這很好理解,因為static修飾的方法是屬於類的,如果去直接使用對象的成員變量,它會不知所措(不知該使用哪一個對象的屬性)。

3.靜態塊

在說明static關鍵字的第三個用法時,我們有必要重新梳理一下一個對象的初始化過程。以下面的代碼為例:

教你一招,快速掌握java  static關鍵字的用法

package com.dotgua.study;
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2 = new Book("static成員book2成員變量初始化");

public Person(String msg) {
System.out.println(msg);
}

Book book3 = new Book("book3成員變量初始化");
static Book book4 = new Book("static成員book4成員變量初始化");

public static void main(String[] args) {
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}
教你一招,快速掌握java  static關鍵字的用法

上面的例子中,Person類中組合了四個Book成員變量,兩個是普通成員,兩個是static修飾的類成員。我們可以看到,當我們new一個Person對象時,static修飾的成員變量首先被初始化,隨後是普通成員,最後調用Person類的構造方法完成初始化。也就是說,在創建對象時,static修飾的成員會首先被初始化,而且我們還可以看到,如果有多個static修飾的成員,那麼會按照他們的先後位置進行初始化。

實際上,static修飾的成員的初始化可以更早的進行,請看下面的例子:

教你一招,快速掌握java  static關鍵字的用法

class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2 = new Book("static成員book2成員變量初始化");

public Person(String msg) {
System.out.println(msg);
}

Book book3 = new Book("book3成員變量初始化");
static Book book4 = new Book("static成員book4成員變量初始化");

public static void funStatic() {
System.out.println("static修飾的funStatic方法");
}

public static void main(String[] args) {
Person.funStatic();
System.out.println("****************");
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* static修飾的funStatic方法
* ***************
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}
教你一招,快速掌握java  static關鍵字的用法

在上面的例子中我們可以發現兩個有意思的地方,第一個是當我們沒有創建對象,而是通過類去調用類方法時,儘管該方法沒有使用到任何的類成員,類成員還是在方法調用之前就初始化了,這說明,當我們第一次去使用一個類時,就會觸發該類的成員初始化。第二個是當我們使用了類方法,完成類的成員的初始化後,再new該類的對象時,static修飾的類成員沒有再次初始化,這說明,static修飾的類成員,在程序運行過程中,只需要初始化一次即可,不會進行多次的初始化。

回顧了對象的初始化以後,我們再來看static的第三個作用就非常簡單了,那就是當我們初始化static修飾的成員時,可以將他們統一放在一個以static開始,用花括號包裹起來的塊狀語句中:

教你一招,快速掌握java  static關鍵字的用法

class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2;

static {
book2 = new Book("static成員book2成員變量初始化");
book4 = new Book("static成員book4成員變量初始化");
}

public Person(String msg) {
System.out.println(msg);
}

Book book3 = new Book("book3成員變量初始化");
static Book book4;

public static void funStatic() {
System.out.println("static修飾的funStatic方法");
}

public static void main(String[] args) {
Person.funStatic();
System.out.println("****************");
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* static修飾的funStatic方法
* ***************
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}

教你一招,快速掌握java  static關鍵字的用法

我們將上一個例子稍微做了一下修改,可以看到,結果沒有二致。

4.靜態導包

相比於上面的三種用途,第四種用途可能瞭解的人就比較少了,但是實際上它很簡單,而且在調用類方法時會更方便。以上面的“PrintHelper”的例子為例,做一下稍微的變化,即可使用靜態導包帶給我們的方便:

教你一招,快速掌握java  static關鍵字的用法

/* PrintHelper.java文件 */
package com.dotgua.study;
public class PrintHelper {
public static void print(Object o){
System.out.println(o);
}
}
教你一招,快速掌握java  static關鍵字的用法

教你一招,快速掌握java  static關鍵字的用法

/* App.java文件 */
import static com.dotgua.study.PrintHelper.*;
public class App
{
public static void main( String[] args )
{
print("Hello World!");
}
/**Output
* Hello World!
*///~
}
教你一招,快速掌握java  static關鍵字的用法

上面的代碼來自於兩個java文件,其中的PrintHelper很簡單,包含了一個用於打印的static方法。而在App.java文件中,我們首先將PrintHelper類導入,這裡在導入時,我們使用了static關鍵字,而且在引入類的最後還加上了“.*”,它的作用就是將PrintHelper類中的所有類方法直接導入。不同於非static導入,採用static導入包後,在不與當前類的方法名衝突的情況下,無需使用“類名.方法名”的方法去調用類方法了,直接可以採用"方法名"去調用類方法,就好像是該類自己的方法一樣使用即可。

總結

static是java中非常重要的一個關鍵字,而且它的用法也很豐富,主要有四種用法:

  1. 用來修飾成員變量,將其變為類的成員,從而實現所有對象對於該成員的共享;
  2. 用來修飾成員方法,將其變為類方法,可以直接使用“類名.方法名”的方式調用,常用於工具類;
  3. 靜態塊用法,將多個類成員放在一起初始化,使得程序更加規整,其中理解對象的初始化過程非常關鍵;
  4. 靜態導包用法,將類的方法直接導入到當前類中,從而直接使用“方法名”即可調用類方法,更加方便。

依照


分享到:


相關文章: