欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java對象初始化代碼詳解

 更新時間:2017年11月14日 17:01:14   作者:hapjin  
這篇文章主要介紹了java對象初始化代碼詳解,涉及實例變量的初始化,類變量的初始化等相關介紹幾代碼示例,具有一定參考價值,需要的朋友可以了解下。

本文主要記錄JAVA中對象的初始化過程,包括實例變量的初始化和類變量的初始化以及final關鍵字對初始化的影響。另外,還討論了由于繼承原因,探討了引用變量的編譯時類型和運行時類型

一,實例變量的初始化

這里首先介紹下創(chuàng)建對象的過程:

類型為Dog的一個對象首次創(chuàng)建時,或者Dog類的static字段或static方法首次訪問時,Java解釋器必須找到Dog.class(在事先設定好的路徑里面搜索); 
找到Dog.class后(它會創(chuàng)建一個Class對象),它的所有static初始化模塊都會運行。因此,static初始化僅發(fā)生一次——在Class對象首次載入的時候; 
創(chuàng)建一個newDog()時,Dog對象的構建進程首先會在內存堆(Heap)里為一個Dog對象分配足夠多的存儲空間; 
這種存儲空間會清為零,將Dog中的所有基本類型(Primitive)設為它們的默認值(0用于數(shù)字,以及boolean和char的等價設定); 
進行成員字段定義時發(fā)生的所有初始化都會執(zhí)行; 
執(zhí)行構造函數(shù)。

然后,開始對實例變量進行初始化。一共有三種方式對實例變量進行初始化:

①定義實例變量時指定初始值

②非靜態(tài)初始化塊中對實例變量進行初始化

③構造器中對實例變量進行初始化

當new對象初始化時,①②要先于③執(zhí)行。而①②的順序則按照它們在源代碼中定義的順序來執(zhí)行。

當實例變量使用了final關鍵字修飾時,如果是在定義該final實例變量時直接指定初始值進行的初始化(第①種方式),則:該變量的初始值在編譯時就被確定下來,那么該final變量就類似于“宏變量”,相當于JAVA中的直接常量。

public class Test {
  public static void main(String[] args) {
    final String str1 = "HelloWorld";
    final String str2 = "Hello" + "World";
    System.out.println(str1 == str2);//true
    
    final String str3 = "Hello" + String.valueOf("World");
    System.out.println(str1 == str3);//false
  }
}

第8行輸出false,是因為:第7行中str3需要通過valueOf方法調用之后才能確定。而不是在編譯時確定。

再來看一個示例:

public class Test {
  
  final String str1 = "HelloWorld";
  final String str2 = "Hello" + "World";
  final String str3;
  final String str4;
  {
    str3 = "HelloWorld";
  }
  {
    System.out.println(str1 == str2);//true
    System.out.println(str1 == str3);//true
//    System.out.println(str1 == str4);//compile error
  }
  public Test() {
    str4 = "HelloWorld";
    System.out.println(str1 == str4);//true
  }
  
  public static void main(String[] args) {
    new Test();
  }
}

把第13行的注釋去掉,會報編譯錯誤“Theblankfinalfieldstr4maynothavebeeninitialized”

因為變量str4是在構造器中進行初始化的。而前面提到:①定義實例變量時直接指定初始值(str1和str2的初始化)、②非靜態(tài)初始化塊中對實例變量進行初始化(str3的初始化)要先于③構造器中對實例變量進行初始化。

另外,對于final修飾的實例變量必須顯示地對它進行初始化,而不是通過構造器(<clinit>)對之進行默認初始化。

public class Test {
   final String str1;//compile error---沒有顯示的使用①②③中的方式進行初始化
   String str2;
 }

str2可以通過構造器對之進行默認的初始化,初始化為null。而對于final修飾的變量 str1,必須顯示地使用 上面提到的三種方式進行初始化。如下面的這個Test.java(一共有22行的這個Test類)

public class Test {
  final String str1 = "Hello";//定義實例變量時指定初始值
  
