Java 繼承 抽象類 介面
阿新 • • 發佈:2018-12-30
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; } }