[第三章]第三節、類、接口、方法和垃圾回收

第三節、類、接口、方法和垃圾回收

       一、類

              1.類和對象的區別

當你創建一個類時,你創建了一種新的數據類型。你可以使用這種類型來聲明該種類型的對象。要獲得一個類的對象需要兩步:一,你必須聲明該類類型的一個變量,這個變量沒有定義一個對象。實際上它只是一個能夠引用對象的簡單變量;二,使用new運算符創建一個對象的實際的物理拷貝,並把對於該對象的引用賦給該變量。new運算符爲對象動態分配(即在運行時分配)內存空間,並返回對它的一個引用。這個引用或多或少的是new分配給對象的內存地址。在Java中,所有的類對象都必須動態分配。

讓我們再次複習類和對象之間的區別:類創建一種新的數據類型,該種類型能被用來創建對象。也就是,類創建了一個邏輯的框架,該框架定義了它的成員之間的關係。當你聲明類的對象時,你正在創造該類的實例。因此,類是一個邏輯構造,對象有物理的真實性(也就是對象佔用內存空間)。      

              例如下面代碼段,你認爲它完成了哪些操作?

                     HelloWorld hw=new HelloWorld();

                     HelloWorld hw2=hw;

              第一句:創建了一個HelloWorld類的對象,然後將該對象的引用賦給變量 hw

第二句:將對第一句創建的對象的引用(不是對象的拷貝)賦給變量 hw2hwhw2指向同一個對象

 

類可以使用的修飾符有:

        public  公共的

abstract 聲明該類爲抽象類,必須由其子類來提供完整的實現

final       聲明該類爲final的,則不能被其他類繼承

strictfp 使用原始的浮點運算模型

protectedprivate 當一個類聲明在另一個類內部時,稱之爲內部類 ,由於它是所屬類的成員,

         故可以使用protectedprivate,而頂層類則不能使用。