  final String str2;//非靜態(tài)初始化塊中對實例變量進行初始化
  final String str3;//構造器中對實例變量進行初始化
  
  {
    str2 = "Hello";
  }
  public Test() {
    str3 = "Hello";
  }
  
  public void show(){
    System.out.println(str1 + str1 == "HelloHello");//true
    System.out.println(str2 + str2 == "HelloHello");//false
    System.out.println(str3 + str3 == "HelloHello");//false
  }
  public static void main(String[] args) {
    new Test().show();
  }
}

由于str1采用的是第①種方式進行的初始化,故在執(zhí)行15行:str1+str1連接操作時,str1其實相當于“宏變量”

而str2和str3并不是“宏變量”,故16-17行輸出false

在非靜態(tài)初始化代碼塊中初始化變量和在構造器中初始化變量的一點小區(qū)別:因為構造器是可以重寫的,比如你把某個實例變量放在無參的構造器中進行初始化,但是在new對象時卻調用的是有參數(shù)的構造器,那就得注意該實例變量有沒有正確得到初始化了。

而放在非靜態(tài)初始化代碼塊中初始化變量時,不管是調用有參的構造器還是無參的構造器,非靜態(tài)初始化代碼塊都會執(zhí)行。

二,類變量的初始化

類變量一共有兩個地方對之進行初始化:

❶定義類變量時指定初始值

❷靜態(tài)初始化代碼塊中進行初始化

不管new多少個對象,類變量的初始化只執(zhí)行一次。

三,繼承對初始化的影響

主要是理解編譯時類型和運行時類型的不同,從這個不同中可以看出this關鍵字和super關鍵字的一些本質區(qū)別。

class Fruit{
  String color = "unknow";
  public Fruit getThis(){
    return this;
  }
  public void info(){
    System.out.println("fruit's method");
  }
}

public class Apple extends Fruit{

  String color = "red";//與父類同名的實例變量
  
  @Override
  public void info() {
    System.out.println("apple's method");
  }
  
  public void accessFruitInfo(){
    super.info();
  }
  public Fruit getSuper(){
    return super.getThis();
  }
  
  //for test purpose
  public static void main(String[] args) {
    Apple a = new Apple();
    Fruit f = a.getSuper();
    
    //Fruit f2 = a.getThis();
    //System.out.println(f == f2);//true
    
    System.out.println(a == f);//true
    System.out.println(a.color);//red
    System.out.println(f.color);//unknow
    
    a.info();//"apple's method"
    f.info();//"apple's method"
    
    a.accessFruitInfo();//"fruit's method"
  }
}

值得注意的地方有以下幾個:

⒈第35行引用變量a和f都指向內存中的同一個對象,36-37行調用它們的屬性時,a.color是red,而f.color是unknow

因為,f變量的聲明類型(編譯時類型)為Fruit,當訪問屬性時是由聲明該變量的類型來決定的。

⒉第39-40行,a.info()和f.info()都輸出“apple'smethod”

因為,f變量的運行時類型為Apple,info()是Apple重載的父類的一個方法。調用方法時由變量的運行時類型來決定。

⒊關于this關鍵字

當在29行new一個Apple對象,在30行調用getSuper()方法時,最終是執(zhí)行到第4行的returnthis

this的解釋是:返回調用本方法的對象。它返回的類型是Fruit類型(見getThis方法的返回值類型),但實際上是Apple對象導致的getThis方法的調用。故,這里的this的聲明類型是Fruit,而運行時類型是Apple

⒋關于super關鍵字

super與this是有區(qū)別的。this可以用來代表“當前對象”,可用return返回。而對于super而言,沒有returnsuper;這樣的語句。

super主要是為了:在子類中訪問父類中的屬性或者在子類中調用父類中的方法而引入的一個關鍵字。比如第24行。

⒌在父類的構造器中不要去調用被子類覆蓋的方法(Override),或者說在構造父類對象時,不要依賴于子類覆蓋了父類的那些方法。這樣很可能會導致初始化的失敗(沒有正確地初始化對象)

