如何在手機(jī)上做網(wǎng)站搜索引擎調(diào)價(jià)工具哪個(gè)好
?Java繼承:
繼承的概念:
在Java中,繼承(inheritance)是面向?qū)ο缶幊痰囊粋€(gè)重要概念,它允許一個(gè)類(子類)繼承另一個(gè)類(父類)的屬性和方法。通過繼承,子類可以擁有父類的屬性和方法,并且可以擴(kuò)展或修改這些屬性和方法,實(shí)現(xiàn)代碼的重用和擴(kuò)展。
父類怎么形成的?
當(dāng)我們定義了多個(gè)類,發(fā)現(xiàn)每個(gè)類中的屬性重復(fù),我們就可以把這些重復(fù)屬性抽取出來,形成父類,其它的類直接繼承這個(gè)父類即可。
繼承其實(shí)更貼合于設(shè)計(jì)這方面的感覺,因?yàn)槲覀儗懘a的時(shí)候,不可能寫了好多類之后,然后發(fā)現(xiàn)好多類的屬性重復(fù),再去抽取,這樣就很慢,而是在設(shè)計(jì)的時(shí)候就提前想好。
在Java中,使用關(guān)鍵字extends
來實(shí)現(xiàn)繼承,語法如下:?
class 子類名 extends 父類名 {// 子類的屬性和方法
}
繼承的基本使用:
1:首先定義一個(gè)父類(people)
package Testextends;public class People {String name;int age;public void work(){System.out.println("工作");}private void eat(){System.out.println("吃飯");}
}
2:定義若干子類(teacher,manager)
package Testextends;public class Manager extends People{
}
package Testextends;public class Teacher extends People{}
同時(shí)繼承父類people,不過在子類中不寫任何代碼
3:在main方法中創(chuàng)建對(duì)象
package Testextends;public class Test01 {public static void main(String[] args) {Teacher teacher = new Teacher();}
}
如圖,通過java的反射機(jī)制,我們就可以看出teacher繼承了people的方法,
不過我們仔細(xì)看,私有方法eat并沒有顯示出來。
說明子類使用不了父類的私有方法和變量,只能使用非私有方法和變量
繼承中成員變量的使用:
?1:繼承中成員變量不重名:
首先我們需要明白一個(gè)規(guī)則,我創(chuàng)建了一個(gè)對(duì)象,如果我想調(diào)用這個(gè)對(duì)象里面的屬性,我先是在這個(gè)對(duì)象的范圍里面查找,找不到,再去查父類
明白了這個(gè)規(guī)則
我們看下面的代碼:
在子類中創(chuàng)建一個(gè)變量zi = 10
package Testextends;public class Teacher extends People{int zi = 10;
}
在父類中創(chuàng)建一個(gè)變量fu = 100?
package Testextends;public class People {String name;int age;private int gender;int fu = 100;public void work(){System.out.println("工作");}private void eat(){System.out.println("吃飯");}
}
在主函數(shù)中創(chuàng)建父類對(duì)象?
?仔細(xì)看上面的代碼:
父類調(diào)用子類的特有變量(zi)報(bào)錯(cuò),這也印證了上面的規(guī)則。
2:繼承中成員變量重名:
重名的話,很好理解,就是直接覆蓋
?繼承中成員方法的使用:
其實(shí)和上面的差不多,如果不重名,則這個(gè)方法就算子類的特有方法
如果重名,那這個(gè)就算子類重寫了父類的方法,直接覆蓋掉了
注意點(diǎn):
如果這個(gè)時(shí)候來一行代碼:
使用父類來創(chuàng)建一個(gè)子類的對(duì)象
People people1 = new Teacher();
這個(gè)時(shí)候的成員變量和成員方法的結(jié)果就需要特別注意了:
package Testextends;public class Teacher extends People{int age = 18;public void work(){System.out.println("教師工作");}
}
package Testextends;public class People {String name;int age = 100;private int gender;public void work(){System.out.println("工作");}private void eat(){System.out.println("吃飯");}
}
People people1 = new Teacher();System.out.println(people1.age);people.work();teacher.work();people1.work();
輸出結(jié)果:
100
工作
教師工作
教師工作
我們可以發(fā)現(xiàn),對(duì)于成員變量來說,age的值還是父類的值,對(duì)于成員方法來說,work方法的內(nèi)容卻變成了子類重寫過的內(nèi)容
所以我們可以總結(jié):
?繼承中:
成員變量訪問特點(diǎn):看等號(hào)左邊是誰,先調(diào)用誰中的成員變量
如上面People people1 = new Teacher();等號(hào)左邊是父類(people),那成員變量的值就是左邊
成員方法訪問特點(diǎn):看new的是誰,先調(diào)用誰中的方法
還是上面這個(gè)例子,調(diào)用的方法就是Teacher重寫過的方法。
方法重寫:?
方法重寫(Method Overriding)是指子類重寫(覆蓋)其父類中具有相同名稱、相同參數(shù)列表和返回類型的方法。
檢測(cè)是否為重寫方法:在該方法上寫
? @Override
方法重寫和方法重載的區(qū)別:
方法重載(Method Overloading)指的是在同一個(gè)類中可以有多個(gè)同名的方法,但這些方法的參數(shù)列表必須不同(包括參數(shù)的類型、順序或個(gè)數(shù))。在調(diào)用這些同名方法時(shí),編譯器會(huì)根據(jù)傳入的參數(shù)類型來確定調(diào)用哪個(gè)重載的方法。
這是方法的重載
public class OverloadExample {public void printInfo(int num) {System.out.println("Number: " + num);}public void printInfo(String str) {System.out.println("String: " + str);}public void printInfo(int num1, int num2) {System.out.println("Numbers: " + num1 + " and " + num2);}public static void main(String[] args) {OverloadExample example = new OverloadExample();example.printInfo(10); // 調(diào)用第一個(gè)printInfo方法example.printInfo("Hello"); // 調(diào)用第二個(gè)printInfo方法example.printInfo(5, 8); // 調(diào)用第三個(gè)printInfo方法}
}
這是方法的重寫:
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Animal animal = new Animal();animal.sound(); // 輸出:Animal makes a soundDog dog = new Dog();dog.sound(); // 輸出:Dog barks}
}
@Override判斷此方法是否是重寫方法,如果不是就會(huì)報(bào)錯(cuò)
重寫的注意事項(xiàng):
?1:子類重寫父類方法,權(quán)限必須要保證大于等于父類權(quán)限,這里的權(quán)限指的是訪問權(quán)限
(子類中重寫的方法不能使用比父類中被重寫的方法更嚴(yán)格的訪問修飾符)
權(quán)限排序:public > protected > 默認(rèn)(什么都不加)> private?
2:父類私有方法,構(gòu)造方法,靜態(tài)方法
私有方法不能繼承,也不能重寫
構(gòu)造方法不可以繼承,不能重寫
靜態(tài)方法可以繼承,不能重寫
3:子類重寫父類方法之后,返回值得是父類方法返回值得子類類型
重寫的使用場(chǎng)景:
-
實(shí)現(xiàn)多態(tài)性:方法重寫是實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性的一種重要手段。通過子類重寫父類方法,可以在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來調(diào)用相應(yīng)的方法,實(shí)現(xiàn)動(dòng)態(tài)綁定。
-
修改父類方法的行為:有時(shí)候子類可能需要修改父類方法的行為或者提供更具體的實(shí)現(xiàn)。通過重寫父類方法,子類可以根據(jù)自己的需求來實(shí)現(xiàn)特定的邏輯。
-
擴(kuò)展父類方法的功能:在某些情況下,子類可能需要在父類方法的基礎(chǔ)上添加額外的邏輯或功能。通過重寫父類方法并在其中調(diào)用父類的原方法,可以實(shí)現(xiàn)邏輯的擴(kuò)展。
-
更好地適應(yīng)子類的需求:通過重寫父類方法,子類可以更好地適應(yīng)自身的特性和需求,從而提高代碼的靈活性和可維護(hù)性。
(from GPT)
super和this:
先來看一個(gè)案例:
我在這里只初始化了一個(gè)子類對(duì)象,只調(diào)用了子類的構(gòu)造方法。
可是連通父類的構(gòu)造方法一起調(diào)用了。
所以,我們可以得出:
1:new子類對(duì)象時(shí),會(huì)先初始化父類(父類的無參構(gòu)造方法)
2:原因:
? ? ? ? 每個(gè)構(gòu)造方法的第一行,默認(rèn)會(huì)有一個(gè)super(),jvm自動(dòng)提供
? ? ? ? super代表父類的無參構(gòu)造
?了解了這個(gè)案例之后:
super的使用:
1:概述:代表的是父類的引用
2:使用:
????????a.調(diào)用父類的構(gòu)造方法:在子類的構(gòu)造方法中使用super
關(guān)鍵字可以調(diào)用父類的構(gòu)造方法。這樣可以確保在子類對(duì)象被實(shí)例化時(shí),父類的構(gòu)造方法也會(huì)被執(zhí)行。如果不顯式使用super
關(guān)鍵字調(diào)用父類構(gòu)造方法,Java編譯器會(huì)默認(rèn)插入調(diào)用父類的無參構(gòu)造方法。
????????super() -> 調(diào)用父類無參構(gòu)造
????????super(實(shí)參)->調(diào)用父類有參構(gòu)造
? ? ? ? ?b.調(diào)用父類的成員變量和方法:在子類中使用super
關(guān)鍵字可以訪問調(diào)用父類的成員變量和方法
? ? ? ? super.成員變量名
? ? ? ? super.成員方法名(實(shí)參)
public class Fu {int num = 10;public Fu(){System.out.println("我是父類中的無參構(gòu)造");}public Fu(int data){System.out.println("我是父類中的有參構(gòu)造");}public void method(){System.out.println("我是父類中的method方法");}
}``````java
public class Zi extends Fu{int num = 100;public Zi(){super();//調(diào)用父類中的無參構(gòu)造System.out.println("我是子類中的無參構(gòu)造");}public Zi(int num){super(10);//調(diào)用父類的有參構(gòu)造System.out.println("我是子類中的有參構(gòu)造");}public void method(){super.method();//調(diào)用父類的method方法System.out.println("我是子類中的method方法");System.out.println(num);//子類自己的System.out.println(super.num);//調(diào)用父類的num}
}``````java
public class Test01 {public static void main(String[] args) {Zi zi = new Zi();System.out.println("============");Zi zi1 = new Zi(10);System.out.println("============");Zi zi2 = new Zi();zi2.method();}
}
this的使用:
1.this概述:代表的是當(dāng)前對(duì)象(哪個(gè)對(duì)象調(diào)用的this所在的方法,this就代表哪個(gè)對(duì)象)
當(dāng)前對(duì)象:指的是指定上下文中的實(shí)例化對(duì)象
public class MyClass {private int num;public void setNum(int num) {this.num = num; // 在這里,this表示當(dāng)前對(duì)象的引用,指正在調(diào)用setNum方法的對(duì)象}
}MyClass obj = new MyClass();
obj.setNum(10);
// 當(dāng)調(diào)用setNum方法時(shí),this表示obj這個(gè)實(shí)例化的對(duì)象
這里面的obj就是當(dāng)前對(duì)象
2.作用:
? a.區(qū)分重名的成員變量和局部變量
? b.調(diào)用當(dāng)前對(duì)象中的成員
3.使用:
? a.調(diào)用當(dāng)前對(duì)象的構(gòu)造:在構(gòu)造中寫
? ? this():調(diào)用當(dāng)前對(duì)象的無參構(gòu)造
? ? this(實(shí)參):調(diào)用當(dāng)前對(duì)象的有參構(gòu)造
public class MyClass {private int num;public MyClass() {this(0); // 在這里,this表示當(dāng)前對(duì)象的引用,指構(gòu)造當(dāng)前對(duì)象的實(shí)例}public MyClass(int num) {this.num = num;}
}MyClass obj = new MyClass();
// 當(dāng)創(chuàng)建MyClass對(duì)象時(shí),this表示的是正在實(shí)例化的MyClass對(duì)象
當(dāng)在主函數(shù)中實(shí)例化一個(gè)對(duì)象后,就算你調(diào)用的是一個(gè)無參構(gòu)造方法,?在無參構(gòu)造中調(diào)用了this(0)
還是會(huì)用那個(gè)有參的構(gòu)造方法實(shí)例化。
? b.調(diào)用當(dāng)前對(duì)象的成員變量:
? ? this.成員變量名
public class MyClass {int m;public void setNum(int num){this.num = num;System.out.println(this.num);}private int num;public void method(){int num = 100;System.out.println(num);System.out.println(this.num);}
}
?當(dāng)全局變量有一個(gè)和方法中的變量重名的時(shí)候(這段代碼中的num),我們就可以用this來區(qū)分全局變量和局部變量
? c.將當(dāng)前對(duì)象作為參數(shù)傳遞給其他方法:
public class MyClass {private int num;public void method() {newMethod(this); // 在這里,this表示當(dāng)前對(duì)象的引用,指調(diào)用method方法的對(duì)象}public void newMethod(MyClass obj) {// 在這里,obj參數(shù)接收到的是this所指向的對(duì)象}
}MyClass obj = new MyClass();
obj.method();
// 當(dāng)調(diào)用method方法時(shí),this表示的是obj這個(gè)實(shí)例化的對(duì)象
4:注意點(diǎn):
不管是super還是this,只要在構(gòu)造中使用,都必須在第一行,所以二者不能同時(shí)手寫出來
繼承的特點(diǎn):
1.繼承只支持單繼承,不能多繼承
? public class A extends B,C{} ?-> 錯(cuò)誤
2.繼承支持多層繼承
? public class A extends B{}
? public class B extends C{}
3.一個(gè)父類可以有多個(gè)子類
? public class A extends C{}
? public class B extends C{}
?繼承(為父類私有屬性賦值)
首先我們知道,父類的私有屬性,子類是沒有辦法訪問的,那這個(gè)時(shí)候如果我們還是要去修改值怎么辦呢?
兩種方法:
1:利用set賦值
其實(shí)這個(gè)就是去構(gòu)造一個(gè)javabean對(duì)象,然后通過get和set方法對(duì)這個(gè)變量進(jìn)行賦值
public class Employee {private String name;private int age;public Employee() {}public Employee(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void work(){System.out.println("工作");}
}
Teacher teacher = new Teacher();teacher.setName("張三");teacher.setAge(18);System.out.println(teacher.getName()+"..."+teacher.getAge());
2:利用構(gòu)造方法賦值
在子類的構(gòu)造方法中利用super(參數(shù))來構(gòu)造
其實(shí)利用構(gòu)造方法賦值本質(zhì)上也是利用了set方法。
public class Manager extends Employee{public Manager() {}public Manager(String name, int age) {super(name, age);}
}
Manager manager = new Manager("金蓮", 24);System.out.println(manager.getName()+"..."+manager.getAge());
繼承中的抽象類:?
首先我們?cè)O(shè)想一個(gè)場(chǎng)景:三角形類,正方形類,圓形類,他們中都有一個(gè)方法是求面積
根據(jù)我們之前學(xué)習(xí)的繼承,我們很容易想到將這個(gè)求面積的方法抽象出來成一個(gè)形狀類
不過我們?cè)倏?#xff0c;三個(gè)形狀求面積的公式不同,我們不能想之前那樣抽象成一個(gè)方法。
所以就需要我們的抽象類。
抽象類的定義:
Java中的抽象類是指不能被實(shí)例化的類,通常用于定義一些共同的方法和屬性,讓子類來實(shí)現(xiàn)具體的邏輯。抽象類通常包含抽象方法(沒有具體實(shí)現(xiàn)的方法)和非抽象方法(有具體實(shí)現(xiàn)的方法)。
?抽象類案例:
public abstract class Animal {protected String name;public Animal(String name) {this.name = name;}// 抽象方法public abstract void sound();// 非抽象方法public void sleep() {System.out.println(name + " is sleeping");}
}
在上面的例子中,Animal類是一個(gè)抽象類,包含一個(gè)抽象方法sound()和一個(gè)非抽象方法sleep()。任何繼承Animal類的子類都必須實(shí)現(xiàn)sound()方法,否則會(huì)被標(biāo)記為抽象類。
要實(shí)現(xiàn)一個(gè)繼承自抽象類的子類,需要在子類中實(shí)現(xiàn)所有的抽象方法,如下所示:
public class Dog extends Animal {public Dog(String name) {super(name);}@Overridepublic void sound() {System.out.println(name + " is barking");}
}
在這個(gè)例子中,Dog類繼承自Animal類,并且實(shí)現(xiàn)了抽象方法sound()。這樣,Dog類就可以被實(shí)例化并調(diào)用其方法了。
特點(diǎn):
- 繼承了抽象類,就必須重寫所有的抽象方法,由此我們也可以得出,抽象方法不能用private,final 或者static關(guān)鍵字來修飾,這是因?yàn)槎x抽象方法的目的就是想將方法的具體實(shí)現(xiàn)延遲到子類,最終是要被子類重寫的,而private,final,static這幾個(gè)關(guān)鍵字都和“方法重寫”的目的背道而馳。
- 擁有抽象方法的類一定是抽象類;但是抽象類不一定有抽象方法。這個(gè)如何理解,如果一個(gè)類你一開始定義為普通類,后面想用抽象方法,就會(huì)報(bào)錯(cuò),必須加上abstract,但是一開始,如果你定義了一個(gè)抽象類,你可以不寫抽象方法,后面再補(bǔ)也許
-
抽象類不能被實(shí)例化,只能創(chuàng)建其非抽象子類對(duì)象 。既然抽象類不能實(shí)例化,那抽象類的構(gòu)造方法用處是什么呢?我們之前講過繼承可以為父類私有屬性賦值,同時(shí)我們也可以利用這一點(diǎn)
package Testextends;public class Manager extends Person{public Manager(String name, int age) {super(name, age);}
}package Testextends;public abstract class Person {String name;int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}
}package Testextends;public class Test01 {public static void main(String[] args) {Manager manager = new Manager("張三",18);System.out.println(manager.getName()+manager.getAge());}
}
在這個(gè)案例中,Manager繼承了抽象類Person,并且super(name, age);
就和上面那張圖一樣,將抽象類中的name和age屬性賦上了值。
Java接口:
接口的定義:
在Java中,接口(Interface)是一種抽象數(shù)據(jù)類型,它定義了一組方法的簽名,但并沒有提供方法的具體實(shí)現(xiàn)。接口可以看作是一種約定或契約,讓類來實(shí)現(xiàn)這些方法以確保特定的行為或功能。
來舉一個(gè)生活中例子把:
當(dāng)我們訂購(gòu)?fù)赓u時(shí),我們可以將食品平臺(tái)比作接口,而餐廳則是實(shí)現(xiàn)了這個(gè)接口的具體類。在這個(gè)例子中,食品平臺(tái)定義了一組規(guī)范,比如訂單方法、支付方法等,餐廳則按照這些規(guī)范實(shí)現(xiàn)了具體的行為。
接口代表食品平臺(tái)提供的服務(wù),而餐廳作為實(shí)現(xiàn)類,提供了具體的食品和服務(wù)。我們可以通過食品平臺(tái)下單、支付等方法與不同的餐廳交互,而不需要關(guān)心具體餐廳是如何處理訂單和支付的。
這樣的設(shè)計(jì)使得我們可以更加靈活地選擇不同的餐廳進(jìn)行訂餐,并且不需要知曉每個(gè)餐廳的具體細(xì)節(jié),只需按照規(guī)范進(jìn)行操作就可以了。
接口的理解(特點(diǎn)):
- 接口可以理解為一個(gè)抽象類,實(shí)現(xiàn)這個(gè)接口的類必須重寫這個(gè)接口里面的所有方法,但是接口和抽象類不同的是:接口可以實(shí)現(xiàn)多實(shí)現(xiàn),這就和之前的繼承不同了,也算彌補(bǔ)了java不能多繼承的缺點(diǎn),子類可以繼承一個(gè)父類的同時(shí)實(shí)現(xiàn)一個(gè)或者多個(gè)接口
- 接口里面的方法默認(rèn)都是abstract的,在JDK8中添加了default和static,在JDK9中添加了private,如果在接口中不去特意定義default,private和static這三個(gè)關(guān)鍵字的話,那是不是接口中的方法默認(rèn)都是abstract
- 接口中的變量默認(rèn)都是靜態(tài)常量,即使用?
public static final
?修飾的常量。 - 同時(shí)接口也不能被實(shí)例化,都只能通過子類來new
接口的使用:
關(guān)鍵字:
? ?a.interface 接口
? ? ? public interface 接口名{}
? ?
? b.implements 實(shí)現(xiàn)
? ? ? 實(shí)現(xiàn)類 implements 接口名{}
第一個(gè)案例:最簡(jiǎn)單的一個(gè)接口的使用
public interface USB {public abstract void open();public abstract void close();
}public class Mouse implements USB{@Overridepublic void open() {System.out.println("鼠標(biāo)打開");}@Overridepublic void close() {System.out.println("鼠標(biāo)關(guān)閉");}
}public class Test01 {public static void main(String[] args) {Mouse mouse = new Mouse();mouse.open();mouse.close();}
}
第二個(gè)案例:定義了默認(rèn)方法:
public interface Greetable {// 默認(rèn)方法default void greet() {System.out.println("Hello, nice to meet you!");}
}public class Person implements Greetable {public static void main(String[] args) {Person p = new Person();p.greet(); // 調(diào)用接口中的默認(rèn)方法}
}
對(duì)于默認(rèn)方法,需要通過接口的實(shí)例來調(diào)用
第三個(gè)案例:定義了靜態(tài)方法:
public interface Calculator {// 靜態(tài)方法static int add(int a, int b) {return a + b;}
}public class TestCalculator {public static void main(String[] args) {int sum = Calculator.add(10, 20); // 調(diào)用接口中的靜態(tài)方法System.out.println("Sum: " + sum);}
}
靜態(tài)方法直接用接口名就可以調(diào)用
第四個(gè)案例:定義了私有方法
public interface Calculation {default int add(int a, int b) {return performAddition(a, b); // 調(diào)用私有方法}private int performAddition(int a, int b) {return a + b;}
}public class TestCalculation implements Calculation {public static void main(String[] args) {TestCalculation calc = new TestCalculation();int sum = calc.add(10, 20); // 調(diào)用接口中的默認(rèn)方法System.out.println("Sum: " + sum);}
}
在這個(gè)例子中,接口?Calculation
?中定義了一個(gè)默認(rèn)方法?add
,并在該默認(rèn)方法中調(diào)用了一個(gè)私有方法?performAddition
。私有方法被用來實(shí)現(xiàn)具體的邏輯,但只能在接口內(nèi)部使用,外部無法訪問。
Java多態(tài):
多態(tài)是指同一個(gè)方法調(diào)用,在不同對(duì)象上有不同的表現(xiàn)形式。
這樣講其實(shí)蠻難理解:
舉一個(gè)例子:
class Animal {void makeSound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void makeSound() {System.out.println("Dog barks");}void lookDoor(){System.out.println("Dog lookDoor");}
}class Cat extends Animal {void makeSound() {System.out.println("Cat meows");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog(); // 編譯時(shí)類型為Animal,運(yùn)行時(shí)類型為Doganimal.makeSound(); // 運(yùn)行時(shí)調(diào)用Dog類的makeSound方法Animal animal = new Cat(); // 運(yùn)行時(shí)類型為Catanimal.makeSound(); // 運(yùn)行時(shí)調(diào)用Cat類的makeSound方法}
}
首先創(chuàng)建一個(gè)動(dòng)物類Animal類,狗dog類,貓cat類
dog和cat同時(shí)繼承Animal,并且重寫了父類的makeSound,父類引用指向子類對(duì)象
這是我們就會(huì)發(fā)現(xiàn)控制臺(tái)輸出了:
Dog barks
Cat meows
這就是多態(tài),相同的引用到不同的對(duì)象上,有不同的表現(xiàn)形式。
注意點(diǎn):由父類引用指向子類對(duì)象創(chuàng)建出來的對(duì)象不能調(diào)用自己的特有方法
比如用Animal animal = new Dog();創(chuàng)建了一個(gè)對(duì)象animal,這個(gè)animal就不能調(diào)用Dog類中特有的方法lookDoor
?多態(tài)的前提:
- ? 必須有子父類繼承或者接口實(shí)現(xiàn)關(guān)系
- ? 必須有方法的重寫(沒有重寫,多態(tài)沒有意義),多態(tài)主要玩兒的是重寫方法
- ? new對(duì)象:父類引用指向子類對(duì)象,Fu fu = new Zi() -> 理解為大類型接收了一個(gè)小類型的數(shù)據(jù) ->比如 ?double b = 10
- ? 多態(tài)下不能直接調(diào)用子類特有功能(這一點(diǎn)和繼承一樣)
多態(tài)的條件下成員的訪問特點(diǎn)
?看new的是誰,先調(diào)用誰中的成員方法,子類沒有,找父類
這一句話和繼承的時(shí)候?qū)W的是一樣的
比如:
成員變量的使用:
public class Fu {int num = 1000;
}
public class Zi extends Fu{int num = 100;
}
public class Test01 {public static void main(String[] args) {Fu fu = new Zi();System.out.println(fu.num);}
}
new的是子類,所以輸出的就是子類的成員變量
成員方法的使用:
public class Fu {int num = 1000;public void method(){System.out.println("我是父類中的method方法");}
}public class Zi extends Fu{int num = 100;public void method(){System.out.println("我是子類中的method方法");}
}public class Test01 {public static void main(String[] args) {Fu fu = new Zi();System.out.println(fu.num);//父類中的numfu.method();//子類中重寫的method方法}
}
這里也是同理。
多態(tài)的優(yōu)點(diǎn):
要講多態(tài)的優(yōu)點(diǎn),我們先來舉個(gè)例子吧,這個(gè)例子的目的其實(shí)也不是說能說明多態(tài)的優(yōu)點(diǎn),就是做一個(gè)對(duì)比,已多態(tài)的方式創(chuàng)建對(duì)象和普通的方式創(chuàng)建對(duì)象
問題描述:
? 如果使用原始方式new對(duì)象(等號(hào)左右兩邊一樣),既能調(diào)用重寫的,還能調(diào)用繼承的,還能調(diào)用自己特有的成員
? 但是多態(tài)方式new對(duì)象,只能調(diào)用重寫的,不能直接調(diào)用子類特有的成員,那為啥還要用多態(tài)呢?
既然我們學(xué)了,那肯定就有學(xué)的用處:
先來看一下優(yōu)缺點(diǎn)吧:
多態(tài)方式和原始方式new對(duì)象的優(yōu)缺點(diǎn):
? 原始方式:
? ? a.優(yōu)點(diǎn):既能調(diào)用重寫的,還能調(diào)用父類非私有的,還能調(diào)用自己特有的
? ? b.缺點(diǎn):擴(kuò)展性差
? ? ? ??
? 多態(tài)方式:
? ? a.優(yōu)點(diǎn):擴(kuò)展性強(qiáng)
? ? b.缺點(diǎn):不能直接調(diào)用子類特有功能? ? ? ?
這樣講肯定太寬泛了:
具體的代碼案例:
package Testduotai;class Animal {void makeSound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void makeSound() {System.out.println("Dog barks");}
}class Cat extends Animal {void makeSound() {System.out.println("Cat meows");}
}
public class Test01 {public static void main(String[] args) {//不用多態(tài),用普通的方法Dog dog = new Dog();method01(dog);Cat cat = new Cat();method02(cat);}public static void method01(Dog dog){dog.makeSound();}public static void method02(Cat cat){cat.makeSound();}
}
這段代碼是用普通的創(chuàng)建對(duì)象的方式:
這段代碼中:method01 和method02這兩個(gè)方法的參數(shù)分別是dog和cat,
因?yàn)镈og和Cat兩個(gè)對(duì)象之間沒有什么關(guān)系,所以,我們需要建兩個(gè)方法,方法的參數(shù)分別是dog和cat
但是如果我們用了多態(tài)的創(chuàng)建對(duì)象的方式:
public static void main(String[] args) {//不用多態(tài),用普通的方法Dog dog = new Dog();method01(dog);Cat cat = new Cat();method02(cat);//用多態(tài)的方式Animal animal = new Dog();method(animal);Animal animal1 = new Cat();method(animal1);}public static void method(Animal animal){animal.makeSound();}
我們用一個(gè)方法就可以實(shí)現(xiàn)上面的功能,并且如果動(dòng)物的種類多了,用這種方式可以更好的實(shí)現(xiàn)代碼的復(fù)用性
總結(jié)起來就是:
形參傳遞父類類型,調(diào)用此方法父類類型可以接收任意它的子類對(duì)象
傳遞哪個(gè)子類對(duì)象,就指向哪個(gè)子類對(duì)象,就調(diào)用哪個(gè)子類對(duì)象重寫的方法
?
多態(tài)中的轉(zhuǎn)型
一:向上轉(zhuǎn)型:
????????1.父類引用指向子類對(duì)象
? ????????好比是: double b = 1;
二:向下轉(zhuǎn)型:
????????1.向下轉(zhuǎn)型:好比強(qiáng)轉(zhuǎn),將大類型強(qiáng)制轉(zhuǎn)成小類型
????????2.表現(xiàn)方式:
? ????????父類類型 對(duì)象名1 = new 子類對(duì)象() -> 向上轉(zhuǎn)型 -> double b = 1
? ????????子類類型 對(duì)象名2 = (子類類型)對(duì)象名1 -> 向下轉(zhuǎn)型 -> int i = (int)b
????????3.想要調(diào)用子類特有功能,我們就需要向下轉(zhuǎn)型?
具體講一下第三點(diǎn)調(diào)用子類特有功能:
看代碼:
public abstract class Animal {public abstract void eat();
}
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("貓吃魚");}//特有方法public void catchMouse(){System.out.println("貓會(huì)捉老鼠");}
}
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗啃骨頭");}//特有方法public void lookDoor(){System.out.println("狗會(huì)看門");}
}
public class Test01 {public static void main(String[] args) {//多態(tài)new對(duì)象 向上轉(zhuǎn)型Animal animal = new Dog();animal.eat();//dog重寫的//animal.lookDoor();//多態(tài)不能調(diào)用子類特有功能//向下轉(zhuǎn)型Dog dog = (Dog) animal;dog.eat();dog.lookDoor();}
}
這段代碼中,貓和狗都有一個(gè)自己特定的方法捉老鼠和看門,
父類對(duì)象animal如果想要調(diào)用這兩個(gè)方法,就需要向下轉(zhuǎn)型
轉(zhuǎn)型時(shí)會(huì)遇到的問題:
如果等號(hào)左右兩邊類型不一致,會(huì)出現(xiàn)類型轉(zhuǎn)換異常(ClassCastException)
在我們進(jìn)行類型轉(zhuǎn)換的時(shí)候難免會(huì)發(fā)生這個(gè)異常
比如我們?nèi)フ{(diào)用一個(gè)別人寫好的方法,我們不知道類型,我們直接進(jìn)行強(qiáng)轉(zhuǎn),就很容易錯(cuò)
解決辦法:
? 在向下轉(zhuǎn)型之前,先判斷類型 關(guān)鍵字:instanceof
? 判斷結(jié)果是boolean型
? 對(duì)象名 instanceof 類型 -> 判斷的是關(guān)鍵字前面的對(duì)象是否符合關(guān)鍵字后面的類型
具體看代碼:
public static void method(Animal animal){if(animal instanceof Dog){Dog dog = (Dog) animal;dog.lookDoor();}if(animal instanceof Cat){Cat cat = (Cat) animal;cat.catchMouse();}}
判斷傳進(jìn)來的類型再進(jìn)行輸出。