溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java對象初始化代碼分享

發(fā)布時(shí)間:2021-08-20 19:49:13 來源:億速云 閱讀:102 作者:chen 欄目:編程語言

這篇文章主要講解了“java對象初始化代碼分享”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java對象初始化代碼分享”吧!

一,實(shí)例變量的初始化

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

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

然后,開始對實(shí)例變量進(jìn)行初始化。一共有三種方式對實(shí)例變量進(jìn)行初始化:

①定義實(shí)例變量時(shí)指定初始值

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

③構(gòu)造器中對實(shí)例變量進(jìn)行初始化

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

當(dāng)實(shí)例變量使用了final關(guān)鍵字修飾時(shí),如果是在定義該final實(shí)例變量時(shí)直接指定初始值進(jìn)行的初始化(第①種方式),則:該變量的初始值在編譯時(shí)就被確定下來,那么該final變量就類似于“宏變量”,相當(dāng)于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,是因?yàn)椋旱?行中str3需要通過valueOf方法調(diào)用之后才能確定。而不是在編譯時(shí)確定。

再來看一個(gè)示例:

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行的注釋去掉,會(huì)報(bào)編譯錯(cuò)誤“Theblankfinalfieldstr4maynothavebeeninitialized”

因?yàn)樽兞縮tr4是在構(gòu)造器中進(jìn)行初始化的。而前面提到:①定義實(shí)例變量時(shí)直接指定初始值(str1和str2的初始化)、②非靜態(tài)初始化塊中對實(shí)例變量進(jìn)行初始化(str3的初始化)要先于③構(gòu)造器中對實(shí)例變量進(jìn)行初始化。

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

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

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

public class Test {
  final String str1 = "Hello";//定義實(shí)例變量時(shí)指定初始值
  
  final String str2;//非靜態(tài)初始化塊中對實(shí)例變量進(jìn)行初始化
  final String str3;//構(gòu)造器中對實(shí)例變量進(jìn)行初始化
  
  {
    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采用的是第①種方式進(jìn)行的初始化,故在執(zhí)行15行:str1+str1連接操作時(shí),str1其實(shí)相當(dāng)于“宏變量”

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

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

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

二,類變量的初始化

類變量一共有兩個(gè)地方對之進(jìn)行初始化:

?定義類變量時(shí)指定初始值

?靜態(tài)初始化代碼塊中進(jìn)行初始化

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

三,繼承對初始化的影響

主要是理解編譯時(shí)類型和運(yùn)行時(shí)類型的不同,從這個(gè)不同中可以看出this關(guān)鍵字和super關(guān)鍵字的一些本質(zhì)區(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";//與父類同名的實(shí)例變量
  
  @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"
  }
}

值得注意的地方有以下幾個(gè):

⒈第35行引用變量a和f都指向內(nèi)存中的同一個(gè)對象,36-37行調(diào)用它們的屬性時(shí),a.color是red,而f.color是unknow

因?yàn)椋琭變量的聲明類型(編譯時(shí)類型)為Fruit,當(dāng)訪問屬性時(shí)是由聲明該變量的類型來決定的。

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

因?yàn)?,f變量的運(yùn)行時(shí)類型為Apple,info()是Apple重載的父類的一個(gè)方法。調(diào)用方法時(shí)由變量的運(yùn)行時(shí)類型來決定。

⒊關(guān)于this關(guān)鍵字

當(dāng)在29行new一個(gè)Apple對象,在30行調(diào)用getSuper()方法時(shí),最終是執(zhí)行到第4行的returnthis

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

⒋關(guān)于super關(guān)鍵字

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

super主要是為了:在子類中訪問父類中的屬性或者在子類中調(diào)用父類中的方法而引入的一個(gè)關(guān)鍵字。比如第24行。

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

因?yàn)椋呵懊娴?點(diǎn)和第2點(diǎn)談到了,對象(變量)有聲明時(shí)類型(編譯時(shí)類型)和運(yùn)行時(shí)類型。而方法的調(diào)用取決于運(yùn)行時(shí)類型。

當(dāng)new子類對象時(shí),會(huì)首先去初始化父類的屬性,而此時(shí)對象的運(yùn)行時(shí)類型是子類,因此父類的屬性的賦值若依賴于子類中重載的方法,會(huì)導(dǎo)致父類屬性得不到正確的初始化值。示例如下:

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

主要是因?yàn)榈?行 this.getColor()調(diào)用的是Apple類的getColor方法,而此時(shí)Apple類的color屬性是直接從Fruit類繼承的。

感謝各位的閱讀,以上就是“java對象初始化代碼分享”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對java對象初始化代碼分享這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI