學習Java類必須知道的幾點

jopen 10年前發布 | 13K 次閱讀 Java Java開發

1、類的加載執行順序 

看一下如下的例子:
    class ParentClass {  
        public static int  a=2;  
        public int b=3;  
        {  
            System.out.println("this is anonymity b="+b);  
        }  
        static {  
            a=4;  
            System.out.println("this is static and a="+a);  
        }  
        public ParentClass() {  
            System.out.println("this is parent gozao");  
            this.s();  
        }  
        public void s() {  
            System.out.println("this is parent");  
        }  
    }  
    public class Son extends ParentClass {  
        public Son(){  
            System.out.println("this is  son gozao");  
        }  
        public static void main(String[] args) {  
            ParentClass d = new Son();  
            d.s();  
        }  
        public void s() {  
            //super.s();  
            System.out.println("this is son");  
        }  
    }  
運行結果如下:
    this is static and a=4  
    this is anonymity   
    this is parent gozao  
    this is son  
    this is  son gozao  
    this is son  
可以看出類內的加載順序為:
(1)初始化變量。對于靜態變量肯定要首先進行初始化,因為后面的方法可能會使用這個變量,或者構造函數中也可能用到。而對于非靜態變量而言,由于匿名塊內、非靜態方法和構造函數都可以進行操作(不僅僅是初始化),所以要提前進行加載和賦默認值。
(2)初始化靜態代碼塊,多個靜態代碼塊按順序加載,這里需要注意:在這個順序不難是類內書寫的順序,也是類加載的順序,也就是說如果子類也有靜態代碼塊,則子類的也加載。由于靜態代碼塊可能會負責變量的初始化,或者是對象等的初始化,這樣在構造函數或者方法中就變得可用了。而順序加載多半是由于Java是按順序執行代碼的原因。
(3)匿名代碼塊,這個要后初始化于靜態代碼塊,因為其依然屬于實例對象,而不屬于類。在這里可以對非靜態成員變量進行初始化工作。
(4)構造函數 這里需要解釋一下,為什么初始化子類必先初始化父類,由于子類可能會繼承父類的屬性或方法,所以肯定要先初始化父類了,而初始化父類則必須要調用父類的構造函數。
至于方法不用考慮,因為方法不用初始化,所以無論是靜態還是不靜態,和這個沒有關系。
其實如上的代碼還能說明一些問題,可以看到,在父類中通過this.s()調用的是子類的方法,子類的s()方法覆蓋了父類的方法后,無論在哪里調用,都是調用子類的方法。

2、不可變類  

不可變類是指當創建了這個類的實例后,就不允許修改它的屬性值。在JDK的基本類庫中,所有基本類型的包裝類,如Integer、Long等都是不可變類,除此之外還有java.lang.String也是不可變類。不可變類的實例一但創建,其內在成員變量的值就不能被修改。創建一個不可變類需要如下條件:

1. 對于一般成員都是private,還可以使用public static final 來定義一個全局的常量。
2. 不提供對成員的修改方法,例如:setXXX()
3. 確保所有的方法不會被重載。手段有兩種:使用final Class(強不可變類),或者將所有的類方法加上final關鍵字(弱不可變類)。
4. 如果某一個類成員不是原始變量(primitive)或者不可變類,必須通過在成員初始化或者get方法時通過深度clone方法,來確保類的不可變。

其它的都好理解,下面來著重解釋一下第4條。舉個例子:
    public final class MyImmutableWrong {  // 使用final關鍵字聲明為強不可變類  
        private final int[] myArray;  

        public MyImmutableWrong(int[] anArray) {  
            this.myArray = anArray; // wrong  
        }  

        public String toString() {  
            StringBuffer sb = new StringBuffer("Numbers are: ");  
            for (int i = 0; i < myArray.length; i++) {  
                sb.append(myArray[i] + " ");  
            }  
            return sb.toString();  
        }  
    }  
由于int[] anArray是一個數組,屬于引用類型。這樣當再次去操作外部的一個引用時,其指向的共同內容,也就是數組的值。
    public class dd {  
        public static void main(String[] args) {  
             int array[]={1,2,3,4};  
             MyImmutableWrong service=new MyImmutableWrong(array);  
             array[2]=99;  
             System.out.println(service.toString());  
        }  

    }  
