1. 程式人生 > >Java 繼承 抽象類 介面

Java 繼承 抽象類 介面

JAVA繼承 抽象類 介面
作者: 嗯呢
一.類的繼承
      通過繼承可以實現程式碼的複用,被繼承的類稱為父類或超類(superclass),由繼承而得到的類稱為子類(subclass)。一個父類可以擁有多個子類,但一個類只能有一個直接父類,這是因為JAVA語言中不支多重繼承。

      子類繼承父類的成員變數和成員方法,同時可以修改父類的成員變數或重寫父類的方法,還可以新增新的成員變數或成員方法。

      JAVA語言中有一個名為java.lang.Object的特殊類,所有的類都是直接或間接地繼承該類而得到的。

      1.子類的建立
           類的繼承是通過extends關鍵字來實現的,在定義類時若使用ectends關鍵字指出新定義類的父類,就是在兩個類之間建立了繼承關係。新定義的類稱為子類,它可以從父類那裡繼承所有非private的成員作為自己的成員。

   子類的建立:

 * 格式:  class SubClass extends SuperClass   //SubClass為子類,SuperClass為父類

 *      {

 *         .

 *         .

 *         .

 *       }

     2.呼叫父類中特定的構造方法
         在沒有明確地指定構造方法時,子類還是會先呼叫父類中沒有引數的構造方法,以便進行初始化的操作。在子類的構造方法中可以通過super()來呼叫父類特定的構造方法。

 

例://以Person作為父類,建立學生子類Student,並在子類中呼叫父類裡某指定的構造方法。

class Person2

{

       private String name;

       private int age;

       publicPerson2()//定義Person2類的無參構造方法

       {

              System.out.println("呼叫了Person2類的無參構造方法");

       }

       publicPerson2(String name,int age)//定義Person2類的有參構造方法

       {

              System.out.println("呼叫了Person2類的有參構造方法");

              this.name=name;

              this.age=age;

       }

       publicvoid show()

       {

              System.out.println("姓名:"+name+"  年齡:"+age);

       }

}

class Student2extends Person2//定義繼承自Person2類的子類Student2

{

       privateString department;

       publicStudent2()//定義Student2類的無參構造方法

       {

              System.out.println("呼叫了學生類的無參構造方法Student2()");

       }

       publicStudent2(String name,int age,String dep)//定義Student2類的有參構造方法

       {

              super(name,age);//呼叫父類的無參構造方法

              department=dep;

              System.out.println("我是"+department+"學生");

              System.out.println("呼叫了學生類的有參構造方法Student2(Stringname,int age,String dep)");

       }

}

public class App8_2 {

       publicstatic void main(String[] args)

       {

              Student2stu1=new Student2();//建立物件,並呼叫無參構造方法

              Student2stu2=new Student2("李小四",23,"資訊系");//建立物件並呼叫有參構造方法

              stu1.show();

              stu2.show();

       }

}

 