2.構造器(constructor

                  我們同過new運算符動態的爲一個對象分配地址,它的通用格式如下:

                            class-var = new classname();

       其中,class-var是所創建類類型的變量。classname是被實例化的類的名字。類名後面跟的圓括號指定了類的構造器。一個類的構造器必須和該類類名準確匹配,而且是無返回類型聲明的,它的隱含返回類型是該類本身。

在每次創建類實例時必須初始化所有變量是一件非常耗時和令人厭倦的工作,你希望在這些對象創建時,它能夠自己初始化。Java使用類構造器來進行這個工作,使對象創建時能設置對象變量的初始狀態。Java中每一個類都有一個默認的構造器(即無帶參數的構造器),但是當你爲某個類定義了帶參數的構造器時,如果你沒有再定義無參數的構造器,那麼該類的默認構造器是無效的(invalid),你不能再使用它來創建新的對象。

 

              3.繼承

繼承是面向對象編程技術的一塊基石,因爲它允許創建分等級層次的類。Java中使用extends關鍵字來聲明,例如我們創建了一個類A,如果想創建另一個類B並且B是繼承A的,那麼聲明如下:

                     class B extends A{

                     }  // A是超類 B是子類

Java術語中,被繼承的類叫做超類(superclass),繼承超類的類叫做子類(subclass),子類是超類一個專門用途的版本,它繼承了超類定義的所有的實例變量和方法(即子類隱含包括了超類所有的實例變量和方法,但不包括構造器),並且爲它自己增添獨特的元素。但子類不能訪問超類中被聲明爲爲private的成員。

上面說到了子類繼承超類中定義的所有的實例變量和方法,但是如果字類中有方法和超類中的方法同名會出現什麼情況呢?這就涉及到Java中覆蓋和重載的概念:當子類中定義的方法和超類中定義的方法有相同的名稱,返回類型和參數列表時,我們稱子類中的方法覆蓋超類的方法(overridded method),超類中的被覆蓋方法被子類隱藏 (即不能通過子類訪問該方法)。覆蓋方法有三點需要注意的:

* 當子類方法的名稱和參數列表同超類的方法相同時,那麼它的返回類型就必須聲明和超類該方法的返回類型相同,否則會出現編譯錯誤。若僅有名稱和(或)返回類型相同時,只是方法重載(overloaded)。

* 覆蓋方法的訪問修飾符的訪問範圍不能比超類被覆蓋的方法小,比如,超類中被覆蓋的方法聲明爲protectd的,那麼子類中的覆蓋方法就必須聲明爲protectedpublic的,而不能聲明爲private

* 覆蓋方法必須拋出和超類中被覆蓋方法相同的異常

               超類變量可以引用子類對象,即:如果類B是繼承類A的,那麼可以這樣聲明:

B b1 = new A();  Java中引用變量的類型-----而不是引用對象的類型---決定了什麼成員可以被訪問。也就是說,當一個子類對象的引用被賦給一個超類引用變量時,你只能訪問超類中定義的部分,如果超類中有方法被子類覆蓋了,那麼雖然你使用的超類類型的引用變量,但你只能訪問字類中的覆蓋方法,不能訪問超類中被覆蓋的方法。

例子:

package Examples;

public  class ClassDemo{              // 超類

   public ClassDemo(){

   }

   protected String newPrint(int i){

          return "["+i+"]This is the method in SuperClass!";

   }

}

class SubClassDemo extends ClassDemo{  //子類

   // 覆蓋超類中的newPrint方法

   protected String newPrint(int s){

          return String.valueOf(s);

   }

   // 使用不同參數重載newPrint方法

   public String newPrint(String s){

          return s+" after ";

   }

}

class OverClassDemo{       

   public static void main(String[] args){

          SubClassDemo scd=new SubClassDemo();  //創建子類的一個對象

          System.out.println(scd.newPrint(10));      //調用覆蓋方法

          System.out.println(scd.newPrint(“Hello”));  //調用子類中的重載方法

          ClassDemo cd=scd;  //將子類引用變量賦給超類引用變量

          System.out.println(cd.newPrint(99));  // 這裏調用的還是子類的覆蓋方法

          // 下面的語句則會導致編譯錯誤

          // System.out.println(cd.newPrint("KK")); //意圖調用子類中的重載方法,

// 而這是訪問不到,編譯器會提示參數必須是int類型的錯誤。

   }

}

                            

當一個子類需要引用它的直接超類時,它可以用關鍵字super來實現。super使用在子類中,用來引用超類中不爲private的成員。在類層次結構中,構造器以派生的次序調用,從超類到子類。當你創建一個子類的對象時,子類的構造器的初始化次序是:先按派生的次序隱含的使用super()調用它的每個超類的默認的構造器,然後再調用子類中聲明的初始化代碼。你可以使用super()顯式的調用直接超類的默認構造器,但這個super()語句必須放在構造器內所有代碼之前。

              4.抽象類

有些情況下,你希望定義一個超類,該超類定義了一種給定結構的抽象但是不提供任何完整的方法來實現。也就是說,創建一個只定義一個被它的所有子類共享的通用形式,由每個子類自己去填寫細節。

這種情況下,由於這種超類中的有沒有實際意義的方法,你希望確保子類真正重載了所有必須的方法。Java中使用abstract關鍵字來聲明需要被子類實現(即覆蓋)的方法。任何含有一個或多個抽象方法的類都必須聲明成抽象類,即將類聲明爲abstract的。抽象類沒有對象,也就是說它不能通過new運算符直接實例化。

                            抽象類的字類必須實現超類的所有抽象方法,或者不實現而將它自己也設置成抽象類。

 

                     5.內部類:

                            顧名思義,內部類是定義在另一個類內部的類。

                               * Group classes that logically belong together

                            * Have access to their enclosing class’scope

                           

                            內部類的特性:

                            * 當你   

             

       二、接口

接口定義了一系列的抽象方法和變量。Java中使用interface來聲明一個接口。下面是一個接口的通用形式:

access interface name {

       return-type method-name1(parameter-list);

       return-type method-name2(parameter-list);

       type final-varname1=value;

       type final-varname2=value;

       // ….

       return-type method-nameN(parameter-list);

       type final-varnameN=value;

}

這裏access要麼是public,要麼就沒有用修飾符。接口中的方法沒有方法體,雖然沒有聲明爲abstract的,但它們本質上是抽象方法。在接口中指定的方法沒有默認的實現,每個包含接口的類必需實現所有的方法。接口中可以聲明變量,它們一般是finalstatic型的,它們必須以常量值初始化。如果接口本身定義成public的,那麼接口裏的所有方法和變量都是public的。

一旦接口被定義,那麼一個或多個類可以實現該接口。一個類也可以實現多個接口。一個包括implements子句的類的一般形式如下:

              access class classname [extends superclass] [implements interface[,interface…] ] {

                     // class body

              }

       這裏access要麼是public的,要麼是沒有修飾符的。類中實現接口的方法必須聲明成public,而且實現方法的類型必須嚴格與接口定義中指定的類型匹配。

 

你可以聲明一個接口類型的引用變量,而不是實現接口的類的引用。任何實現了該接口的類的實例都可被這樣一個變量引用。

       例子:

package Examples;

interface Callback{                //這裏聲明瞭一個接口

       void callMethod(int param);

}

      

class Client implements Callback{

       public void callMethod(int p){       //當實現接口的一個方法時,該方法必須被聲明爲public

              System.out.println("callMethod called with "+p);

       }

       void clientMethod(){        //類在實現接口的同時也可以定義自己的方法、變量             

              System.out.println("Client/'s own method!");

       }

}

        

class InterfaceTest{

       public static void main(String[] args){

              Callback cb;   //聲明一個接口類型的引用變量

              Client c1=new Client();

              c1.callMethod(10);

              cb=c1;

              cb.callMethod(99); //這樣cb也可以調用callMethod()方法

       }

}

 

如果一個類包含一個接口但是不完全實現接口定義的方法,那麼該類必須定義成abstract型的。

你可以使用接口來引入多個類的共享常量,這樣做只需要簡單的聲明一個接口,該接口包含被初始化的變量既可。如果一個類實現這樣一個接口,那麼接口中的所有變量名都將作爲常量看待。

接口可以通過使用extends繼承其他接口,當一個類實現一個繼承了另一個接口的接口時,它必須實現接口繼承鏈表中定義的所有方法。

              這裏總結一下接口的知識:

1)使用interface關鍵字,必須聲明爲public或者不加任何修飾符。接口的修飾符隱含決定接口內方法和變量的修飾符。

