繼承思想
-
描述事物(類)的時候,如果有幾個重複的屬性或方法,我們就可以採用繼承的方式來設計
未使用繼承的寫法
class Rabbit{ double weight; int age; //奔跑 public void run() { System.out.println("兔子在奔跑"); } //喫蔬菜 public void eatVegetables() { System.out.println("喫蔬菜"); } } class Tiger{ double weight; int age; //奔跑 public void run() { System.out.println("老虎在奔跑"); } //喫肉 public void eatMeat() { System.out.println("喫肉"); } }
-
繼承語法,使用
extends
關鍵字來繼承class 子類 extends 父類{ 子類屬性... 子類方法... }
-
繼承的特點:
-
子類會把父類所有的能夠繼承的屬性和方法繼承下來
private int type;//private修飾的屬性和行爲不能被繼承
-
子類除了能夠繼承父類的屬性和行爲外,自己也能夠擴展自己的屬性和行爲
-
父類更通用,子類更具體
-
子類只能獲得父類中的非private屬性,如果想要使用就必須提供public的get和set方法,私有方法無法繼承
-
java中Class類只能做單繼承,class上面只能出現一個extends關鍵字
-
java支持多級繼承,即美洲虎繼承老虎,老虎繼承動物
-
Java中所有的類,都是繼承自Object類,Object類是所有類的父類,這種繼承是隱式,JDK已經幫我們完成了這步繼承操作
-
super關鍵字
在JAVA中,super關鍵字表示對當前類的父類的引用,super關鍵字有2種用途,一是訪問被子類成員隱藏的父類成員,二是用來調用父類的構造方法,總之super是用來訪問父類對象的。
-
調用父類成員
如果子類和父類有同名的成員變量或方法,則父類的成員將會被覆蓋,此時我們需要在子類中採用如下方式訪問父類的成員變量和成員方法:
super.<成員變量名> // 如super.age super.<成員方法名> // 如super.run();
注意:在Java語言中,在繼承體系中,成員的訪問遵循如下規則
- 在子類中訪問成員變量和方法時將優先查找是否在本類中已經定義,如果該成員在本類中存在,則使用本類的,否則,按照繼承層次的順序往父類查找,如果未找到,繼續逐層向上到其祖先類查找。
- super特指訪問父類的成員,使用super首先到直接父類查找匹配成員,如果未找到,再逐層向上到祖先類查找。
-
調用父類構造方法
子類可以通過super關鍵字調用父類中定義的構造方法,格式如下:
super();//不帶參數調用 super(....);//帶多個參數調用
注意:子類所調用的構造方法中的參數列表必須和父類的某個構造方法的參數列表完全匹配,才能實現調用
package com.hliedu.supers; public class Demo2 { public static void main(String[] args) { Dog dog = new Dog(); dog.introduce(); } } /* * - super關鍵字,用於引用父類對象(屬性,方法) * - 繼承體系中:屬性、方法查找規則 * 子類在訪問某個屬性的時候,首先會在本類中查找,如果本類中存在那麼就直接使用本類中的屬性或方法,否則會去父類中查找 * 如果父類中未找到該屬性,那麼會一級一級去祖先類查找,如果祖先類都沒有,那麼會直接報錯,所有類的根級父類爲Object * - super調用構造方法 * 構造方法:如果沒有給當前類聲明構造方法,那麼系統會默認給一個無參的構造方法,這個是隱式操作,如果自己定義了帶參或不帶參的構造方法,那麼系統將不會自動給出默認的構造方法 * */ class All{ public void equals(String s) { } } class Animal extends All{ public Animal(int age) { } public Animal() { } int age = 10; public void eat() { System.out.println("喫東西"); } } class Dog extends Animal{ //子類中若存在與父類相同的屬性和方法,那麼父類中的這個屬性和方法就會被隱藏,這個隱藏的屬性或方法就必須採用super關鍵字訪問 public void introduce() { System.out.println("大家好,我今年" + age + "歲"); super.eat(); //一級一級往父類查找,如果都沒有,最終會進入到Object類 equals("123"); } public void eat() { System.out.println("狗喫屎"); } } class Tiger extends Animal { int age = 6; public Tiger() { //this(...)可以調用本類構造方法 //super(...)可以調用父類構造方法 this("" , 1); } public Tiger(String name) { super(1); } public Tiger(String name , int age) { } }
-
子類與其直接父類之間的構造方法存在約束關係,有以下幾條重要原則(重要)
- 按繼承關係,構造方法是從頂向下進行調用的。
- 如果子類沒有構造方法,則它默認調用父類無參的構造方法,如果父類中沒有無參數的構造方法,則將產生錯誤。
- 如果子類有構造方法,那麼創建子類的對象時,先執行父類的構造方法,再執行子類的構造方法。
- 如果子類有構造方法,但子類的構造方法中沒有super關鍵字,則系統默認執行該構造方法時會產生super()代碼,即該構造方法會調用父類無參數的構造方法。
- 對於父類中包含有參數的構造方法,子類可以通過在自己的構造方法中使用super(…);來調用,而且super(…);必須放在子類構造方法中的第一行。
- Java語言中規定當一個類中含有一個或多個有參構造方法,系統不提供默認的構造方法(即不含參數的構造方法),所以當父類中定義了多個有參數構造方法時,應考慮寫一個無參數的構造方法,以防子類省略super關鍵字時出現錯誤。
繼承結構下對象實例化順序
順序如下:
- 父類靜態塊 or 父類靜態成員變量
- 子類靜態塊 or 子類靜態成員變量
- 父類普通塊 or 父類的普通成員字段
- 父類構造方法
- 子類普通塊 or 子類的普通成員字段
- 子類構造方法
代碼測試:
package com.hliedu.supers;
/**
*
* 繼承結構下對象實例化順序
*
* 帶你輕鬆學Java:恆驪學堂
* www.hliedu.com
* QQ:3020685261
*
*/
public class Demo4 {
public static void main(String[] args) {
JavaseBook javaseBook = new JavaseBook();
}
}
/*
* 執行順序
* - 父類中的靜態塊 || 父類中的靜態屬性
* - 子類中的靜態塊 || 子類中的靜態屬性
* - 父類中的語句塊 || 父類中的對象成員屬性
* - 調用父類的構造方法
* - 子類中的語句塊 || 子類中的對象成員屬性
* - 調用子類的構造方法
*/
class Book{
public static int age = 10;
static {
System.out.println("Book中靜態塊" + age);
}
public String name = "Book";
public Book(){
System.out.println("Book中構造方法");
}
{
System.out.println("Book中語句塊" + name);
}
}
class JavaseBook extends Book{
public static int age = 10;
static {
System.out.println("JavaseBook中靜態塊" + age);
}
public String name = "JavaseBook";
{
System.out.println("JavaseBook中語句塊" + name);
}
JavaseBook(){
System.out.println("JavaseBook中構造方法");
}
}
方法重載(overload)
在一個類中存在2個或2個以上的具有相同方法名,但是參數列表不同的方法,稱作爲方法重載。
重載要求:
- 方法名相同
- 形參列表不同(參數個數或類型)
- 與返回值無關
- 與訪問修飾符無關
-
普通方法重載
package com.hliedu.test; public class TestOverLoad { //方法1 void max(int a, int b) { System.out.println(a > b ? a : b); } //方法2 void max(float a, float b) { System.out.println(a > b ? a : b); } //方法3 int max(int a, int b) { return a > b ? a : b; } }
上述代碼中:方法1與方法2構造重載,方法3不構成重載
-
構造方法重載
與普通方法一樣,構造方法也可以重載
public class Person { public Person() { System.out.println("無參構造方法"); } public Person(int i) { System.out.println("有參構造方法"); } }
-
目的
提高代碼可讀性,減少方法詞的命名
方法重寫(override)
在子類繼承父類中,子類的方法和父類的方法相同(訪問修飾符,返回值類型,方法名,參數列表),方法體不同,這種子類的方法將父類的方法覆蓋叫做重寫,重寫僅發生在具有繼承關係的類中。
目的:父類的功能無法滿足子類的需求,子類自定義擴展父類的功能。
方法重寫約束規則:
- 方法重寫時,子類方法名與形參列表必須與父類保持一致。
- 方法重寫時,子類的權限修飾符必須要大於或者等於父類的權限修飾符。
- 方法重寫時,子類的返回值類型必須要小於或者 等於父類的返回值類型。
- 方法重寫時, 子類拋出的異常類型要小於或者等於父類拋出的異常類型。
/*
* 父類代碼
*/
public class Parent {
//要被重寫的方法
public void work(){
//省略代碼塊
}
//要被重寫的方法
public void run(String p){
//省略代碼塊
}
public void sleep(String p){
//省略代碼塊
}
//此方法無法被重寫,因爲在子類中訪問不到
private void eat(){
//省略代碼塊
}
}
/*
* 子類代碼
*/
public class Child extends Parent {
//重寫了父類方法
public void work(){
//省略代碼塊
}
//重寫了父類方法
public void run(String people){
//省略代碼塊
}
//此處無法構成方法重寫,因爲與父類中的sleep方法的參數列表不同
public void sleep(){
}
//這裏會提示編譯錯誤,因爲與父類的方法返回類型不同
public String sleep(String p){
return "nihao";
}
}
重寫與重載的區別
-
重寫(override)
必須是父子類關係,且父子類有相同的方法,唯一不同的就是方法體,一般是父類的該方法滿足不了子類的需求所以才發生重寫
-
重載(overload)
在同一個類中,有着相同的方法名,但是參數類型或參數個數不相同,這兩個方法就構成重載,重載的目的,節省類中方法的命名資源,提高代碼可讀性
final關鍵字
final是指最終的不可更改的東西,final在Java中的應用有3個
-
修飾變量
被final修飾的變量必須要初始化,初始化賦值後不能再重新賦值,即變量不能發生改變。
注意:這裏的不可改變是指基本類型的值不可改變,引用類型變量的引用不可改變,即不能再指向其他對象。
基本類型:
public class Test01{ final int x1= 10000; final int x2; final int x3; { x2 = 20000; } public Test01(){ this.x3 = 3000; } }
引用類型:
final Operate operate = new Operate() ;// operate有一個普通變量i初始化爲10 operate.i = 11; operate.i = 12; System.out.println(operate.i); //輸出12 //引用類型包含:類、接口、數組,所以它們的引用都不能被改變,但引用中對應的值可發生變化
-
修飾方法,表示方法不能被覆寫
-
修飾類,表示類無法被繼承
final修飾的類,類中的所有成員方法都被隱式地指定爲final方法
final修飾類的示例:Math,String
package與訪問權限
package(包)
就是文件夾,主要用於分類管理,可以區分同名不同包的類
訪問修飾符
4種訪問修飾符可以修飾任意的:屬性,方法。
外部類的修飾權限一般定義爲public或default(內部類存在特殊情況),方法大多是public(設計模式,或本類封裝方法可能會採用private和protected修飾),屬性絕大多數都爲private
訪問級別 | 訪問控制修飾符 | 同類 | 同包 | 子類 | 不同的包 |
---|---|---|---|---|---|
公開 | public | √ | √ | √ | √ |
受保護 | protected | √ | √ | √ | – |
默認 | 沒有訪問控制修飾符 | √ | √ | – | – |
私有 | private | √ | – | – | – |
繼承內存結構分析
Java不支持多重繼承,也就是說子類最多隻能繼承一個父類
子類繼承了其父類中不是私有的成員變量和成員方法,作爲自己的成員變量和方法
子類中定義的成員變量和父類中定義的成員變量相同時,則父類中的成員變量不能被繼承
子類中定義的成員方法,並且這個成員方法的名字,返回類型,及參數個數和類型與父類的某個成員方法完全相同,則父類的成員方法不能被繼承