/*在子類中訪問你類的構造方法,其格式為super(引數列表)。

 * super()可以過載,也就是說,super()會根據引數的個數與型別,執行父類相應的構造方法。

 * 呼叫父類構造方法的super()語句必須寫在子類構造方法的第一行。

 *super()與this()的功能相似,但super()是從子類的構造方法呼叫父類的構造方法, 而this()則是在同一個類內呼叫其他的構造方法。

 *super()與this()均必須放在構造方法內的第一行,也就是這個原因,super()與this()無法同時存在同一個構造方法內。

 * 與this關鍵字一樣,super指的也是物件,所以super同樣不能在static環境中使用,包括靜態方法和靜態初始化器(static語句塊)。

 

      3.在子類中訪問父類的成員
         例://在學生子類Student中訪問父類Person的成員。用protected修飾符和super關鍵字訪問父類的成員

class Person3

{

       protectedString name;//用protected(保護成員)修飾符修飾

       protectedint age;

       publicPerson3()//定義Person3類的“不做事”的無參構造方法

       {

             

       }

       publicPerson3(String name,int age)//定義Person3類的有參構造方法

       {

              this.name=name;

              this.age=age;

       }

       protectedvoid show()

       {

              System.out.println("姓名:"+name+"   年齡:"+age);

       }

}

class Student3 extends Person3//定義子類Student3,其父類為Person3

{

       privateString department;

       intage=20;//新添加了一個與父類的成員變數age同名的成員變數


       publicStudent3(String xm,String dep)//定義Student3類的有參構造方法

       {

              name=xm;   //在子類裡直接訪問父類的protected成員name

              department=dep;

              super.age=25;   //利用super關鍵字將父類的成員變數age賦值為25

              System.out.println("子類Student3中的成員變數age="+age);

              super.show();       //去掉super而只寫show()亦可,這是呼叫父類中的成員方法

              System.out.println("系別:"+department);

       }

}

public class App8_3 {

       publicstatic void main(String[] args)

       {

              @SuppressWarnings("unused")

              Student3stu=new Student3("李小四","資訊系");

       }

}

 

/*在子類中訪問父類的成員:

 * 在子類中使用super不但可以訪問父類的構造方法,還可以訪問父類成員就是和成員方法,

 * 但super不能訪問在子類中新增的成員。子類中訪問父類成員的格式如下:

 * 1.  super.變數名;     2.super.方法名;

 * 用protected修飾的成員可以被三種類所引用:該類自身、與它在同一個包中的其他類、在其他包中該的子類。

 * 將成員宣告為protected的最大好處是可以同時兼顧到成員的安全性和便利性。

     4.覆蓋
       過載是指在同一個類內定義名稱相同,但引數個數或型別不同的方法。

       覆蓋(overriding):是指在子類中,定義名稱,引數個數與型別均與父類完全相同的方法,用於重寫父類裡同名方法的功能。

       

例://以個人類Person為父類,建立學生子類Student,並用子類中的方法覆蓋父類的方法。

class Person4

{

       protectedString name;

       protectedint age;

       publicPerson4(String name,int age)

       {

              this.name= name;

              this.age= age;

       }

       protectedvoid show()

       {

              System.out.println("姓名:"+name+"  年齡:"+age);

       }

}

class Student4 extendsPerson4

{

       privateString department;

       publicStudent4(String name,int age,String dep)

       {

              super(name,age);

              department= dep;

       }

       protectedvoid show()//覆蓋父類Person4中的同名方法

       {

              System.out.println("系別:"+department);

       }

}

public class App8_4

{

       publicstatic void main(String[] args)

       {

              Student4stu = new Student4("王永濤",24,"電子");

              stu.show();

       }

}

 

 *覆蓋父類的方法:子類在重新定義父類已有的方法時,應保持與父類完全相同的方法宣告,即應與父類有完全相同的方法名、返回值型別和引數列表,否則就不是方法的覆蓋,而是子類定義自己的與父類無關的方法,父類的方法末被覆蓋,所以仍然存在。

 *注意: 子類中不能覆蓋父類中宣告為final或static的方法。   在子類中覆蓋父類的方法時,可以擴大父類中方法許可權,但不可以縮小父類方法的許可權。

     5.用父類的物件訪問子類的成員
例://利用父類Person5的物件呼叫子類Student5中的成員。與不可被繼承的成員與最終類

class Person5

{

       protected String name;//如果宣告為靜態最終變數,則不能被覆蓋。

       protected int age;

       publicPerson5(String name, int age)

       {

              this.name= name;

              this.age= age;

       }

       protectedvoid show()

       {

              System.out.println("姓名:"+name+"  年齡:"+age);

       }

}

class Student5extends Person5

{

       privateString department;

       publicStudent5(String name,int age, String dep)

       {

              super(name,age);

              department= dep;

       }

       protectedvoid show()

       {

              System.out.println("系別:"+department);

       }

}

public class App8_5

{

       publicstatic void main(String[] args)

       {

              Person5per = new Student5("王永濤",24,"電子");//宣告父類變數per指向子類物件

              per.show();    //利用父類物件per呼叫show()方法

       }

}

 

通過父類的物件訪問子類的成員,只限於“覆蓋”的情況發生時。

    6.不可被繼承的成員與最終類
 * 如果父類的成員不希望被子類的成員所覆蓋,可以將它們宣告為final。

 *

 * 如果就final來修飾成員變數,則說明該成員變數是最終變數,即常量,程式中的其他部分可以訪問,但不能修改。

 * 如果用final修飾成員方法,則該成員方法不能被子類所覆蓋,即該方法是最終方法。

 *

 * 所以已被private修飾符限定為私有的方法,以及所有包含在final類中的方法,都被預設是final的。

 *

 *final成員變數與final區域性變數的區別:final成員變數和定義在方法中的final區域性變數,它們一旦給定,就不能更改。

 * 大體上說,final成員變數和final區域性變數都是隻讀量,它們能且只能被賦值一次,而不能賦值多次。

 *

 * 一個成員變數若被staticfinal兩個修飾符所限定時,它實際的含義,就是常量,所以在程式中通常用static和final一起來指定一個常量。

 *

 * 定義一個成員變數時,若只用final修飾而不用static修飾,則必須且只能賦值一次,不能預設。這種成員變數的賦值方式有兩種:

 * 一種是在定義變數時賦初值;另一種是在某個構造方法中進行賦值。

二.Object類
      1.equals()方法
  equals()方法:判斷兩個物件是否相等。

 equals()方法與==運算子比較方式的一同:“==”運算子用於比較兩個變數本身的值,即兩個物件在記憶體中的首地址,而equals()方法則是比較兩個字串中所包含的內容是否相同;而對於非字串型別的變數來說,“==”運算子和equals()方法都用來比較其所指物件在堆記憶體中的首地址。換句話說,“==運算子和equals()方法都是用來比較兩個類型別的變數是否指向同一個物件。”

     2.toString()方法與getClass()方法
       toString()方法:toString()方法的功能是將呼叫該方法的物件的內容轉換成字串,並返回其內容,但返回的是一些沒有意義且看不懂的字串。

 * 因此,如果要用toString()方法返回物件的內容,可以重新定義該方法用於覆蓋父類中的同名方法以滿足需要。

 *

 *getClass()方法:該方法的功能是返回執行該方法的物件所屬的類。getClass()方法返回值是Class型別。

      3.物件運算子instanceof
    物件運算子instanceof:判斷一個指定物件是否是指定類或它的子類的例項,若是,則返回true,否則返回false。

 getName()方法返回一個類的名稱,返回值是String型別。

 getSuperclass()方法獲得父類。

 

 例://運算子instanceof及getName()、getSuperclass()方法的使用。

 public class Person8

{

       staticint count = 0;

       protectedString name;

       protectedint age;

       @SuppressWarnings("static-access")

       publicPerson8(String n1,int a1)

       {

              name= n1;

              age= a1;

              this.count++;

       }

       publicString toString()

       {

              returnthis.name+" , "+this.age;

       }

       @SuppressWarnings("static-access")

       publicvoid display()

       {

              System.out.println("本類名="+this.getClass().getName()+";");

              System.out.println("父類名="+this.getClass().getSuperclass().getName());

              System.out.println("Person.count="+this.count+"");

              System.out.println("Student.count="+Student8.count+"");

              Objectobj=this;

              if(objinstanceof Student8)

                     System.out.println(obj.toString()+"是Student類物件。");

              else

                     if(objinstanceof Person8)

                            System.out.println(obj.toString()+"是Person類物件");

       }

}

class Student8 extends Person8

{

       staticint count = 0;

       protectedString dept;

       @SuppressWarnings("static-access")

       protectedStudent8(String n1,int a1,String d1)

       {

              super(n1,a1);

              dept= d1;

              this.count++;

       }

       publicString toString()

       {

              returnsuper.toString()+","+dept;

       }

       @SuppressWarnings("static-access")

       publicvoid display()

       {

              super.display();

              System.out.print("super.count="+super.count);

              System.out.println(";this.count="+this.count);

       }

       publicstatic void main(String[] args)

       {

              Person8per = new Person8("王永濤",23);

              per.display();

              Student8stu = new Student8("張小三",22,"計算機系");

              stu.display();

       }

}

 

三.抽象類
     1.抽象類與抽象方法
    抽象類是以修飾符abstract修飾的類,定義抽象類的語法格式如下:

    abstract class 類名

   {

      聲名成員變數;

      返回值的資料型別方法名(引數表)  ——————一般方法

      {

         ……

      }

       abstract 返回值的資料型別方法名(引數表); ——————抽象方法

   }

 

 注意:

       1.由於抽象類是需要被繼承的,所以abstract類不能用final來修飾。也就說,一個類不能既是最終類,又是抽象類,即關鍵字abstract與final不能合用。

       2.abstract不能與private、stastic、final或native並列修飾同一方法。

       3.抽象類的子類必須實現父類中的所有方法,或者將自己也宣告為抽象的。

       4.抽象類中不一定包含抽象方法,但包含抽象方法的類一定要宣告為抽象類。

       5.抽象類可以有構造方法,且構造方法可以被子類的構造方法所呼叫,但構造方法不能被宣告為抽象的。

       6.一個類被定義為抽象類,則該類就不能用new運算子建立具體例項物件,而必須通過覆蓋的方式來實現抽象類中的方法。

       7.抽象方法在抽象方法裡,不能定義方法體。

 

例://抽象類的應用舉例。定義一個形狀抽象類Shape,以該形狀抽象類為父類派生出圓形子類Cricle和矩形子類Pectangle。

abstract class Shape9//定形狀抽象類Shape

{

       protected String name;

       publicShape9(String xm)   //抽象類中一般方法,本方法是構造方法

       {

              name= xm;

              System.out.print("名稱:"+name);

       }

       abstractpublic double getArea();//宣告抽象方法,沒有方法體

       abstractpublic double getLength();//宣告抽象方法

}

class Cricle9extends Shape9//定義繼承自Shape9的圓形子類Cricle9

{

       privatedouble pi=3.14;

       privatedouble radius;

       publicCricle9(String shapeName,double r)

       {

              super(shapeName);

              radius=r;

       }

       public double getArea()      //實現抽象類中的getArea()方法

       {

              returnpi*radius*radius;

       }

       publicdouble getLength()   //實現抽象類中的getLength()方法

       {

              return2*pi*radius;

       }

}

class Rectangle9 extends Shape9   //定義繼承自Shape9的圓形子類Rectangle9

{

       privatedouble width;

       privatedouble height;

       publicRectangle9(String shapeName,double width,double height)

       {

              super(shapeName);

              this.width=width;

              this.height=height;

       }

       publicdouble getArea()

       {

              returnwidth*height;

       }

       publicdouble getLangth()

       {

              return2*(width+height);

       }

       @Override

       publicdouble getLength() {

              //TODO 自動生成方法存根

              return0;

       }

}

public class App8_9

{

       publicstatic void main(String[] args)

       {

              Shape9 rect=new Rectangle9("長方形",6.5,10.3);//宣告父類物件指向子類

              System.out.print(";面積="+rect.getArea());      物件

              System.out.println(";周長="+rect.getLength());

              Shape9 circle=new Cricle9("圓",10.2);

              System.out.print(";面積="+circle.getArea());

              System.out.println(";周長="+circle.getLength());   

       }

}

四.介面
     1.介面的定義、實現與引用
介面與抽象類的不同:

 1.介面的資料成員必須初始化。

 2.介面中的方法必須全部都宣告為abstract,也就是,介面不能像抽象類一樣擁有一般的方法,而必須全部是抽象方法。

 * 介面定義的語法格式如下:

 *[public] interface 介面名稱 [extends 父介面名列表]

 * {

 *    [public] [static] [final] 資料型別 成員變數名=常量;

 *     ……

 *    [public] [abstract] 返回值的資料型別 方法名(引數名);

 *     ……

 * }

 * 其中interface前的public修飾符可以省略。若省略,介面使用預設的訪問控制,即介面只能被與它處在同一包中的成員訪問。

一。 接口裡的“抽象”方法只需要做宣告,不用定義其處理資料的方法體。

二。 成員就是必須賦初值。

 * 介面的實現與引用:

 * 介面的實現是在宣告一個類的同時用關鍵字implements來實現一個介面。  介面實現的語法格式為:

 *class 類名稱 implements介面名錶

 * {

 *    ……

 * }

  一個類要實現一個介面時,注意以下問題:

  1.如果實現某介面的類不是abstract的抽象類,則在類的定義部分必須實現指定介面的所有抽象方法。

   2.一個類在實現某介面的抽象方法時,必須使用完全相同的方法頭。否則,只是在定義一個新方法,而不是實現已有的抽象方法。

   3.介面中抽象方法的訪問控制修飾符都已指定為public,所以類在實現方法時,必須顯示地使用public修飾符,否則將被系統警告為縮小了介面中定義的方法的訪問控制範圍。

     2.介面的繼承與多重繼承
與類相似,介面也有繼承性。定義一個介面時可通過extends關鍵字宣告該新介面是某個已存在的父介面的子介面,它將繼承父介面的所有變數與方法。與類的繼承不同是,一個介面可以有多個父介面,它們之間用逗號分隔,形成父介面列表。如果有子介面中有與父介面同名的常量或相同的方法,則父介面中常量被隱藏方法被覆蓋。

 

例://介面繼承

interface Face1

{

      double pi=3.14;

      abstractdouble area();

}

interface Face2

{

      abstractvoid setColor(String c);    //可以有引數但不能方法體

}

interface Face3 extendsFace1,Face2  //介面的多重繼承,也是使用關鍵字

{                                extends,不同於類,一個介面可以有

      abstractvoid volume();             多個父介面,逗號分隔。

}

public class Cylinder11 implements Face3

{

      private double radius;

      private int height;

      protected String color;

      public Cylinder11(double r,int h)

      {

             radius=r;

             height=h;

      }

      public double area()    //返回值型別相同,方法名相同

      {

             returnpi*radius*radius;

      }

      public voidsetColor(String c)

      {

             color=c;

             System.out.println("黑色:"+color);

      }

      public void volume()   //返回值型別相同,方法名相同

      {

             System.out.println("圓柱體體積="+area()*height);

      }

      public static voidmain(String[] args)

      {

             Cylinder11volu=new Cylinder11(3.0,2);

             volu.setColor("紅色");

             volu.volume();

      }

}

 

五.內部類與匿名類
      1.內部類
    內部類:內部類(inner class)也稱“巢狀類”是定義在類中的類,內部類的主要作用是將邏輯上相關的類放到一起。包含內部類稱為“外部類”,內部類與一般類相同。

注意:

    1.內部類不能與外部類同名。

    2.在外部引用內部類時,則必須在內部類名前冠以其屬外部類的名字才能使用。

    3.在用new運算子建立內部類時,也要在new前面冠以物件變數。

 

說明:

    1.在檔案管理方面,內部類在編譯完成之後,所產生的檔名稱為“外部類名$內部名類名.class”。

    2.內部類可以被宣告為private或protected。因些外部類與內部類的訪問原則是:在外部類中,通過一個內部類的物件引用內部類中的成員;反之,在內部類中可以直接引用它的外部類的成員,包括靜態成員、例項成員及私有成員。內部類也可以通過建立物件從外部類之外被呼叫,但必須將內部類宣告為public的。

 

 內部類具有如下特性:

1.內部類的前面用final修飾,則表明該內部類不能被繼承。

2.內部類可以定義為abstract,但需要被其他的內部類繼承或實現。

3.內部類不能與包含它的外部類名相同。

4.內部類也可以是一個介面,該介面必須由另一個內部類來實現。

5.內部類不但可以在類裡定義,也可以在程式塊之內定義內部類。

6.內部類如果被宣告為static,則靜態內部類將自動轉化為“頂層類”(top level class),即它沒有父類,而且不能引用外部類成員或其他內部類中的成員。非靜態內部類不能宣告靜態成員,只有靜態內部類才能宣告靜態成員。

 

例://內部類與外部類的訪問規則

public class Group13

{

      private int age;//宣告外部類的私有成員變數

      publicclass Student13  //宣告內部類               ———————      {

             Stringname;//宣告內部類的成員變數

             publicStudent13(String n,int a)//定義內部類的構造方法        內

             {

                    name=n;//訪問內部類的成員變數name                  部               

                    age=a;//訪問外部類的成員變數age

             }                                                      類

             publicvoid output()//內部類的成員方法 

             {

                    System.out.println("姓名:"+name+"  年齡:"+age);

             }

      }    //                                          ———————

      public void output()//定義外部類的成員方法

      {

             Student13 stu=newStudent13("劉 洋",24);     //建立內部類物件stu

             stu.output();

      }

      public static voidmain(String[] args)

      {

             Group13 g=newGroup13();               //用g呼叫外部類的方法

             g.output();

      }

}

 

      2.匿名內部類
匿名類(anonymousclass): 匿名類是一種特殊的內部類,它沒有類名,在定義類的同時,就生成該類的一個例項,由於不會在其他地方用到該類,所以不用取名字,因而又被稱為匿名內部類。

這種類不取名字,而是直接用其父類的名字或者它所實現的介面名字,而且匿名內部類的定義與建立該類的一個例項同時進行,即類的定義前面一個new運算子,而不是使用關鍵字class,同時帶上圓括號“()”表示建立物件。

 

格式如下:

 (  //建立匿名內部類,並執行所定義的方法

    new 類名()  //括號"()"內不允許有引數

    {

      方法名(引數,,引數2,···,引數n)

      {

          方法體語句;

       }

     }

  ).方法名(引數1,引數2,···,引數n)

 

說明: 匿名內部類名前不能有修飾符,也不能定義構造方法,因為它沒有名字,也正是這個原因,在建立物件時也不能帶引數,因為預設構造方法不能帶引數

 

例://匿名內部類的使用方法

public class App8_14

{

      public static voidmain(String[] args)

      {

             (          

               new Inner()//建立匿名內部類Inner的物件

               {

                    voidsetName(String n)

                    {

                           name=n;

                           System.out.println("姓名:"+name);//以上三句是為彌補內部

                    }                           //類Inner裡沒有定義到的方法

                }                    //  建立匿名內部類Inner的物件

              ).setName("張  華");     //執行匿名內部類裡所定義的方法

      }

    static class Inner//定義內部類

    {

         String name;

    }

}