繼承(inheritance)
繼承的意義
我們首先來看下面兩個類:
public class Dog {public String name;public int age;public void eat(){System.out.println(this.name+"正在吃飯");}public void bark(){System.out.println(this.name+"正在汪汪叫");}
}
public class Cat {public String name;public int age;public void eat(){System.out.println(this.name+"正在吃飯");}public void miao(){System.out.println(this.name+"正在喵喵叫");}
}
這兩個名為Dog和Cat的類擁有相同的成員變量name、age以及相同的成員方法eat。
在主方法中可以正常調用他們的變量和方法。
public class test {public static void main(String[] args) {Dog dog = new Dog();dog.name="大黃";dog.eat();dog.bark();System.out.println("============");Cat cat = new Cat();cat.name="大橘";cat.eat();cat.miao();}
}
?我們可以發現在Dog和Cat中就具有相同的成員,如果每次新建一個動物類時都要加入這些相同成員,未免有些麻煩,于是繼承出現了。
如何繼承
新建一個Animals類,將動物所具有的共同成員放入其中并刪去原來類中的共同成員。
public class Animals {public String name;public int age;public void eat(){System.out.println(this.name+"正在吃飯");}
}
在原來的類名后加上extends Animals。
格式如下:
修飾符 class 子類名 extends 父類名{
}?
public class Dog extends Animals{public void bark(){System.out.println(this.name+"正在汪汪叫");}
}
public class Cat extends Animals{public void miao(){System.out.println(this.name+"正在喵喵叫");}
}
這樣原來的主函數依然可以正常運行。Animals就稱為父類/基類/超類,Dog和Cat就叫做子類或者派生類。通過繼承,我們實現了代碼的復用。
繼承中的訪問與調用
子類訪問父類成員變量
下面三段代碼分別是子類,父類和主方法。
package demo2;public class Derived extends Base{public int c=3;public void func(){System.out.println(this.a);System.out.println(this.b);System.out.println(this.c);}
}
package demo2;public class Base {public int a=1;public int b=2;
}
package demo2;public class test {public static void main(String[] args) {Derived derived =new Derived();derived.func();}
}
運行結果:
?子類訪問了繼承自父類的a和b,并訪問了自身的成員變量c。
在子類中加入a=100:
package demo2;public class Derived extends Base{public int c=3;public int a=100;public void func(){System.out.println(this.a);System.out.println(this.b);System.out.println(this.c);}
}
運行結果:
可見當父類和子類中同時出現變量a時,this關鍵字訪問的是子類自身的a。
如何在子類中訪問父類中的同名變量a?
使用super關鍵字。
package demo2;public class Derived extends Base{public int c=3;public int a=100;public void func(){System.out.println(super.a);System.out.println(this.b);System.out.println(this.c);}
}
運行結果:
?在子類方法中或者通過子類對象訪問成員時:
1.如果訪問的成員變量子類中有,優先訪問自己的成員變量。
2.如果訪問的成員變量子類中無,則訪問父類繼承下來的,如果父類中也無,則編譯報錯。
3.如果訪問的成員變量與父類中的成員變量同名,則優先訪問自己的。
子類訪問父類成員方法
當子類中成員方法與父類中成員方法不同名時,可以直接在子類中調用父類成員方法:
public class Base {public int a=1;public int b=2;public void testBase(){System.out.println("testBase---");}
}
public class Derived extends Base{public int c=3;public int a=100;public void testDerived(){System.out.println("testDerived---");}public void test(){testBase();testDerived();}
}
public class test {public static void main(String[] args) {Derived derived =new Derived();derived.test();}
}
運行結果:
?當子類中方法與父類中方法重名時,優先就近原則調用子類自己的方法,同時可以構成方法的重載和方法的重寫:
方法的重載:
public class Base {public int a=1;public int b=2;public void testA(){System.out.println("testA()---");}
}public class Derived extends Base{public int c=3;public int a=100;public void testA(char a){System.out.println("testA(char a)---");}public void test(){testA();testA('a');}
}public class test {public static void main(String[] args) {Derived derived =new Derived();derived.test();}
}
?并且可以使用super關鍵字調用父類方法:
public class Base {public int a=1;public int b=2;public void testA(){System.out.println("testA(Base)---");}
}public class Derived extends Base{public int c=3;public int a=100;public void testA(){System.out.println("testA(Derived)---");}public void test(){testA();super.testA();}
}public class test {public static void main(String[] args) {Derived derived =new Derived();derived.test();}
}
運行結果:
但是super只能指代父類,不能指代父類的父類玩套娃。
當子類繼承父類之后,在不使用構造方法的情況下,java會自動為子類和父類提供構造方法,但當我們為類添加構造方法時,子類需要顯式的調用父類的構造方法,幫助父類完成初始化。
public class Animals {public String name;public int age;public void eat(){System.out.println(this.name+"正在吃飯");}public Animals(String name,int age){this.name=name;this.age=age;}
}public class Dog extends Animals {public void bark(){System.out.println(this.name+"正在汪汪叫");}public Dog(String name,int age){super(name,age);}
}public class test {public static void main(String[] args) {Dog dog = new Dog("大黃",3);System.out.println("name:"+dog.name+" age:"+dog.age);}
}
簡而言之,當為父類添加構造方法時,需要同時在子類中添加構造方法并在第一行使用super關鍵字幫助父類初始化。
super和this關鍵字
1.super()和this()構造方法只能在第一行使用,并且不能同時出現。?
2.在非靜態成員方法中,this用來訪問本類中的方法和屬性,super用來訪問繼承父類中的方法和屬性。
3.在構造方法中,this()用于調用本類構造方法,super()用于調用繼承父類的構造方法,兩種調用不能同時在構造方法中出現。
4.構造方法中一定會有super()的調用,即使用戶沒有寫編譯器也會添加,但this()用戶不寫則沒有。
繼承中的代碼塊運行順序
猜測下列標號代碼塊的運行順序:
public class Animals {public String name;public int age;static{System.out.println("static::Animal{}");}//1{System.out.println("實例化代碼塊 Animal");}//2public Animals(String name,int age){this.name=name;this.age=age;System.out.println("Animal()");}//3
}public class Cat extends Animals {static{System.out.println("static::Cat{}");}//4{System.out.println("實例化代碼塊 Cat{}");}//5public void miao(){System.out.println(this.name+"正在喵喵叫");}public Cat(String name,int age){super(name,age);System.out.println("Cat()");}//6
}public class test {public static void main(String[] args) {Cat cat=new Cat("大橘",4);}
}
運行結果:
運行順序是:142356
從中可以得出代碼塊的運行邏輯:先執行父類,子類的靜態代碼塊,再執行父類的實例化和初始化,再執行子類的實例化和初始化。
?繼承的權限
private:僅允許本類中調用
default:允許同包中的不同類使用
protected:允許不同包中的子類使用
public:允許不同包的非子類中使用
在繼承的權限使用中,我們要盡可能的做到封裝:即最大的私密性,隱藏內部細節,只顯露出必要的內容給使用者,能夠使用private,就盡量不要使用public。有一個暴力的方法,就是將所有的變量設置為private權限,將所有的方法設置為public權限。
final關鍵字
繼承關系不宜有太多層,一般三層為上限。final在java中對變量修飾可以將其變為不可改變的常量,對子類修飾則可以防止這個類被繼承,當它被繼承時編譯器會報錯,這個類就被稱為密封類。
繼承與組合
繼承中子類與父類的關系可以表示為子類 is a 父類,而組合可以表示為前者 has a后者,或者后者 is a part of 前者,如汽車和輪胎的關系。
class Student{}class Teacher{}public class school {public Student[] student=new Student[10];public Teacher[] teacher=new Teacher[10];
}
學生和老師是學校的一部分,這就是組合關系。