做網(wǎng)站需要多少錢西安優(yōu)化大師官網(wǎng)下載
一.繼承
1.1我們?yōu)槭裁葱枰^承?
首先,Java中使用類對現(xiàn)實世界中實體來進行描述,類經(jīng)過實例化之后的產(chǎn)物對象,則可以用來表示現(xiàn)實中的實體,但是 現(xiàn)實世界錯綜復(fù)雜,事物之間可能會存在一些關(guān)聯(lián),那在設(shè)計程序是就需要考慮。
例如貓和狗:
public class Dog {String name;int age;float weight;public void eat(){System.out.println(name+"正在吃飯!");}public void sleep(){System.out.println(name+"正在睡覺!");}public void bark(){System.out.println(name+"正在汪汪汪!");}
}public class Cat {String name;int age;float weight;public void eat(){System.out.println(name+"正在吃飯!");}public void sleep(){System.out.println(name+"正在睡覺!");}public void bark(){System.out.println(name+"正在喵喵喵!");}}
我們可以看到上面的代碼我們可以知道,貓和狗他們有一些共性代碼例如eat和sleep,這時我們就可以想到它們都是動物我們是否有什么方法可以將使用一個類方法它們放在一起。確實我們這就就可以用到我們的繼承方法,因為它們都是動物這時我們就可以定義一個Animal作為父類用于存放相同共性的代碼,之后我們Dog和Cat就只需要繼承Animal這時就達到了簡便,從而實現(xiàn)代碼的復(fù)用。
1.2繼承的概念
繼承(inheritance)機制:是面向?qū)ο蟪绦蛟O(shè)計使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進行擴展,增加新功能,這樣產(chǎn)生新的類,稱派生類。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計的層次結(jié)構(gòu), 體現(xiàn)了 由簡單到復(fù)雜的認知過程。繼承主要解決的問題是:共性的抽取,實現(xiàn)代碼復(fù)用。
繼承關(guān)系如圖所示:
?這里我們可以看出繼承其實就是父類和子類的關(guān)系,當(dāng)我們父類中有的代碼時子類可以繼承,子類只需要關(guān)心自已特有的特性。
繼承的意義:實現(xiàn)代碼的復(fù)用。
?1.3繼承的語法
在Java中類之間的繼承需要我們用到extends關(guān)鍵字。
例如:上面的貓和狗我們就可以用繼承關(guān)系進行eat和sleep方法的復(fù)用。
public class Animal {String name;int age;public void eat(){System.out.println(name+"正在吃飯!");}public void sleep() {System.out.println(name + "正在睡覺!");}
}public class Cat extends Animal{public void bark(){System.out.println(name+"正在汪汪汪!");}}
public class Dog extends Animal {public void bark(){System.out.println(name+"正在喵喵喵!!");}
}
public class Test {public static void main(String[] args) {Dog dog=new Dog();dog.name="旺財";dog.age=10;dog.sleep();dog.eat();dog.bark();}
}
例如上面這一段代碼,我們可以看到在測試方法里面我們可以直接通過dog.的形式來調(diào)用name,age,sleep,eat方法,但是我們的dog類方法中并沒有這這些方法,這是它就是繼承了父類Animal中的方法。
注意事項:?
1. 子類會將父類中的成員變量或者成員方法繼承到子類中了
2. 子類繼承父類之后,必須要新添加自己特有的成員,體現(xiàn)出與基類的不同,否則就沒有必要繼承了
1.4父類成員的訪問
1.4.1子類訪問父類的成員變量
1.當(dāng)子類和父類不存在同名成員變量時
public class Base {int a;int b;}
public class Derived extends Base{int c;public void method(){a=200;b=59;c=50;}}
?其中a,b是從父類繼承過來的,子類自己的。
2.當(dāng)父類和子類存在同名變量時
public class Base {int a;int b;int c;}
public class Derived extends Base {int a;char b;public void method(){int a=100;//此處的a訪問子類的a還是繼承父類的a?int b=200;//此處的b訪問子類的b還是繼承父類的b?int c=300;//此處的c子類中沒有所以肯定是繼承父類的c}
}
通過以上兩個我們可以總結(jié)出:
在子類方法中或者通過子類對象訪問成員時:
1.如果子類和父類同時擁有相同的成員變量時優(yōu)先訪問子類自己的。
2.如果訪問成員變量子類沒有,則繼承父類的成員變量,如果父類也沒有,則編譯報錯。
總結(jié)規(guī)律:采用就近原則,子類自己由就訪問自己的,沒有才去繼承父類的。
1.4.2子類中訪問父類的成員方法
1.成員方法名不同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 訪問子類自己的methodB()
methodA(); // 訪問父類繼承的methodA()
// methodD(); // 編譯失敗,在整個繼承體系中沒有發(fā)現(xiàn)方法methodD()
}
}
總結(jié):子類中有的成員方法就訪問自己的,如果沒有在去訪問父類的方法名,若兩者都沒有則編譯報錯。
2.成員方法名字相同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
public void methodA(int a) {
System.out.println("Derived中的method(int)方法");
}
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodA(); // 沒有傳參,訪問父類中的methodA()
methodA(20); // 傳遞int參數(shù),訪問子類中的methodA(int)
methodB(); // 直接訪問,則永遠訪問到的都是子類中的methodB(),基類的無法訪問到
}
}
總結(jié):
1.通過子類對象訪問父類與子類中不同名方法時,優(yōu)先在子類中找,找到則訪問,否則在父類中找,找到 則訪問,否則編譯報錯。
2.通過派生類對象訪問父類與子類同名方法時,如果父類和子類同名方法的參數(shù)列表不同(重載),根據(jù)調(diào)用 方法適傳遞的參數(shù)選擇合適的方法訪問,如果沒有則報錯;
這時我們可能會想如果子類中存在與父類中相同的成員時,那如何在子類中訪問父類相同名稱的成員呢?答案很明顯我們就會引用super關(guān)鍵字。
1.5super關(guān)鍵字
我們平時在設(shè)計場景的時候我們通常會遇到父類和子類的成員變量名相同,那么我們?nèi)绾蝸碓L問父類的相同變量名呢?這時我們就會用到我們的super關(guān)鍵字,super的作用:在子類方法中訪問父類成員。
例如:
public class Base {int a;int b;public void methodA(){System.out.println("Base中的methodA().......");}public void methodB(){System.out.println("Base中的methodB().......");}
}
public class Derived extends Base{int a;//與父類的成員變量名相同且類型相同char b;//與父類的成員變量名相同但類型不同//與父類中的methodA構(gòu)成了重載public void methodA(int a){System.out.println("Derived中的methodA().....");}//與父類中的methodB構(gòu)成了重寫public void methodB(){System.out.println("Derived中的methodB.......");}public void methodC(){a=100;//等價于 this.a=a;b=200;//等價于 this.b=b;//之前我們講過this是對當(dāng)前類中成員變量的直接引用//這里如果我們要訪問父類的a和b,需要借助super關(guān)鍵字//super是指子類從父類繼承下來的部分super.a=300;super.b=400;//父類和子類中構(gòu)成重載的方法,直接可以通過參數(shù)列表區(qū)分清訪問父類還是子類方法methodA();methodA(10);// 如果在子類中要訪問重寫的基類方法,則需要借助super關(guān)鍵字methodB();super.methodB();//調(diào)用父類的methodB}
}
總結(jié):在子類中調(diào)用父類的成員變量和方法名只需要用上super變量就可以了。
上面我們提到了this和super關(guān)鍵字我們來區(qū)分以下它們。具體如下:
?1.6初始化
說起子類構(gòu)造方法我們其實可以在這個板塊里面把父類和子類的靜態(tài),實例,構(gòu)造這三個的執(zhí)行順序全部總結(jié)出來。我們來看以下一段代碼:
public class Animal {static{System.out.println("static::Animal().......");}private final String name;private final int age;{System.out.println("實例代碼塊Animal().....");}public Animal(String name,int age){this.name=name;this.age=age;System.out.println("Animal().......");}}
public class Dog extends Animal{static{System.out.println("static::Dog()....");}{System.out.println("實例代碼塊Dog().......");}public Dog(String name, int age) {super(name, age);System.out.println("Dog().......");}
}
以上是父類和子類的靜態(tài),實例,構(gòu)造代碼,那么我們接下來就可以通過運行結(jié)果來獲得它們的執(zhí)行順序是怎樣的?
通過上圖我們可以看出代碼執(zhí)行順序:
?從而我們可以得到以下結(jié)論:
注意:第二次實例化子類對象時子類和父類的靜態(tài)方法將不再執(zhí)行
?1.7protect關(guān)鍵字
我們在前面學(xué)習(xí)了類和對象中我們可以知道在實現(xiàn)封裝的時候,Java中引入了限定修飾符,主要限定:類或者類中成員能否在類外或者其他包中被訪問。
protect的定義如下:
那么在不同包中的子類使用具體是如何的呢?我們用下面的具體實例來說明
?
上面這一段代碼就很好的說明了protect在不同包中子類的使用。?
1.8繼承方式?
Java中繼承的方式多種多樣下面我們來舉出幾個具體的例子:
在Java中我們一般采用前三種繼承方式,多繼承一般不會被使用。我們又是想要限制繼承,這時我們就要用到關(guān)鍵字final。
1.9final關(guān)鍵字
1.修飾變量
被final修飾的變量不能被修改。
2.修飾類
此類將無法被繼承。?
3.修飾方法
此方法不能被重寫。
1.10繼承和組合
和繼承相似組合也是一種表達類之間的關(guān)系,也可以起到代碼復(fù)用的效果,給我們帶來簡便,但在組合中并沒用想繼承中的extend之類的關(guān)鍵詞,僅僅時將一個類的實例作為另一個類的字段。
繼承表示對象是is-a的關(guān)系,例如:?狗是動物,貓是動物
組合表示對象時has-a的關(guān)系,例如:汽車
// 輪胎類
class Tire{
// ...
}
// 發(fā)動機類
class Engine{
// ...
}
// 車載系統(tǒng)類
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以復(fù)用輪胎中的屬性和方法
private Engine engine; // 可以復(fù)用發(fā)動機中的屬性和方法
private VehicleSystem vs; // 可以復(fù)用車載系統(tǒng)中的屬性和方法
// ...
}
// 奔馳是汽車
class Benz extend Car{
// 將汽車中包含的:輪胎、發(fā)送機、車載系統(tǒng)全部繼承下來
}
組合和繼承都可以實現(xiàn)代碼復(fù)用,應(yīng)該使用繼承還是組合,需要根據(jù)應(yīng)用場景來選擇,一般建議:能用組合盡量用 組合。
二.多態(tài)
2.1多態(tài)的概念
多態(tài)的概念簡單的來說就是當(dāng)不同的對象去完成相同的事的時候會產(chǎn)生不同的狀態(tài)。
例如:
從上面兩個例子我們可以看出:同一件事情發(fā)生在不同的對象身上就會產(chǎn)生不同的結(jié)果。?
2.2多態(tài)的實現(xiàn)條件
首先在Java中多態(tài)實現(xiàn)的必要條件:
1. 必須在繼承體系下
2. 子類必須要對父類中方法進行重寫
3. 通過父類的引用調(diào)用重寫的方法
public class Animal {String name;int age;public Animal(String name,int age){this.name=name;this.age=age;}public void eat(){System.out.println(name+"正在吃飯");}}
public class Dog extends Animal{public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {super.eat();System.out.println(name+"吃骨頭");}
}
public class Cat extends Animal{public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {super.eat();System.out.println(name+"正在吃魚");}
}
public class TestAnimal {public void eat(Animal a){a.eat();}public static void main(String[] args) {Dog dog=new Dog("旺財",1);Cat cat=new Cat("元寶",2);dog.eat();cat.eat();}
}
當(dāng)不同的對象進行相同的行為會產(chǎn)生?不同的結(jié)果這就是多態(tài)。
2.3重寫
重寫:就是覆蓋,重寫是子類對父類非靜態(tài)、非private修飾,非final修飾,非構(gòu)造方法等的實現(xiàn)過程 進行重新編寫,返回值和形參都不能改變。即外殼不變,核心重寫。
注意: 1.被重寫的方法返回值類型可以不同,但是必須是具有父子關(guān)系的。
? ? ? ? ? ? 2.訪問權(quán)限不能比父類中被重寫的方法的訪問權(quán)限更低。例如:如果父類方法被public修飾,則子類中重寫該方 法就不能聲明為 protected
? ? ? ? ? ? 3.父類被static、private修飾的方法、構(gòu)造方法都不能被重寫。
? ? ? ? ? ? 4.重寫的方法, 可以使用 @Override 注解來顯式指定. 有了這個注解能幫我們進行一些合法性校驗. 例如不小心 將方法名字拼寫錯了 (比如寫成 aet), 那么此時編譯器就會發(fā)現(xiàn)父類中沒有 aet 方法, 就會編譯報錯, 提示無法 構(gòu)成重寫.
重寫和重載的區(qū)別:
?2.4靜態(tài)綁定和動態(tài)綁定
靜態(tài)綁定:也稱為前期綁定(早綁定),即在編譯時,根據(jù)用戶所傳遞實參類型就確定了具體調(diào)用那個方法。典型代 表函數(shù)重載。
動態(tài)綁定:也稱為后期綁定(晚綁定),即在編譯時,不能確定方法的行為,需要等到程序運行時,才能夠確定具體 調(diào)用那個類的方法。
動態(tài)綁定具體如下:
2.5向上轉(zhuǎn)型和向下轉(zhuǎn)型
2.5.1向上轉(zhuǎn)型:
向上轉(zhuǎn)型顧名思義在我們的繼承中就是由子類向父類進行向上轉(zhuǎn)型。也就是創(chuàng)造一個子類對象把它當(dāng)作父類對象來使用。
語法格式:
使用場景具體的有三種:
1.直接賦值
2.方法傳參
3.方法返回
向上轉(zhuǎn)型的優(yōu)點:讓代碼實現(xiàn)更簡單靈活。
向上轉(zhuǎn)型的缺陷:不能調(diào)用到子類特有的方法。
?2.5.2向下轉(zhuǎn)型
將一個子類對象經(jīng)過向上轉(zhuǎn)型之后當(dāng)成父類方法使用,再無法調(diào)用子類的方法,但有時候可能需要調(diào)用子類特有的 方法,此時:將父類引用再還原為子類對象即可,即向下轉(zhuǎn)換。
但我們需要注意的是向下轉(zhuǎn)型存在安全隱患
public static void main(String[] args) {Cat cat=new Cat("元寶",10);Dog dog=new Dog("旺財",20);//向上轉(zhuǎn)型Animal animal=cat;animal.eat();animal=dog;animal.eat();//向下轉(zhuǎn)型cat=(Cat)animal;//此時animal指向的是dog但這里向下轉(zhuǎn)型為cat運行時會拋出異常cat.mew();dog=(Dog)animal;dog.bark();//這里animal指向的就是dog故這里不會報錯}
向下轉(zhuǎn)型用的比較少,而且不安全,萬一轉(zhuǎn)換失敗,運行時就會拋異常。Java中為了提高向下轉(zhuǎn)型的安全性,引入 了 instanceof ,如果該表達式為true,則可以安全轉(zhuǎn)換。
public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("元寶",2);
Dog dog = new Dog("小七", 1);
// 向上轉(zhuǎn)型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal;
dog.bark();
}
}
}
2.6多態(tài)的優(yōu)缺點
使用多態(tài)能夠大大降低代碼的“圈復(fù)雜度”,避免大量使用if-else.
圈復(fù)雜度:圈復(fù)雜度是一種描述一段代碼復(fù)雜程度的方式. 一段代碼如果平鋪直敘, 那么就比較簡單容易理解. 而如 果有很多的條件分支或者循環(huán)語句, 就認為理解起來更復(fù)雜.
例如:
public class Shape {public void draw(){System.out.println("畫圖形!");}
}
public class Cycle extends Shape{@Overridepublic void draw() {super.draw();System.out.println("●");}
}
public class Rect extends Shape{@Overridepublic void draw() {super.draw();System.out.println("?");}
}
public class Flower extends Shape{@Overridepublic void draw() {super.draw();System.out.println("?");}
}
public class TestShape {public static void main(String[] args) {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};for (String shape : shapes) {if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();} else if (shape.equals("flower")) {flower.draw();}}}
}
這里我們沒有使用多態(tài)我們就會使用大量的if-else循環(huán)語句這時代碼就比較繁瑣,那當(dāng)我們使用多態(tài)會是什么效果呢?
public class Shape {public void draw(){System.out.println("畫圖形!");}
}
public class Cycle extends Shape{@Overridepublic void draw() {super.draw();System.out.println("●");}
}
public class Rect extends Shape{@Overridepublic void draw() {super.draw();System.out.println("?");}
}
public class Flower extends Shape{@Overridepublic void draw() {super.draw();System.out.println("?");}
}
public class TestShape {public static void main(String[] args) {Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}}}
這里我們不難看出當(dāng)我們使用了多態(tài)以后,代碼就會變得簡單易懂,這就是多態(tài)的好處。
2.使用多態(tài)可擴展性強
簡而言之就是當(dāng)我們要增加一種新的形狀的時候,改動代碼的成本比較低,例如:
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("△");
我們只需要在上面代碼的基礎(chǔ)上新增加一個類就可以了,不需要去改動其他地方。
但是多態(tài)除了它的優(yōu)點也有缺點
1. 屬性沒有多態(tài)性 當(dāng)父類和子類都有同名屬性的時候,通過父類引用,只能引用父類自己的成員屬性
2. 構(gòu)造方法沒有多態(tài)性
好了以上就是關(guān)于繼承和多態(tài)的全部內(nèi)容,我們下期見!?