查看控制臺的輸出內容:Numbers are: 1 2 99 4 
可以看到,不可變類中的數組內容發生了改變。究其原因就是 - 關鍵字final僅對其直接指向的對象有用,并且final引用可以指向帶有非final域的對象。
為了避免這個問題,必須對數組進行深度克隆。也就是專門再為不可變類中的數組開避一份內存空間,然后將參數的值賦值過去。正確的寫法如下:
    public final class MyImmutableCorrect {  
        private final int[] myArray;  

        public MyImmutableCorrect(int[] anArray) {  
            this.myArray = anArray.clone();   
        }  

        public String toString() {  
            StringBuffer sb = new StringBuffer("Numbers are: ");  
            for (int i = 0; i < myArray.length; i++) {  
                sb.append(myArray[i] + " ");  
            }  
            return sb.toString();  
        }  
    }  
測試后發現:Numbers are: 1 2 3 4 

不可變類有一些優點,比如因為它的對象是只讀的,所以多線程并發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成性能上的問題。

3、靜態內部類  

       內部類就是在一個類的內部定義的類,內部類中不能定義靜態成員(靜態成員不是對象的特性,只是為了找一個容身之處,所以需要放到一個類中而已,把“全局變量”放在內部類中就是毫無意義的事情,所以被禁止),內部類可以直接訪問外部類中的成員變量,即使這個成員變量是由private來修飾的。
       同樣拿不可變類來舉一個例子。假如這個不可變類中有許多的private final屬性需要初始化,這時候看起來就不太方便。可能有人會采用工廠方法來解決,但有時候也可以使用靜態內部類來解決這一問題。如下:
    public class Author {  

      private final String name;  

      public String getName() {  
        return name;  
      }  

      public Author(String name_) {  
        name = name_;  
      }  
      @Override  
      public String toString() {  
        return "Author [name=" + name + "]";  
      }  
    }  
    public final class Update {  // 強不可變類  

        private final Author author;  // 作者,是個引用變量  
        private final String updateText;  // 更新內容  
        private final long createTime;  // 更新時間  

        // 私有構造函數,防止外部實例化  
        private Update(Builder b_) {  
            author = b_.author;  
            updateText = b_.updateText;  
            createTime = b_.createTime;  
        }  
        // 構建器  
        public static class Builder {  
            private long createTime;  
            private Author author;  
            private String updateText;  

            public Builder author(Author author_) {  
                author = author_;  
                return this;  
            }  

            public Builder updateText(String updateText_) {  
                updateText = updateText_;  
                return this;  
            }  

            public Builder createTime(long createTime_) {  
                createTime = createTime_;  
                return this;  
            }  

            public Update build() {// 更新外部類的值  
                return new Update(this);  
            }  
        }  
        public Author getAuthor() {  
            return author;  
        }  

        public String getUpdateText() {  
            return updateText;  
        }  

         @Override  
          public String toString() {  
            return "Update [author=" + author + ", updateText=" + updateText  
                + ", createTime=" + createTime + "]";  
          }  
    }  
可以看到,靜態內部類有與外部類同樣的成員變量,但是靜態內部類中的成員變量卻是可以修改的。
來測試一下:
    public class Test {  

        public static void main(String[] args) {  
            final Update first = getUpdate("abc");// 獲取不可變類的實例  
            System.out.println(first.toString());  
        }  

        private static Update getUpdate(String s) {  
            Update.Builder b = new Update.Builder();  
            b.updateText(s).author(new Author("mazhi"));  
            return b.build();// 初始化不可變類并返回實例  
        }  
    }  
運行后的結果如下:
Update [author=Author [name=mazhi], updateText=abc, createTime=0]
   在方法外部定義的內部類可以加上static關鍵字、可以定義成public、protected、默認的、private等多種類型,而普通類只能定義成public和默認的這兩種類型。在外面引用靜態內部類的名稱為"外部類名.內部類名"。不需要創建外部類的實例對象,就可以直接創建靜態內部類。例如:

 
    Update.Builder b = new Update.Builder(); // 獲取靜態內部類的實例對象  

   由于不依賴于外部類的實例對象,所以能訪問外部類的非static成員變量。

想了解更多的內部類,可以查閱其它資料。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!