靜態和成員的特點(Java)

爲了更加能方便的理解我們的靜態函數、變量和成員函數、變量,通過兩個問題來詳細描述一下我們靜態和成員的特點。

問題一:

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

        A a=new A();
    }
}

class A{
  
    A a=new A();    
}

我們發現平時在創建對象時,我們都是在實體類的外面創建該實體類的對象,現在是在該實體類的內部創建了一個對象,而且還是在該類的函數外面,這裏就相當於創建的對象用作該類的成員變量,我們先來看一下運行後的結果:

我們發現運行後,出現了了StackOverflowError這個錯誤,這叫棧溢出異常,之前在介紹遞歸的時候,提到過這個錯誤,就是因爲不斷調用自身而沒有函數的彈棧導致運行函數的棧內存滿了,就產生了這樣的錯誤。那麼在這裏是爲什麼會出現這樣的異常呢?我們在前面介紹構造函數的時候,提到對象的加載的流程,在主類中創建一個對象就相當於調用了該實體類A相應的構造函數,但是我們在該實體類A的構造函數外面創建的對象,當然在創建完對象之後,還要在堆內存中對相應的成員變量進行默認的初始化,而這裏在實體類A中創建的對象相當於該類的成員變量,因此在默認初始化的時候不斷的去創建對象再進行初始化,而導致的構造函數不斷進棧出現棧溢出錯誤。那麼如何避免這樣的錯誤出現呢,我們可以用init,因爲在此函數只會在對象創建的時候只調用一次。

package Excise2;

public class Demo02 {

	public static void main(String[] args) {

	A a = new A();
     }
}
class A{

   A a;
  public void init(){   
  this.a=new A();
 }
}

問題二:

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

        B b=new B();
        System.out.println(b==b.b);
        System.out.println(b.b==b.b.b);
        System.out.println(b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b);
    }
}

class B{
    static B b=new B();
}

這裏與上面的問題不同之處在於將實體類B中創建的對象用static修飾了,那麼就是這個區別,就與上面產生了不同的運行結果:

通過運行發現,這裏不僅沒有出現錯誤還運行出了結果,原因是該實體類B在創建自己的對象時將創建的對象用static修飾了,那麼表明此對象是全局靜態的變量,在前面講到過靜態變量和函數是隨着類加載的,那麼在類加載到方法區後就已經創建了一個對象,在主類裏面創建對象後,是不用對靜態的變量進行默認初始化的,所以就不會存在一直調用構造函數使棧溢出的結果,那後面打印的結果是如何得來的呢?如圖:

首先在方法區的靜態區裏會創建一個B的對象b且是全局的,然後主函數進棧再創建一個同名的對象b,此時兩個對象的地址和存儲位置是不同的,b.b是用主類中的對象去調用其他地方的對象,查找三部曲:1.該對象會先去它所在的函數裏面找有沒有要調用的東西,如果沒有的話;2.去堆內存找相應的要調用的東西,如果還沒有;3,就去該項目下的靜態方法區中查找,如果還沒有的話那就是真的沒有了。

所以這裏先在自己所在的函數中找除了自己叫b的其他叫b的對象,沒有就去堆內存中找b的對象,還是沒有就去靜態區找,此時找到了並拿到了該b的地址0x123,然後當前主類的b拿到的地址是0x234,b==b.b比較的是兩個對象的地址,不同則結果爲false;而b.b==b.b.b和b.b.b.b.b.b.b==b.b.b.b.b.b.b.b.b.b.b.b.b兩邊所拿到的地址都是一樣的都是0x123,因此都是true。

這就是靜態和成員的特點,希望通過這兩個例子,能對你理解靜態變量和函數的加載有更好的幫助。

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