java 面向物件三大特性之一:繼承
前言:
繼承,既是對現有類的複用。如果子類繼承了父類,那麼子類就擁有了父類的能力了。除此之外,子類還能加入自己新加入的方法,功能,或是重新定義從父類繼承的某項功能。這樣,就可以在現有的功能上開發新的功能,這樣能大大減少開發的週期.
|繼承的優點|
現在我們來構建一個Cat的類
package Test.practice.thinking_in_java.extend_test; /** * @Author 7aY * @Description: TODO() * @Date :Create in 13:202018/3/12 */ /** * 貓類 */ public class Cat { private int age;//年齡 private int weigth;//體重 private String name;//名字 public int getAge() { return age; } public Cat(int age,int weigth,String name){ this.age=age; this.weigth=weigth; this.name=name; } public void setAge(int age) { this.age = age; } public int getWeigth() { return weigth; } public void setWeigth(int weigth) { this.weigth = weigth; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void printMessage(){ System.out.println("name:"+getName()+" weigth:"+getWeigth()+"kg age:"+getAge()); } public Cat(){} }
這個Cat類包含體重 名字 身高等資訊,但現在我想要在這個Cat的基礎上建立一個EgyptianCat的類,我要重新把之前的程式碼重新再寫一次然後再新增新的東西下去嗎?很顯然這會十分的麻煩且無用,所以這時候extend 這個關鍵字發揮了他的用處了
public class EgyptianCat extends Cat { private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public EgyptianCat(int height,int weigth,String name,String color){ super(height,weigth,name); this.color=color; } @Override public void printMessage() { System.out.println("name:"+getName()+" weigth:"+getWeigth()+"kg age:"+getAge()+"color:"+getColor()); } }
和Cat相比,EgyptianCat就多了個一個私用屬性color,通過extend繼承,我們的類構建可以簡潔很多,並且可以在繼承的基礎上重新構建一個屬於我們新類的方法,這種方法叫做重寫方法(PS:重寫方法會呼叫上一層繼承的被重寫方法)
這裡我們重寫了父類的printMessage()方法,在列印資訊中新加入了color屬性
現在我們來new下父類和子類並且呼叫他們的printMessage方法檢視下結果
public static void main(String[] args) { Cat cat = new Cat(2,10,"jay"); cat.printMessage(); EgyptianCat egyptianCat = new EgyptianCat(3,5,"lois","black"); egyptianCat.printMessage(); }
輸出結果為:
name:jay weigth:10kg age:2
name:lois weigth:5kg age:3 color:black
|成員的繼承|
如果子類繼承了父類,則:
1.子類繼承父類的所有public與protect成員,無論子類是否與父類在同一個包中;
2.如果子類與父類在同一個包中,那麼子類也會繼承父類包訪問許可權
3.子類不能繼承父類的私有成員,但是子類可以通過父類的public或protect方法間接訪問父類的私有成員
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 22:212018/3/13
*/
public class SuperClass {
public int public_value;
protected int protect_value;
private int private_value;
public SuperClass(){
public_value=1;
protect_value=2;
}
public int getPrivate_value() {
return private_value;
}
public void setPrivate_value(int private_value) {
this.private_value = private_value;
}
}
在SuperClass這個類中我們宣告三種訪問許可權的變數,讓我們看下他的子類是怎麼繼承成員變數的
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 22:242018/3/13
*/
public class ExtendClass extends SuperClass {
public void printMessage() {
System.out.println("public value:"+public_value);
System.out.println("protect value"+protect_value);
// System.out.println("private value"+private_value); 並不能直接訪問私有屬性
System.out.println("private value:"+getPrivate_value());
}
public static void main(String[] args) {
ExtendClass extendClass = new ExtendClass();
extendClass.setPrivate_value(3);
extendClass.printMessage();
}
}
我們新建立了一個ExtendClass類,其中聲明瞭一個printMessage打印出成員變數的資訊,讓我們看下結果吧
public value:1
protect value2
private value:3
結果說明,子類能直接繼承父類的public和protect成員變數,但並不能直接繼承私有變數,但能通過setter和getter方法來訪問private變數
|繼承的向上轉型和向下轉型|
如果子類繼承了父類,那麼我們可以認為子類是父類的其中一種,是父類的特殊拓展版,例如Cat類繼承於Animal類,那麼我們可以把Cat看做為Animal,Cat具有Animal的所有特徵,但並不能把Animal看做為Cat,因為並不是所有的Animal都是Cat.
在Java中,我們可以把父類的引用賦值給子類的物件,然後通過父類的引用來進行操作,這種操作被稱為向上轉型,但是,子類的新增方法並不能進行使用和操作。我們不能把子類的引用賦值給父類的物件,就像前面所說的,可以把Cat看做為Animal,Cat具有Animal的所有特徵,但並不能把Animal看做為Cat,因為並不是所有的Animal都是Cat.
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 22:432018/3/13
*/
public class Animal {
public void walk(){
System.out.println("Animal walk!!");
}
public void sleep(){
System.out.println("Animal sleep!!");
}
public void eat(){
System.out.println("Animal eat!!");
}
}
我們現在建立一個Animal類,然後我們再建立一個Cat類繼承於Animal類
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 13:202018/3/12
*/
/**
* 貓類
*/
public class Cat extends Animal {
private int age;//年齡
private int weigth;//體重
private String name;//名字
public int getAge() {
return age;
}
public Cat(int age,int weigth,String name){
this.age=age;
this.weigth=weigth;
this.name=name;
}
public void setAge(int age) {
this.age = age;
}
public int getWeigth() {
return weigth;
}
public void setWeigth(int weigth) {
this.weigth = weigth;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printMessage(){
System.out.println("name:"+getName()+" weigth:"+getWeigth()+"kg age:"+getAge());
}
public Cat(){}
public void Meow(){
System.out.println("meow!!!!");
}
}
其中Cat類中新增了一個Meow的貓叫方法
現在我們來測試一下:
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 22:492018/3/13
*/
public class Test {
public static void main(String[] args) {
Animal cat = new Cat();
cat.eat();
cat.sleep();
cat.walk();
//但是不能呼叫Meow這個方法,現在我們把cat引用轉為Cat
cat=(Cat) cat;
((Cat) cat).Meow();
//Cat animal = new Animal(); 編譯器報錯,並不能向下轉型!
}
}
結果:
Animal eat!! Animal sleep!! Animal walk!! meow!!!!
正如之前所說的一樣,父類引用指向子類物件不能使用子類自己新增的方法,而且如果向下轉型的話編譯器會報錯,那麼向下轉型有什麼作用呢?之後我們會提到.
|多型的實現|
現在我們要編寫一個動物園類,檢查傳入的動物是什麼動物
package Test.practice.thinking_in_java.extend_test;
/**
* @Author 7aY
* @Description: TODO()
* @Date :Create in 0:212018/3/15
*/
public class Zoo {
//動物園檢查動物
public void check(Animal animal){
System.out.println("this is animal!");
}
public void check(Cat cat){
System.out.println("this is cat!");
}
public void check(EgyptianCat egyptianCat){
System.out.println("this is egyptianCat!");
}
public static void main(String[] args) {
Zoo zoo = new Zoo();
Animal animal = new Animal();
Cat cat = new Cat();
EgyptianCat egyptianCat = new EgyptianCat();
zoo.check(animal);
zoo.check(cat);
zoo.check(egyptianCat);
}
}
結果:
this is animal!
this is cat!
this is egyptianCat!
以上的程式碼是check方法的過載,看上去沒有任何問題.但只要我們稍加修改,用到上面的向上轉型,結果則會出人意料
public static void main(String[] args) {
Zoo zoo = new Zoo();
Animal animal = new Animal();
Cat cat = new Cat();
Animal egyptianCat = new EgyptianCat();
zoo.check(animal);
zoo.check(cat);
zoo.check(egyptianCat);
}
}
輸出結果:
this is animal!
this is cat!
this is animal!
這是因為方法過載是靜態的,傳入引數型別是看型別的引用,前面cat的引用型別是Animal,所有編譯器自動把cat傳到Animal的過載方法中。
現在我們稍加修改
public class Zoo {
//動物園檢查動物
public void check(Animal animal){
animal.printInfo();
}
public static void main(String[] args) {
Zoo zoo = new Zoo();
Animal animal = new Animal();
Cat cat = new Cat();
Animal egyptianCat = new EgyptianCat();
zoo.check(animal);
zoo.check(cat);
zoo.check(egyptianCat);
}
}
結果為:
this is Animal
this is cat
this is EgyptianCat
這裡說明一下,Animal 、Cat、EgyptianCat類中都有一個printInfo方法打印出自身的資訊
從結果可以看出,雖然是向上轉型,但是仍能準確輸出類的資訊,並且在Zoo類中並不需要過載方法,試想一下每當有一個新的類出現就過載一次,那麼這個Zoo類的過載方法是不是太多了,以後管理起來也不方便,但使用多型的話,這需要在子類中重寫方法即可,十分的方便。
【總結】
使用繼承這個關鍵字extend 能減少我們程式碼編寫的工作量,但在重寫的時候要注意,例如重寫,類的變數繼承等
因為有了繼承,使得程式碼的複用有了千變萬化的機會