Java技術——Java中的static關鍵字解析

0. 前言  java

staticJava中的重要的一個點。也是面試的時候常常被問到的點,若是理解不夠很容易給面試官語言基礎不紮實的印象。本文從static方法、static內部類、static變量、以及static代碼塊四個角度分別解析static關鍵字。轉載請註明出處爲SEU_Calvin的博客面試

 

1.  static方法編程

Java編程思想》裏有這麼一句話——「static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是能夠的。並且能夠在沒有建立任何對象的前提下,僅僅經過類自己來調用static方法。這實際上正是static方法的主要用途。」函數

 

static方法通常稱做靜態方法,因爲靜態方法不依賴於任何對象、僅經過類名就能夠進行訪問,前提是類被加載。也所以在靜態方法中不能訪問類的非靜態成員方法/變量,由於非靜態成員方法/變量都是必須依賴具體的對象纔可以被調用,若是經過類名調用靜態方法,而該方法內部有非靜態變量,此時對象都尚未建立,就會產生錯誤,所以Java設置了這樣的限制。固然反過來,在非靜態成員方法中是能夠訪問靜態成員方法/變量的性能

 

咱們最多見的static方法就是main方法,另外還有,即便沒有顯示地聲明爲static類的構造器實際上也是靜態方法。優化

還有就是須要注意,若是你不必訪問對象外部,那麼就把你的方法成爲靜態方法,由於它會比實例方法更快的調用(後者爲了實現多態需維護一個虛擬函數導向表)ui

 

2.  static內部類this

靜態內部類和非靜態內部類是咱們在開發中都常常用到的,那麼二者之間到底有什麼不一樣呢?spa

這裏主要總結一下二者的區別,順便提出在使用static內部類時須要注意的一些性質:.net

1內部靜態類不須要有指向外部類的引用,但非靜態內部類須要持有對外部類的引用。這也是不少非靜態內部類常常默認Android Activity外部類的引用,從而間接致使內存泄漏的緣由。

2)非靜態內部類可以訪問外部類的靜態和非靜態成員,顯然一個非靜態內部類不能脫離外部類實體被建立,而靜態類不能訪問外部類的非靜態成員,它只能訪問外部類的靜態成員。這一點和上面static方法的性質相似。

 

3.  static變量

一樣介紹靜態變量和非靜態變量的區別:

靜態變量被全部的對象所共享,在內存中只有一個副本,它當且僅當在類初次加載時會被初始化。而非靜態變量是對象所擁有的,在建立對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。靜態成員變量雖然獨立於對象,可是不表明不能夠經過對象去訪問,全部的靜態方法和靜態變量均可以經過對象訪問

 

須要注意的是,不管是static方法仍是static變量,經過類名直接調用時,也會判斷該方法/變量是否被修飾爲private,若是是,仍然是沒法獲取到的,這說明static關鍵字沒法改變成員的訪問權限

 

4.  static代碼塊

首先看看下面程序會輸出什麼呢?

public class Test {
    static{ System.out.println("test static 1");}
    public static void main(String[] args) {}
    static{ System.out.println("test static 2");}
}

雖然在main方法中沒有任何語句,可是仍是會輸出"test static 1""test static 2"static塊能夠置於類中的任何地方,只要不是方法內部,類中也能夠有多個static塊。在類初次被加載的時候,會按照static塊的順序來執行每一個static塊,而且只會執行一次

根據只會執行一次的特性,靜態代碼塊能夠用以優化程序性能。實例以下:

class Person{
    private Date birthDate;
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
    boolean isBirthdaySuitable() {
        Date startDate = Date.valueOf("1990");
        Date endDate = Date.valueOf("1999");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) <= 0;
    }
}

這個實例用於判斷該Person是不是90後孤寡老人。每次isBirthdaySuitable()被調用的時候,都會生成startDateendDate兩個對象,形成了空間浪費,使用static靜態塊優化以下:

class Person{
    private Date birthDate;
private static Date startDate,endDate;
//一次性的初始化操做放在static代碼塊中進行
    static{
        startDate = Date.valueOf("1990");
        endDate = Date.valueOf("1999");
    }
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
    boolean isBirthdaySuitable () {
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) <=0;
    }
}

5.  static代碼塊的執行順序

先看看下面程序會輸出什麼?

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}

咱們來分析一下這段代碼的執行過程:

1)首先加載Test類,所以會先執行Test類中的static

2)接着執行main函數中的newMyClass(),而MyClass類尚未被加載,所以須要加載MyClass。在加載MyClass類的時候,發現MyClass類繼承自Test類,可是因爲Test類已經被加載過了,因此只須要加載MyClass類,那麼就會執行MyClass類的中的static

3)在加載完以後,就經過構造器來生成對象。而在生成對象的時候,必須先初始化父類的成員變量,所以會執行Test中的Personperson = new Person(),而Person類尚未被加載,所以會先加載Person類並執行Person類中的static塊,接着執行父類的構造器,完成了父類的初始化

4)最後初始化MyClass,所以會先接着執行MyClass中的Person person = new Person()最後執行MyClass的構造器

相關文章
相關標籤/搜索