2)實現接口的類同接口一樣,修飾符要麼是public的,要麼沒有修飾符。而且如果類中實現了某個接口中的方法,那麼所有實現的方法必須聲明爲public的。如果有類implements某個接口,但沒有實現接口中所有的方法,那麼該類必須聲明爲abstract的。

       3)可以聲明一個接口的引用變量,那麼所有實現此接口的類的對象引用都可以賦給此引用變量。

4)一個接口可以繼承(extends)其他接口,實現一個繼承了其他接口的接口時,必須實現該接

   口中聲明的和所繼承的所有其他接口中的方法。

 

       三、方法重載

              Java中,同一個類中的2個或2個以上的方法可以有同一個名字,只要它們的參數聲明不同即可。 在這種情況下,該方法就被稱爲重載方法(overloaded method)。方法重載是Java實現多態性的一種方式。當一個重載方法被調用時,Java用參數的類型和(或)數量來表明實際調用的是哪個方法。因此,每個重載方法的參數的類型和(或)數量必須是不同的。雖然每個重載方法可以有不同的返回類型,但返回類型並不足以區分所使用的是哪個方法。當Java調用一個重載方法時,參數與調用匹配的方法被執行。

 

       四、垃圾回收

              由於使用new運算符來爲對象動態地分配內存,你可能想知道這些對象是如何撤銷的以及他們的內存在以後的重新分配時是如何釋放的。在一些語言,例如C++中,用delete運算符來手工地釋放動態分配的對象的內存。Java使用一種不同的、自動地處理重新分配內存的方法:垃圾回收(garbage collection)技術。由JVM自動進行垃圾回收。當你的程序不再引用某對象時,則該對象就會被考慮收集到垃圾收集堆裏。僅因爲你的程序失去對一個對象的引用,並不意味着JVM會立刻收回這個對象的內存,甚至根本不收回;Java將垃圾收集進程作爲一個低優先級線程運行,JVM僅會在需要更多的內存以繼續執行程序時纔會進行垃圾收集。                                                                                                                                                                               例如:

package Examples;

       public class GLDemo{

              public static void main(String[] args){

                     String s=”Hello”

                     String s= s + new String(“World”);  // s指向一個新的對象”HelloWorld”

//此時字符串對象”Hello”可以被作爲垃圾收集

                     int[] arr={3,4,5,6,7};

                     arr[2]=99;

                     arr=new in[10];            // arr指向一個新的數組,數組對象{3, 4 , 99 , 6 , 7}可以被作爲垃圾收集

 

                     s=null;                          // s設置爲null使我們失去了對字符串對象”HelloWorld”的引用,

// 所以這個對象也可被作爲垃圾收集

              }

       }

             

      

 *  finalize()方法:

       有時當撤銷一個對象時,需要完成一些操作。例如,如果一個對象正在處理的是非Java資源,如文件句柄或window字符字體,這時你要確認一個對象被撤銷以前要保證這些資源被釋放。爲處理這樣的狀況,Java提供了被稱爲收尾(finalization)的機制。使用該機制你可以定義一些特殊的操作,這些操作在一個對象將要被垃圾回收程序釋放時執行。要給一個類增加收尾(finalizer),你只要定義finalize()方法即可。Java回收該類的一個對象時,就會調用這個方法。在finalize()方法中,你要指定在一個對象被撤銷前必須執行的操作。

       finalize()方法的通用格式如下:

              protected void finalize(){}

Java會爲程序中每一個對象只調用一次finalize()方法。finalize()被聲明爲protected,不返回任何值,而且拋出一個Throwable對象。你也可以調用自己對象的finalize()方法。如果你覆蓋finalize()方法時,則一定要調用超類的此方法。

      

       顯示的調用垃圾收集程序:

              分成兩步:1、獲取一個代表當前運行時的對象;2、調用這個對象的gc()方法。下面是這種步驟的一個程序片斷:

              Runtime rt=Runtime.getRuntime();

              rt.gc();

       Runtime對象定義了四個方法:

              gc() :在這個方法返回前,JVM會已經執行了垃圾收集。

              runFinalization() :在這個方法返回前,JVM已經爲所有還沒有運行finalize()方法的等候垃圾收集

                                           的對象執行了finalize()方法。

              totalMemory()      這個方法返回一個int值,它包括JVM中爲分配對象所提供的可用內存總數。

              freeMemory()      這個方法返回一個int值,這個數值總是小於totalMemory()的返回值。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章