隨筆-95  評論-31  文章-10  trackbacks-0
          最近一直在看設(shè)計模式和數(shù)據(jù)結(jié)構(gòu),把java面向?qū)ο蟮囊恍┘毠?jié)一些容易出錯的地方。唉 遺忘了 我覺得造成這種后果的原因是:
          在做項目的時候容易形成那種習(xí)慣性的東西,有時候這種習(xí)慣會影響到自己對java語法的一些理解(如果不經(jīng)常溫習(xí)基礎(chǔ)知識,有時候會讓自己的習(xí)慣潛移默化或者改變自己原本正確的認識,或者漸漸模糊了一些最基礎(chǔ)的東西) ,不說這些了,我之前自認為面向?qū)ο蠡A(chǔ)那一塊是很扎實的,但我覺得我還是浮躁了!
          這里我要重新認識和總結(jié)下static final super this 以及實例變量、類變量的初始化時機以及初始化的順序,以及多重繼承的時候調(diào)用父類的隱式構(gòu)造和顯示構(gòu)造這些東西,如果里面在有一些賦值應(yīng)該怎樣怎樣?還是以代碼來說話,首先先說明實例變量的初始化時機,先不提繼承的東西
          package com.lx;

          public class Cat
          {
              String name;
              int age;

              public Cat(String name, int age)
              {
                  System.out.println("執(zhí)行構(gòu)造器");
                  this.name = name;
                  this.age = age;
              }

              {
                  System.out.println("執(zhí)行非靜態(tài)初始化塊");
                  weight = 2.0;
              }
              double weight = 2.3;

              public String toString()
              {
                  return "Cat[name=" + name + ",age=" + age + ",weight=" + weight + "]";
              }
              
              public static void main(String[] args)
              {
                  Cat cat1 = new Cat("first",2);
                  System.out.println(cat1);
                  Cat cat2 = new Cat("second",3);
                  System.out.println(cat2);
              }
             
          }
          這里要說明的問題是:實例變量的初始化以及賦值時機
          實例變量有3個地方執(zhí)行實例化:
          1、定義實例變量時候指定初始值
          2、非靜態(tài)初始化塊中對實例變量指定初始值
          3、構(gòu)造器中對實例變量指定初始值。
          關(guān)鍵點:當(dāng)創(chuàng)建對象new的時候,定義實例變量(非static)賦值、非靜態(tài)初始化塊賦值,這兩種賦值總是位于構(gòu)造器的賦值之前!同時變量賦值、非靜態(tài)初始化塊賦值之間的順序是在源代碼中寫的順序。(記住這句話就不會出錯了?。?br />
          其次說明類變量的初始化以及賦值時機,看如下代碼:
          package com.lx;

          public class Price
          {
              final static Price INSTANCE = new Price(2.8);
              static double initPrice = 20;
              double currentPrice;
              public Price(double discount){
                  currentPrice = initPrice - discount;
              }
              public static void main(String[] args)
              {
                  System.out.println(Price.INSTANCE.currentPrice);
                  Price p = new Price(2.8);
                  System.out.println(p.currentPrice);
              }
          }
          輸出結(jié)果:-2.8和17.2
          類變量的初始化以及賦值時機:
          1、定義類變量時候指定初始值。
          2、靜態(tài)初始化塊中對類變量指定初始值。
          首先要明確:無論new多少個Price實例,static的變量都只初始化賦值(如果有賦值的話)一次,要點:static的變量只有在使用的時候即第一次調(diào)用它的時候(而不管它是否new過),才會開始初始化并賦值,順序是:系統(tǒng)為該類的static類變量分配內(nèi)存空間。按初始化代碼的排列順序?qū)︻愖兞窟M行初始化。
          所以上面代碼,調(diào)用Price.INSTANCE.currentPrice的時候,系統(tǒng)開始為static的變量初始化并開始賦值,按順序執(zhí)行,那么INSTANCE=new Price(2.8);是賦值,然而initPrice還沒有賦值,僅僅只是分配了內(nèi)存空間,因為是按初始化代碼的順序?qū)︻愖兞窟M行初始化。所以相減為0-2.8=-2.8。

          隱式調(diào)用和顯示調(diào)用

          當(dāng)調(diào)用某個類的構(gòu)造器來創(chuàng)建Java對象時,系統(tǒng)總會先調(diào)用父類的非靜態(tài)初始化塊進行初始化,然后調(diào)用父類的構(gòu)造器,這個是隱式調(diào)用(缺省是無參構(gòu)造),如果顯示調(diào)用就是super()根據(jù)super里面不同的參數(shù)去調(diào)用對應(yīng)的構(gòu)造,但是在構(gòu)造里面又遵循這個原則,先調(diào)用父類的非靜態(tài)初始化塊,然后父類的構(gòu)造器。。。一直到Object。所以看下面的代碼:
          package com.lx;

          public class Creature
          {
              {
                  System.out.println("Creature的非靜態(tài)初始化塊");
              }

              public Creature()
              {
                  System.out.println("Creature無參數(shù)的構(gòu)造器");
              }

              public Creature(String name)
              {
                  System.out.println("Creature有參的構(gòu)造器參數(shù)為:" + name);
              }
              public static void main(String[] args)
              {
                  new Wolf(5.6);
              }
          }

          class Animal extends Creature
          {
              {
                  System.out.println("Animal的非靜態(tài)初始化塊");
              }

              public Animal(String name)
              {
                  super(name);
                  System.out.println("Animal帶一個有參數(shù)的構(gòu)造器,name參數(shù)為:" + name);
              }

              public Animal(String name, int age)
              {
                  this(name);
                  System.out.println("Animal帶2個參數(shù)的構(gòu)造器,其age:" + age);
              }
          }

          class Wolf extends Animal
          {
              {
                  System.out.println("wolf的非靜態(tài)初始化塊");
              }

              public Wolf()
              {
                  super("灰太狼", 3);
                  System.out.println("Wolf無參數(shù)的構(gòu)造器");
              }

              public Wolf(double weight)
              {
                  this();
                  System.out.println("Wolf的帶weight參數(shù)的構(gòu)造器,weight參數(shù):" + weight);
              }
          }
          理解了上面那句話對于這段代碼的輸出也就不會覺得難理解了。同時super和this不能同時出現(xiàn)

          最后一個關(guān)鍵點很重要:編譯時類型調(diào)用實例變量其值是編譯時的變量值,編譯時類型調(diào)用方法的時候其值調(diào)用的是實際類型的方法!如何理解?就是這樣比如
          父類 父 = new 子類();//父和子都有int a這個變量
          父.a  //那么這里其實是父的a變量的值  即編譯時類型即調(diào)用的是父的變量值
          父.方法();//那這里其實是子類的重寫方法,即運行時類型是子類。
          當(dāng)調(diào)用子類構(gòu)造的時候,不管隱式調(diào)用還是顯示調(diào)用,當(dāng)調(diào)用父類構(gòu)造的時候父類構(gòu)造里面出現(xiàn)this的時候,這個this一定是子類類型的!(規(guī)律) 但是編譯時類型是父類的,如果里面調(diào)用父類的變量,那么其實是父類的變量,而不是子類的變量,雖然它實際類型是子類
          代碼如下:
          package com.lx;

          public class Animal1
          {
              private String desc;

              public Animal1()
              {
                  this.desc = getDesc();
              }

              public String getDesc()
              {
                  return "Animal";
              }

              public String toString()
              {
                  return desc;
              }
              
              class Wolf1 extends Animal1{
                  private String name;
                  private double weight;
                  public Wolf1(String name,double weight){
                      this.name = name;
                      this.weight = weight;
                  }
                  @Override
                  public String getDesc()
                  {
                      return "Wolf[name="+name+",weight="+weight+"]";
                  }
              }
              
              public static void main(String[] args)
              {
                  System.out.println(new Animal1().new Wolf1("灰太狼",32.3));
              }
              
          }
          根據(jù)上面說的原則,這里調(diào)用new Wolf1(“灰太狼”,32.3)的時候里面沒有super顯示調(diào)用父類構(gòu)造,那么就會是隱式調(diào)用父構(gòu)造即父無參構(gòu)造,而父無參構(gòu)造里面的this.desc=getDesc();
          其中給getDesc()前面加上this,就是this.desc = this.getDesc();這里的this其實是Wolf1的類型,可以打印輸出System.out.println(this.getClass());輸出的是Wolf1類型,那么關(guān)鍵點就來了:這里的this實際上對于desc變量來說是編譯時類型的變量,那么會直接調(diào)用編譯類型的變量即這里的desc,然而對于方法來說:這里this.getDesc()其實調(diào)用的運行時類型的方法。然而這個時候name和weight分別只分配了內(nèi)存空間null和0.0所以最后結(jié)果就是這樣。
          通過javap工具可以清楚的看到里面運行時候變量的分配情況,
          最后一點,如果父類和子類有同名的成員變量,有重寫的方法,java的編譯器在處理方法和成員變量的時候存在區(qū)別:
          1、父類的變量不會轉(zhuǎn)移到子類去,父類和子類仍然有各自的同名的成員變量
          2、父類的方法會轉(zhuǎn)移到子類中去,如果子類也包含了相同的方法,那么父類的方法不會轉(zhuǎn)移到子類中去,這個時候子類的方法就構(gòu)成了重寫。
          再理解:父類和子類同名的成員變量,子類只是對父類的成員變量做了隱藏,內(nèi)存中仍然為父類的同名變量開辟了空間。如下代碼:
          package com.lx;

          public class Parent
          {
              public String tag = "java編程";
              public static void main(String[] args)
              {
                  Son s = new Son();
          //        System.out.println(s.tag);  //編譯不通過
                  System.out.println(((Parent)s).tag);
              }
          }
          class Son extends Parent{
              private String tag="編程思想";
          }

          最后最后最重要的一點:自己摸索的規(guī)律
          不管在什么地方,調(diào)用方法或者調(diào)用構(gòu)造,這個引用調(diào)用的時候,如果延伸到子類或者父類,不管在哪里只要出現(xiàn)this,這個this的運行時類型就是它調(diào)用時候的類型。而不管這個this出現(xiàn)在父類或者子類也好,這個this一定是引用調(diào)用時候的類型。而編譯類型是這個this處在哪個類當(dāng)中,就指的是哪個類。
          暫時到這里

          posted on 2012-06-12 00:12 朔望魔刃 閱讀(293) 評論(0)  編輯  收藏 所屬分類: java
          主站蜘蛛池模板: 金塔县| 泸州市| 南昌县| 南通市| 平谷区| 阿坝县| 措美县| 新绛县| 紫云| 谢通门县| 武山县| 镇坪县| 衡阳市| 宜黄县| 台安县| 德昌县| 泌阳县| 康平县| 察隅县| 和龙市| 台中市| 蒙阴县| 贵州省| 张家口市| 鹤峰县| 山东| 三明市| 长宁县| 通榆县| 绥化市| 灵川县| 林周县| 香格里拉县| 蒙城县| 商水县| 宝兴县| 新乡市| 宜黄县| 洪洞县| 托里县| 百色市|