第十一章:繼承和多態
一.父類和子類
1.繼承可以使你創建一個類(父類),之後可以擴充該類爲一個更加特定的類(子類)
public class Circle extends GeometricObject{}
Circle爲子類,也可稱爲次類、派生類、擴展類
GeometricObject爲父類,也成爲基類,超類
extends爲繼承關鍵字
2.注意
- 子類不是父類的子集,子類通常比父類包含更多的信息和方法
- 父類中的私有成員變量子類不可訪問,但是可以訪問父類中公共的訪問器和修改器
- 不是“是一種”就是繼承關係,例如square是矩形的一種,但是不能用square類來擴展矩形類,因爲width和height不適合正方形
- 父類和子類之間必須存在“是一種”的關係,但是不能盲目建立繼承關係
- java中不予許多繼承,一個Java類只能直接繼承自一個父類,但是可以通過接口來實現多繼承
幾個類派生出一個子類稱爲多繼承
二.使用super關鍵字
關鍵字super指代父類,可以用於
- 調用父類中的構造方法
super();//調用父類無參構造
super(參數);//調用有參構造
- 調用父類中的普通方法
使用super()或super(參數)必須出現在子類構造方法的第一行,是顯式調用父類的構造方法的唯一方式
public class GeometricObject {//定義父類
private String color;//顏色
private boolean filled;//是否填充
public GeometricObject() {
this("white", false);//調用重載的構造方法
}
public GeometricObject(String color, boolean filled) {
this.color = color;//this調用對象引用的隱藏數據域
this.filled = filled;
}
}
public class Circle extends GeometricObject {//Circle類繼承GeometricObject 類
private double radius;
public static final double PI = 3.14;
public Circle() {
this(1.0, "black", true);
}
public Circle(double radius, String color, boolean filled) {
super(color, filled);//調用父類構造方法,必須放在第一行
this.radius = radius;
}
}
構造一個類的實例時, 將會調用沿着繼承鏈的所有父類的構造方法。 當構造一個子類的對象時, 子類構造方法會在完成自己的任務之前, 首先調用它的父類的構造方法。 如果父類繼承自其他類, 那麼父類構造方法又會在完成自己的任務之前,調用它自己的父類的構造方法。 這個過程持續到沿着這個繼承體系結構的最後一個構造方法被調用爲止。
下面這個例子是查資料得來的,更能反映構造方法鏈
public class Faculty extends Employee{
public Faculty(){
System.out.println("(3)Performs Faculty's tasks") ;
}
public static void main(String[] args){
Faculty fac=new Faculty();
}
}
class Employee extends Person {
public Employee(){
System.out.println("(2)Performs Employee's tasks");
}
}
class Person {
public Person(){
System.out.println("(1)Performs Person's tasks");
}
}
//運行結果
//(1)Performs Person's tasks
//(2)Performs Employee's tasks
//(3)Performs Faculty's tasks
子類中沒有顯式定義構造方法,默認無參構造被隱式調用,並默認調用父類的無參構造,然而父類中沒有定義無參構造,因此無法編譯成功
public class GeometricObject {
private String color;
private boolean filled;
public GeometricObject(String color, boolean filled) {
this.color = color;
this.filled = filled;
}
//父類中沒有定義無參構造
}
public class Circle extends GeometricObject {
private double radius;
public static final double PI = 3.14;
//默認調用無參構造和父類的無參構造
}
所以:如果要設計一個可以被繼承的類,最好提供一個無參構造方法避免程序設計錯誤
三.方法重寫
1.要重寫一個方法,就要和父類定義一樣的簽名和返回值類型來對該方法進行定義
public class GeometricObject {
@Override
public String toString() {//父類toString方法
return "GeometricObject [color=" + color + ", filled=" + filled + "]";
}
}
public class Circle extends GeometricObject {
@Override//只有父類的實例方法是可訪問時才能被覆蓋
public String toString() {//子類重寫父類toString方法,方法簽名和返回值類型相同
return super.toString()+" radius:"+getRadius();//重寫方法必須使用super.方法名 調用父類的方法
}
}
public static void main(String[] args) {//測試
// TODO Auto-generated method stub
Circle circle2=new Circle(3.0,"black",true);
System.out.println(circle2.toString());//GeometricObject [color=black, filled=true] radius:3.0
}
與實例方法相同,靜態方法也能被繼承,但是靜態方法不能被覆蓋,如果父類重定義的靜態方法在子類中被重寫,那麼父類中的靜態方法將會被隱藏
2.重寫與重載的區別
**重載:**相同的方法名不同的簽名定義多個方法
**重寫:**在子類中提供一個對方法的新的實現,簽名和返回值類型必須相同
方法重寫發生在因爲繼承而相關的不同類中
方法重載可以發生在通一個類中,也可以發生在由於繼承而相關的不同類中
重寫標註@Override
如果使用了標註但是卻沒有重寫,就會報錯
重寫方法中必須使用super.function()調用父類的方法
3.Object類
java中所有的類都繼承自Object類
四.多態
1.多態意味着父類的變量可以指向父類的對象
2.每個子類的實例都是其父類的實例,反之不行
五.動態綁定
1.聲明類型和實際類型
Object o=new GeometricObject();
//Object聲明類型 GeometricObject實際類型
2.動態綁定是指可以沿着繼承鏈的多各個中實現
3.引用變量的聲明類型決定編譯時匹配哪個方法,實際類型決定動態綁定方法的實現
public static void main(String[] args) {
// TODO Auto-generated method stub
printString(new Circle(3.0));//創建Circle的匿名對象
printString(new Rectangle("black", true, 5.0, 6.0));//創建Rectangle的匿名對象
}
public static void printString(GeometricObject go) {//動態調用toString方法
System.out.println(go.toString());
//運行結果:
//GeometricObject [color=white, filled=false] radius:3.0
//GeometricObject [color=black, filled=true] width:5.0 high:6.0
六.對象轉換和instanceof
1.對象的引用可以可以類型轉換爲另一對象的引用,稱爲對象轉換
Object o=new Student();//向下轉換
//但是 Student s=o; 錯誤,需要顯式轉換
Student s=(Student)o;//向上轉換,顯式
嘗試顯式轉換時確保一個對象是另一個對象的實例,利用instancesOf關鍵字實現
public static void main(String[] args) {
// TODO Auto-generated method stub
GeometricObject[] go = { new Circle(5.0), new Rectangle("black", true, 5.0, 6.0),
new GeometricObject("blue", true) };//創建一個GeometricObject類型的數組
for (GeometricObject i : go) {//遍歷數組中元素
if (i instanceof Circle) {//i是否是Circle的實例
System.out.println(((Circle) i).getArea());//因爲GeometricObject 類中沒有getArea方法,所以要顯式轉換
} else if (i instanceof Rectangle) {//i是否是Rectangle的實例
System.out.println(((Rectangle) i).getPrimeter());//因爲GeometricObject 類中沒有getPrimeter方法,所以要顯式轉換
} else {//i是否是GeometricObject的實例
System.out.println(i.toString());
}
}
}
//運行結果:
//78.5
//22.0
//GeometricObject [color=blue, filled=true]
爲什麼沒有一開始就定義爲Circle類型或Rectangle類型呢?
是爲了能夠通用設計,把變量定義爲父類,這樣就可以接收任何子類型的值
2.對象成員訪問運算符(.)優先於類型轉換。所以要先轉換在打點調用方法
例:((Rectangle) i).getPrimeter()
3.注意
轉換基本類型會返回一個新的值,但是轉換對象不回創建新的對象
Object o=new Circle();
Circle c=(Circle)o;
//o和c指向同一個對象
七.Object類中equals()方法
1.在子類中,使用簽名equals(SomeClassName obj)(例如:equals(Circle c)重寫equals方法是錯誤的,應該使用equals(Object obj))
@Override
public boolean equals(Object obj) {
if(obj instanceof Circle) {//判斷Circle是否是obj的實例
return this.radius==((Circle)obj).getRadius();//比較兩個的半徑是否相等
}
else
return this==obj;
}
八.ArrayList類
1.Arraylist可以用於存儲對象列表
2.可以存儲不限個數的對象
3.ArrayList是一個泛型類,具有一個泛型類型E
4.語法
ArrayList<E> list=new ArrayList<E>();
//jdk1.7之後可以去掉這個E
5.方法
ArrayList類中包含添加、清除、是否包含、查找、刪除、修改等方法,在
下文中會提及
6.數組和ArrayList的區別
數組 | ArrayList | |
---|---|---|
定義 | int[] array=new array[5] | ArrayList<> list=new ArrayList<>() |
引用 | array[index] | list.get(index) |
更新元素 | array[index]=b | list.set(index,b) |
返回大小 | a.length | list.size() |
添加新元素 | list.add() | |
插入新元素 | list.add(index,b) | |
刪除一個元素 | list.remove(index)或list.remove(b) | |
刪除所有 | list.clear() | |
排序 | java.util.Arrays.sort(array) | java.util.Collections.sort(list) |
ArrayList中存儲的都是對象,所以要用基本類型的包裝類型
ArrayList<int> list=new ArrayList<>();//錯誤
ArrayList<Integer> list=new ArrayList<>();//正確
注意
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.remove(1);
System.out.println(list);//[1]刪掉的是下標所對應的元素
//如果就想刪掉1 用用list.remove((Integer)1);
}
7.數組和數組列表之間的轉換
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str= {"hello","java","fun"};
ArrayList<String> list = new ArrayList<>(Arrays.asList(str));//將數組轉爲數組列表
System.out.println(list);//[hello, java, fun]
String[] str2=new String[list.size()];
list.toArray(str2);//將數組列表轉爲數組
for(String i:str2) {
System.out.print(i+" ");//hello java fun
}
}
8.求列表中最大和最小以及打亂列表
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(i);
}
System.out.println(java.util.Collections.max(list));//獲取最大值 9
System.out.println(java.util.Collections.min(list));//獲取最小值 0
java.util.Collections.shuffle(list);//打亂數組列表
System.out.println(list);//[5, 7, 4, 3, 8, 9, 1, 2, 0, 6]
}
九.protected數據和方法
1.一個類的受保護成員可以從子類中訪問
2.允許子類訪問父類的數據域和方法,但不允許非子類訪問,使用protected關鍵字
3.可見性遞增:
私有的(private)、默認的(default)、受保護的(protected)、全局的(public)
4.protected允許包內類和包外子類訪問
5.子類可以重寫父類中protected修飾的方法,但是不能削弱方法的訪問性,只能增加。
十.防止擴展和重寫
1.一個被final修飾的類和方法都不能擴展
2.只有final可以用在方法的局部變量上
十一.總結
通過對本章的學習,我知道了父類和子類之間關係,學會使用super調用父類的構造方法和方法,一定記得放在第一句,學會了多態的含義(父類的變量可以指向子類的對象),知道了重寫和重載的區別,知道了動態加載的原因,懂得了向上顯式轉換,向下轉換和instanceof的用法,學會了ArrayList數組列表的方便,protected修飾符的用法以及使用final關鍵字修飾類和方法來防止擴展。
加油!第十二章待更……