因為:前面第1點和第2點談到了,對象(變量)有聲明時類型(編譯時類型)和運行時類型。而方法的調用取決于運行時類型。

當new子類對象時,會首先去初始化父類的屬性,而此時對象的運行時類型是子類,因此父類的屬性的賦值若依賴于子類中重載的方法,會導致父類屬性得不到正確的初始化值。示例如下:

class Fruit{
    String color;
    
    public Fruit() {
      color = this.getColor();//父類color屬性初始化依賴于重載的方法getColor
//      color = getColor();
    }
    public String getColor(){
      return "unkonw";
    }
    
    @Override
    public String toString() {
      return color;
    }
  }
  
  public class Apple extends Fruit{
  
    @Override
    public String getColor() {
      return "color: " + color;
    }
    
//    public Apple() {
//      color = "red";
//    }
    
    public static void main(String[] args) {
      System.out.println(new Apple());//color: null
    }
  }

Fruit類的color屬性 沒有正確地被初始化為"unknow",而是為 null

主要是因為第5行 this.getColor()調用的是Apple類的getColor方法,而此時Apple類的color屬性是直接從Fruit類繼承的。

四,參考資料

瘋狂Java 突破程序員基本功的16課  第二章

Effective Java中文版 第2版 中文 PDF版    第二版第17條

相關文章

  • java環(huán)境變量path和classpath的配置

    java環(huán)境變量path和classpath的配置

    這篇文章主要為大家詳細介紹了java系統(tǒng)環(huán)境變量path和classpath的配置過程,感興趣的小伙伴們可以參考一下
    2016-07-07
  • java多線程開啟的三種方式你知道嗎

    java多線程開啟的三種方式你知道嗎

    這篇文章主要為大家詳細介紹了java多線程開啟的三種方式,使用數(shù)據(jù)庫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下<BR>
    2022-02-02
  • 關于SpringMVC中控制器如何處理文件上傳的問題

    關于SpringMVC中控制器如何處理文件上傳的問題

    這篇文章主要介紹了關于SpringMVC中控制器如何處理文件上傳的問題,在 Web 應用程序中,文件上傳是一個常見的需求,例如用戶上傳頭像、上傳文檔等,本文將介紹 Spring MVC 中的控制器如何處理文件上傳,并提供示例代碼,需要的朋友可以參考下
    2023-07-07
  • 對java for 循環(huán)執(zhí)行順序的詳解

    對java for 循環(huán)執(zhí)行順序的詳解

    今天小編就為大家分享一篇對java for 循環(huán)執(zhí)行順序的詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • 使用Mybatis的PageHelper分頁工具的教程詳解

    使用Mybatis的PageHelper分頁工具的教程詳解

    這篇文章主要介紹了使用Mybatis的PageHelper分頁工具的教程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Struts2 OGNL表達式實例詳解

    Struts2 OGNL表達式實例詳解

    這篇文章主要介紹了Struts2 OGNL表達式實例詳解,相關實例代碼,需要的朋友可以參考。
    2017-09-09
  • Java如何正確的使用wait-notify方法你知道嗎

    Java如何正確的使用wait-notify方法你知道嗎

    這篇文章主要為大家詳細介紹了Java如何正確的使用wait-notify方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 通過idea創(chuàng)建Spring Boot項目并配置啟動過程圖解

    通過idea創(chuàng)建Spring Boot項目并配置啟動過程圖解

    這篇文章主要介紹了通過idea創(chuàng)建Spring Boot項目并配置啟動過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • 詳解Java關于JDK中時間日期的API

    詳解Java關于JDK中時間日期的API

    這篇文章主要介紹了詳解Java關于JDK中時間日期的API,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • mybatis中方法返回泛型與resultType不一致的解決

    mybatis中方法返回泛型與resultType不一致的解決

    這篇文章主要介紹了mybatis中方法返回泛型與resultType不一致的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論