文章目錄
面向對象的三個基本特徵是:封裝、繼承、多態
繼承
繼承可以使我們更容易實現類的擴展,在現實世界中比比皆是。並且繼承實現了代碼的重用,不用再重新發明輪子,提高了代碼的複用性。
繼承是用extends關鍵字實現的,例如:
class 子類 extends 父類{
Java語句;… … …
}
可以通過一個哺乳動物的例子來理解:
public class ExtendsTest {
public static void main(String[] args) {
Human human=new Human("人類",173);
//擁有自己定義的方法
human.study();
//繼承了來自父類的方法
human.suckle();
human.viviparous();
System.out.println("------------------------");
Dog dog=new Dog("狗",50);
dog.bark();
//dog.study();無法使用Human類中的方法
//繼承了來自父類的方法
dog.suckle();
dog.viviparous();
}
}
//Mammal是父類,Human和Dog是Mammal的子類
class Mammal{
String name;
int height;
//哺乳動物的特性
public void suckle() {
System.out.println("哺乳");
}
public void viviparous() {
System.out.println("胎生");
}
}
class Human extends Mammal{
//天然擁有父類的屬性
public Human(String name,int height){
this.name=name;
this.height=height;
}
//可以重寫父類的方法
@Override
public void suckle() {
System.out.println("人類是胎生的");
}
//可以自己定義方法
public void study(){
System.out.println(this.name+"身高"+this.height+",擁有高等智慧。");
}
}
class Dog extends Mammal{
public Dog(String name,int height){
this.name=name;
this.height=height;
}
public void bark(){
System.out.println(this.name+"身高"+this.height+",會汪汪汪。");
}
}
繼承的使用要點
- 父類也稱作超類、基類、派生類等。
- Java中只有單繼承,沒有像C++那樣的多繼承。多繼承會引起混亂,使得繼承鏈過於複雜,系統難於維護。
- Java中類沒有多繼承,接口有多繼承。
- 子類繼承父類,可以得到父類的全部屬性和方法 (除了父類的構造方法),但不見得可以直接訪問(比如,父類私有的屬性和方法)。
- 如果定義一個類時,沒有調用extends,則它的默認繼承的父類是:java.lang.Object。
常用的關鍵字
this關鍵字
this的本質就是“創建好的對象的地址”,由於在構造方法調用前,對象已經創建。因此,在構造方法中也可以使用this代表“當前對象” 。
this常用的用法:
- 在程序中產生二義性之處,應使用this來指明當前對象;普通方法中,this總是指向調用該方法的對象。構造方法中,this總是指向正要初始化的對象。
- 使用this關鍵字調用重載的構造方法,避免相同的初始化代碼。但只能在構造方法中用,並且必須位於構造方法的第一句。
- 靜態方法不需要定義類的對象就可以直接使用的,而this表示調用這個方法的對象,兩個同時使用就矛盾了,靜態方法被調用的時候是不存在類的對象的。所以,this不能用於static方法中。
static關鍵字
在類中,用static聲明的成員變量爲靜態成員變量,也稱爲類變量。 類變量的生命週期和類相同,在整個應用程序執行期間都有效。它有如下特點:
- 爲該類的公用變量,屬於類,被該類的所有實例共享,在類被載入時被顯式初始化。
- 對於該類的所有對象來說,static成員變量只有一份。被該類的所有對象共享。
- 一般用“類名.類屬性/方法”來調用。(也可以通過對象引用或類名(不需要實例化)訪問靜態成員。)
- 在static方法中不可直接訪問非static的成員。
核心要點(參考上文this最常的用法的第三點):
- static修飾的成員變量和方法,從屬於類。
- 普通變量和方法從屬於對象的。
super關鍵字
-
super是直接父類對象的引用,可以通過super來訪問父類中被子類覆蓋的方法或屬性。
-
使用super調用普通方法,語句沒有位置限制,可以在子類中隨便調用。
-
若是構造方法的第一行代碼沒有顯式的調用super(…)或者this(…);那麼Java默認都會調用super(),含義是調用父類的無參數構造方法,這裏的super()可以省略。
final關鍵字
- 修飾變量:被他修飾的變量不可改變,一旦賦了初值,就不能被重新賦值。
- 修飾方法:該方法不可被子類重寫,但是可以被重載(因爲重載本質上是兩個不同的方法)。
- 修飾類:修飾的類不能被繼承。比如:Math、String等。
封裝及權限修飾符
封裝的作用及其優點
- 封裝的作用
- 封裝是指一種將抽象性函式接口的實現細節部分包裝、隱藏起來的方法。
- 封裝可以被認爲是一個保護屏障,防止該類的代碼和數據被外部類定義的代碼隨機訪問。
- 要訪問該類的代碼和數據,必須通過嚴格的接口控制。
- 封裝最主要的功能在於我們能修改自己的實現代碼,而不用修改那些調用我們代碼的程序片段。
- 適當的封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。
- 編程中封裝的具體優點:
- 提高代碼的安全性。
- 提高代碼的複用性。
- “高內聚”:封裝細節,便於修改內部代碼,提高可維護性。
- “低耦合”:簡化外部調用,便於調用者使用,便於擴展和協作。
權限修飾符
Java中使用權限修飾符來控制哪些細節需要封裝,哪些細節需要暴露的。 Java中4種權限修飾符分別爲private、default、protected、public,它們說明了面向對象的封裝性,所以我們要利用它們儘可能的讓訪問權限降到最低,從而提高安全性。
權限修飾符的訪問範圍:
權限修飾符 | 同一個類 | 同一個包 | 子類 | 所有類 |
---|---|---|---|---|
private | √ | |||
無修飾符(default) | √ | √ | ||
protected | √ | √ | √ | √ |
public | √ | √ | √ | √ |
-
私有權限(private):private可以修飾數據成員,構造方法,方法成員,不能修飾類(此處指外部類,不考慮內部類)。被private修飾的成員,只能在定義它們的類中使用,在其他類中不能調用。
-
默認權限(default):類,數據成員,構造方法,方法成員,都能夠使用默認權限,即不寫任何關鍵字。默認權限即同包權限,同包權限的元素只能在定義它們的類中,以及同包的類中被調用。
-
受保護權限(protected):protected可以修飾數據成員,構造方法,方法成員,不能修飾類(此處指外部類,不考慮內部類)。被protected修飾的成員,能在定義它們的類中,同包的類中被調用。如果有不同包的類想調用它們,那麼這個類必須是定義它們的類的子類。
-
公共權限(public):public可以修飾類,數據成員,構造方法,方法成員。被public修飾的成員,可以在任何一個類中被調用,不管同包或不同包,是權限最大的一個修飾符。
封裝的使用細節
- 類的屬性的處理:
- 一般使用private訪問權限。
- 提供相應的get/set方法來訪問相關屬性,這些方法通常是public修飾的,以提供對屬性的賦值與讀取操作(注意:boolean變量的get方法是is開頭)。
- 一些只用於本類的輔助性方法可以用private修飾,希望其他類調用的方法用public修飾。
- 權限修飾符使用的注意事項
- 並不是每個修飾符都可以修飾類(指外部類),只有public和default可以。
- 所有修飾符都可以修飾數據成員,方法成員,構造方法。
- 爲了代碼安全起見,修飾符不要儘量使用權限大的,而是適用即可。比如,數據成員,如果沒有特殊需要,儘可能用private。
- 修飾符修飾的是“被訪問”的權限。
可以用一個人的例子來理解封裝的使用:
public class EncapsulationTest {
public static void main(String[] args) {
Person p=new Person("小王",21);
System.out.println(p.getName()); //小王
System.out.println(p.getAge()); //21
System.out.println(p); //Person{name=小王, age=21}
p.setAge(-1); //設置的年齡不合法,賦值失敗
System.out.println(p.getAge()); //21
}
}
class Person{
//屬性一般使用private修飾
private String name;
private int age;
private boolean flag;
public Person() {
}
public Person(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) {
//在賦值前判斷年齡是否合法
if (age>130 || age<0){
System.out.println("請輸入正確的年齡");
}else{
this.age = age;
}
}
public boolean isFlag() { //注意:boolean類型的屬性get方法是is開頭的
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
//重寫Object類中的toString方法
@Override
public String toString() {
return "Person{" +"name=" + name +", age=" + age+ '}';
}
}
多態
多態的概念
-
**概念:**同一操作作用於不同的對象,可以有不同的解釋,產生不同的執行結果,這就是多態性。簡單的說:就是用父類的引用指向子類的對象。
-
我們經常說“哺乳動物有很多種叫聲”,如”汪汪”,”喵喵”,”嗷嗚”等,這就是哺乳動物叫聲的多態,多種形態。
-
當一個類有很多子類時,並且這些子類都重寫了父類中的某個方法。那麼當我們把子類創建的對象的引用放到一個父類的對象中時(即上轉型對象),那麼這個上轉型對象調用這個方法時就具有多種形態,因爲子類在重寫父類方法時可能生產不同行爲。
-
多態就是指父類的某個方法被子類重寫時,可以各自產生自己的功能行爲。
-
Java綁定原理
- Java中有兩種綁定機制:靜態綁定和動態綁定
- 編譯期間進行的綁定(靜態綁定或者前期綁定):對象的屬性以及對象的類方法與變量的聲明類型進行綁定叫做靜態綁定或者前期綁定。
- Java在運行時動態決定實例方法的調用叫做後期綁定(late binding):根據具體引用的實體來決定實例方法的調用叫運行期綁定,正是因爲有了動態綁定機制才能實現Java中的多態行爲。
多態的使用要點
- 多態是方法的多態,不是屬性的多態(多態與屬性無關)。
- **多態的存在要有3個必要條件:**繼承,方法重寫,父類引用指向子類對象。
- 父類引用指向子類對象後,用該父類引用調用子類重寫的方法,此時多態就出現了。
對象的轉型
- 向上轉型的特點
- 父類引用指向子類對象,我們稱這個過程爲向上轉型,屬於自動類型轉換。
- 不能操作子類新增的成員變量和方法;
- 可以操作子類繼承的變量,也可操作子類繼承或重寫的實例方法;
- 如果子類重寫了父類的某個方法後,當對象的上轉型對象調用這個方法時一定是調用了這個重寫的方法。
- 上轉型對象向下轉型
-
不要將父類創建的對象和子類對象上轉型對象混淆;
-
對象的向下轉型:可以將對象的上轉型對象再強制轉換到一個子類對象,這時該子類對象又具備子類所有屬性和功能;(即下溯)
Animal animal = new Tiger();//上轉型對象
Tiger tiger = (Tiger)animal;//下溯寫法
- **注意:**不可以將父類創建的對象的引用賦值給子類聲明的對象(不能說”哺乳動物是貓”);
instanceof運算符
instanceof是二元運算符,左邊是對象,右邊是類;當對象是右邊類或子類所創建對象時,返回true;否則,返回false。使用instanceof關鍵字用來判斷當前對象是否是類的一一個實例。
可以以一下動物的例子理解多態以及對象的轉型,例如:
Animal animal = tiger;
if (animal instanceof Cat){ //animal instanceof Cat判斷animal是否是Cat的實例
Cat cat = (Cat) animal;
} else if (animal instanceof Tiger) {
Tiger tiger = (Tiger) animal;
tiger.shout();
} else {
System.out.println("animal不是Tiger的上轉型對象");
}
類型自動提升及強制類型轉換條件
- 創建對象的類之間存在繼承關係。
- 向下轉型應該加一個條件:必須是該子類的上轉型對象才能被成功下溯成子類對象
多態的例子
public class PolymorphismTest {
//有了多態,只需要讓增加的這個類繼承Animal類就可以,這樣就提高了代碼的複用性了。
//如果沒有多態,每增加一種動物,就需要重載一種動物的喊叫方法,非常麻煩。
public static void animalShout(Animal a) {
a.shout();
}
public static void main(String[] args) {
Animal a1 = new Tiger();
//傳的具體是哪一個類就調用哪一個類的方法,大大提高了程序的可擴展性。
animalShout(a1);
Animal a2 = new Cat();
animalShout(a2); //a2爲編譯類型,Dog對象纔是運行時類型。
//a1.Hunting(); 不能操作子類新增的成員變量和方法,需要先將對象向下轉型
//編寫程序時,如果想調用運行時類型的方法,只能進行強制類型轉換,否則通不過編譯器的檢查。
Tiger t=(Tiger)a1; //向下需要強制類型轉換
t.Hunting();
System.out.println("--------下部分爲對象的轉型---------");
Animal animal = new Tiger(); //可以向上自動轉型
if (animal instanceof Cat){ //animal instanceof Cat判斷animal是否是Cat的實例
Cat cat = (Cat) animal;
} else if (animal instanceof Tiger) { //animal instanceof Tiger判斷animal是否是Tiger的實例
Tiger tiger = (Tiger) animal;
tiger.shout();
} else {
System.out.println("animal不是Tiger的上轉型對象");
}
}
}
//Animal爲父類,Tiger和Cat爲Animal的子類
class Animal{
public void shout(){
System.out.println("吼叫");
}
}
class Tiger extends Animal {
@Override
public void shout() {
System.out.println("嗷嗚");
}
public void Hunting(){
System.out.println("捕獵");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}