<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    public class Cat
    {
        String name;
        int age;

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

        {
            System.out.println("執行非靜態初始化塊");
            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個地方執行實例化:
    1、定義實例變量時候指定初始值
    2、非靜態初始化塊中對實例變量指定初始值
    3、構造器中對實例變量指定初始值。
    關鍵點:當創建對象new的時候,定義實例變量(非static)賦值、非靜態初始化塊賦值,這兩種賦值總是位于構造器的賦值之前!同時變量賦值、非靜態初始化塊賦值之間的順序是在源代碼中寫的順序。(記住這句話就不會出錯了!)

    其次說明類變量的初始化以及賦值時機,看如下代碼:
    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);
        }
    }
    輸出結果:-2.8和17.2
    類變量的初始化以及賦值時機:
    1、定義類變量時候指定初始值。
    2、靜態初始化塊中對類變量指定初始值。
    首先要明確:無論new多少個Price實例,static的變量都只初始化賦值(如果有賦值的話)一次,要點:static的變量只有在使用的時候即第一次調用它的時候(而不管它是否new過),才會開始初始化并賦值,順序是:系統為該類的static類變量分配內存空間。按初始化代碼的排列順序對類變量進行初始化。
    所以上面代碼,調用Price.INSTANCE.currentPrice的時候,系統開始為static的變量初始化并開始賦值,按順序執行,那么INSTANCE=new Price(2.8);是賦值,然而initPrice還沒有賦值,僅僅只是分配了內存空間,因為是按初始化代碼的順序對類變量進行初始化。所以相減為0-2.8=-2.8。

    隱式調用和顯示調用

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

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

        public Creature()
        {
            System.out.println("Creature無參數的構造器");
        }

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

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

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

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

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

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

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

    最后一個關鍵點很重要:編譯時類型調用實例變量其值是編譯時的變量值,編譯時類型調用方法的時候其值調用的是實際類型的方法!如何理解?就是這樣比如
    父類 父 = new 子類();//父和子都有int a這個變量
    父.a  //那么這里其實是父的a變量的值  即編譯時類型即調用的是父的變量值
    父.方法();//那這里其實是子類的重寫方法,即運行時類型是子類。
    當調用子類構造的時候,不管隱式調用還是顯示調用,當調用父類構造的時候父類構造里面出現this的時候,這個this一定是子類類型的!(規律) 但是編譯時類型是父類的,如果里面調用父類的變量,那么其實是父類的變量,而不是子類的變量,雖然它實際類型是子類
    代碼如下:
    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));
        }
        
    }
    根據上面說的原則,這里調用new Wolf1(“灰太狼”,32.3)的時候里面沒有super顯示調用父類構造,那么就會是隱式調用父構造即父無參構造,而父無參構造里面的this.desc=getDesc();
    其中給getDesc()前面加上this,就是this.desc = this.getDesc();這里的this其實是Wolf1的類型,可以打印輸出System.out.println(this.getClass());輸出的是Wolf1類型,那么關鍵點就來了:這里的this實際上對于desc變量來說是編譯時類型的變量,那么會直接調用編譯類型的變量即這里的desc,然而對于方法來說:這里this.getDesc()其實調用的運行時類型的方法。然而這個時候name和weight分別只分配了內存空間null和0.0所以最后結果就是這樣。
    通過javap工具可以清楚的看到里面運行時候變量的分配情況,
    最后一點,如果父類和子類有同名的成員變量,有重寫的方法,java的編譯器在處理方法和成員變量的時候存在區別:
    1、父類的變量不會轉移到子類去,父類和子類仍然有各自的同名的成員變量
    2、父類的方法會轉移到子類中去,如果子類也包含了相同的方法,那么父類的方法不會轉移到子類中去,這個時候子類的方法就構成了重寫。
    再理解:父類和子類同名的成員變量,子類只是對父類的成員變量做了隱藏,內存中仍然為父類的同名變量開辟了空間。如下代碼:
    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="編程思想";
    }

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

    posted on 2012-06-12 00:12 朔望魔刃 閱讀(290) 評論(0)  編輯  收藏 所屬分類: java
    主站蜘蛛池模板: 久久精品国产亚洲麻豆| 亚洲日本国产乱码va在线观看| 亚洲中文字幕久久精品蜜桃| ww4545四虎永久免费地址| 337p日本欧洲亚洲大胆精品555588| 久久国产精品免费视频| 中文字幕亚洲精品资源网| 真人做人试看60分钟免费视频| 亚洲国产综合第一精品小说| 99久久免费国产精品特黄| 亚洲人av高清无码| 无码欧精品亚洲日韩一区夜夜嗨| 成人精品综合免费视频| 亚洲免费观看视频| 男人的天堂亚洲一区二区三区| 亚洲人成77777在线播放网站不卡| 67194成是人免费无码| 亚洲AV无码一区二区乱子仑| 亚洲国产av一区二区三区| 中国好声音第二季免费播放| 亚洲综合一区二区精品导航| 一级毛片视频免费观看| 国产成人精品日本亚洲专区61| 亚洲第一综合天堂另类专| 国产成人福利免费视频| 亚洲爆乳少妇无码激情| 亚洲一区二区三区在线播放| 一区二区三区观看免费中文视频在线播放| 91久久亚洲国产成人精品性色| 中文字幕无码不卡免费视频 | 中文字幕久久亚洲一区| 毛片免费在线观看| 亚洲中文字幕AV每天更新| 久久亚洲国产成人影院网站| 亚洲一级毛片免费观看| 羞羞漫画小舞被黄漫免费| 亚洲AV无码专区国产乱码4SE| 国内免费高清在线观看| 久久久久久久国产免费看| 亚洲制服丝袜在线播放| 免费人成年轻人电影|