目錄
一、封裝(encapsulation)
什麼是封裝?舉個例子,比如一臺電視機,電視機內部有複雜的各種器械,而展現在外部的只有開關和幾個按鍵。封裝可以抽象的理解爲就是那個電視機殼子,它將複雜的東西“包”起來,只保留簡單的對外接口。
專業一點來說,封裝就是將對象的屬性和方法儘可能隱藏內部的複雜性,只保留有限的簡單的對外接口便於外界的調用。封裝的特性使得對象以外的部分不能隨意存取對象的內部數據(屬性),保證了程序和數據不受外部干擾且不被誤用,從而提高系統的可擴展性、可維護性。我們的程序設計要追求“高內聚,低耦合”,高內聚 :就是類的內部數據操作細節自己完成,不允許外部干涉;低耦合 :僅暴露少量的方法給外部使用
那麼我們如何去實現封裝呢?實現封裝,需要使用訪問控制符,訪問控制符一共有四種,public(公共的),protected(受保護的),default/friendly(默認的/友好的),private(私有的)。
對於類的訪問權限只有兩種:
1、public 可被同一項目中所有的類訪問。 (必須與文件名同名)
2、default/friendly 可被同一個包中的類訪問
對於類中成員(成員變量或成員方法)訪問權限共有四種:
1、public 可以被項目中所有的類訪問。(項目可見性)
2、protected 可以被這個類本身訪問;同一個包中的所有其他的類訪問;被它的子類(同一個包以及不同包中的子類)訪問
3、default/friendly 可以被這個類本身訪問;被同一個包中的類訪問。(包可見性) •
4、private 只能被這個類本身訪問。(類可見性)
封裝要點:
類的屬性的處理:
1、一般使用private。 (除非本屬性確定會讓子類繼承)
2、提供相應的get/set方法來訪問相關屬性。這些方法通常是public,從而提供對屬性的讀取操作。
3、boolean變量的get方法是用:is開頭!
4、一些只用於本類的輔助性方法可以用private,
5、希望其他類調用的方法用public
接下來我們用一個程序去具體的理解封裝:
package cn.cou.pack;
public class TestEncapsulation { //此類可被同一項目中所有的類訪問
public static void main(String[] args) {
Human h = new Human();
//h.age = 13; //在此處age用不了
h.age1 = 13;
h.age2 = 14;
h.age3 = 15;
h.sayAge();
}
}
class Human { //此類爲默認權限default,可被同一個包中的類訪問
private int age; //只在此類(Human)中可用
int age1; //可被同一包中的類訪問
protected int age2; //可以被這個類本身訪問;同一個包中的所有其他的類訪問;被它的子類(同一個包以及不同包中的子類)訪問
public int age3; //可以被項目中所有的類訪問
void sayAge() {
System.out.println(age);
}
}
二、繼承
java的繼承通過extends關鍵字來實現,實現繼承的類被稱爲子類,被繼承的類稱爲父類,父類和子類的繼承關係是從一般到特殊的關係,比如說,水果和蘋果的關係,蘋果繼承了水果,蘋果是水果的子類,水果是蘋果的父類。另外,extends的英文意思是“擴展”,至於爲何國內把他翻譯成繼承就不知道了,因此,我們可以理解爲,子類是父類的擴展,子類會獲得父類的全部屬性和方法,但是要注意的是子類不能獲得父類的構造方法,以及父類中被private修飾的變量或方法。
java中只有單繼承,也就是說,一個子類只能繼承自一個父類,每個子類只有一個直接父類,不過,一個父類是可以有多個子類的,這一點可以聯想到樹形圖。java中可以通過接口實現多繼承。不過多繼承是有弊端的,多繼承會引起混亂,使得繼承鏈過於複雜,系統難於維護。就像我們現實中,如果你有多個父母親,那是一個多麼混亂的世界。多繼承,就是爲了實現代碼的複用性,卻引入了複雜性,使得系統類之間的關係混亂。
如果定義一個類時,沒有調用extends,則它的父類默認是:java.lang.Object。
我們沿用並加以擴展上面講述封裝時的程序,進行詳細描述:
package cn.cou.pack;
public class TestExtends { //此類可被同一項目中所有的類訪問
public static void main(String[] args) {
Human1 h = new Human1();
//h.age = 13; //在此處age用不了
h.age1 = 13;
h.age2 = 14;
h.age3 = 15;
h.sayAge();
Man man = new Man();
//man.age = 1; // 此處age依然不可用,private修飾的age只在Human1類中可用
man.age1 = 2;
man.age2 = 3;
man.age3 = 4;
man.sayAge();
man.work();
}
}
class Human1 { //此類前沒有修飾,說明爲默認權限default,可被同一個包中的類訪問
private int age; //只在此類(Human1)中可用
int age1; //(默認權限default)可被同一包中的類訪問
protected int age2; //可以被這個類本身訪問;同一個包中的所有其他的類訪問;被它的子類(同一個包以及不同包中的子類)訪問
public int age3; //可以被項目中所有的類訪問
void sayAge() { //此方法前沒有修飾,說明爲默認權限default,可被同一個包中的類訪問
System.out.println(age);
}
}
class Man extends Human1{ //子類Man繼承父類Human1
void work() {
System.out.println("工作工作工作!!!");
}
}
接下來詳細的說一下,子類與父類在繼承後,變量、方法的關係:
1、子類可以繼承父類的所有特性,但其可見性,由父類成員變量、方法的修飾符決定。
①、對於被private修飾的類成員變量或方法,其子類是不可見的,也即不可訪問;
②、對於定義爲默認訪問(沒有修飾符修飾)的類成員變量或方法,只有與父類同處於一個包中的子類可以訪問;
③、對於定義爲public或protected 的類成員變量或方法,所有子類都可以訪問。
2、子類中可以聲明與父類同名的成員變量,這時父類的成員變量就被隱藏起來了,在子類中直接訪問到的是子類中定義的成員變量。
3、子類中也可以聲明與父類相同的成員方法,包括返回值類型、方法名、形式參數都應保持一致,稱爲方法的重寫(override),重寫方法不能使用比被重寫方法更嚴格的訪問權限(由於多態)。
4、被隱藏的父類成員變量、方法是可以被引用的,通過super來實現對被隱藏或被覆蓋的父類成員的訪問。
①、訪問父類被隱藏的成員變量和成員方法: super.父類成員變量名
②、調用父類被隱藏的方法:super.父類成員方法名([參數列表])
③、調用父類的構造方法:super([參數列表]),需要注意到是:super( )只能在子類的構造函數中出現,並且永遠都是位於子類構造函數中的第一條語句。
下面用程序舉例說明,請詳細分析,好好理解:
package cn.cou.pack;
public class TestExtends { //此類可被同一項目中所有的類訪問
public static void main(String[] args) {
Human h = new Human();
//h.age = 13; //在此處age用不了
h.age1 = 13;
h.age2 = 14;
h.age3 = 15;
h.sayAge();
Man man = new Man();
//man.age = 1; // 此處age依然不可用,private修飾的age只在Human類中可用
man.age1 = 2;
man.age2 = 3;
man.age3 = 4;
man.sayAge();
man.work();
}
}
class Human { //此類前沒有修飾,說明爲默認權限default,可被同一個包中的類訪問
private int age; //只在此類(Human)中可用
int age1; //(默認權限default)可被同一包中的類訪問
protected int age2; //可以被這個類本身訪問;同一個包中的所有其他的類訪問;被它的子類(同一個包以及不同包中的子類)訪問
public int age3; //可以被項目中所有的類訪問
long number = 8000000000L;
void sayAge() { //此方法前沒有修飾,說明爲默認權限default,可被同一個包中的類訪問
System.out.println("age1 = "+age1);
System.out.println("fnumber = "+number);
}
Human() //父類的構造方法(每一個類都至少有一個構造方法,如果此處我們不寫,系統會默認爲我們創建一個無參的構造方法,而且不顯示)
{
}
}
class Man extends Human{ //子類Man繼承父類Human
long number = 40000000000L;//覆蓋了父類的number變量
void sayAge() //隱藏了父類的sayAge方法
{
System.out.println("age1 = "+age1);
System.out.println("znumber = "+number);
}
void work() {
System.out.println("工作工作工作!!!");
System.out.println("super.number = "+super.number);//調用父類中被隱藏的成員變量
super.sayAge();//調用父類的sayAge方法
}
Man()//子類的構造方法
{
super();//調用父類的構造方法
}
}
值得注意一點是:Object類是所有java類的根基類,如果在類的聲明中未使用extends關鍵字指明其父類,則默認父類爲Object類 ,比如上述代碼中TestExtends類,未指明其父類,則其父類就爲Object類。如果你使用的編譯軟件是eclipse,可以選中TestExtends類,點Ctrl+T,就可以查看他的繼承關係。
三、多態(polymorphism)
多態指的是同一個方法調用,由於對象的不同可能會有不同的行爲,比如在我們現實生活中,用同一個方法比如玩,不同的對象,比如男生玩可能就是打遊戲,女生玩可能就是逛街買東西。同樣都是玩,對象不同,行爲可能就會不同。
多態的定義格式:
定義格式:父類類型 變量名 = new 子類類型();
多態的要點:
1、多態是方法的多態,不是屬性的多態,多態與屬性無關。
2、多態的存在要有3個必要條件:要有繼承,要有方法重寫,父類引用變量指向子類對象
3、父類引用變量指向子類對象後,用該父類引用調用子類重寫的方法,此時就出現了多態。
引用變量的兩種類型:
1、編譯時類型(一般是一個父類):由聲明時的類型決定
2、運行時類型(運行時,具體是哪個子類就是哪個子類):由實際對應的對象類型決定。
對象的轉型:
父類引用變量指向子類對象,這個過程稱之爲向上轉型(子轉父),屬於自動類型轉換,向上轉型後的父類引用變量只能調用它編譯時類型的方法,不能調用它運行時類型的方法。如果想要調用它運行時類型的方法,需要進行類型的強制轉換,我們稱之爲向下轉型(父轉子),在向下轉型過程中,必須將引用變量轉成真實的子類類型(運行時類型),否則會出現類型轉換異常。
1、向上轉型,多態本身就是向上轉型過的過程
使用格式:父類類型 變量名 = new 子類類型();
注意: 上轉型對象不能操作子類新增的成員變量和方法,可以操作子類繼承或重寫的成員變量和方法 ,如果子類重寫了父類的某個方法,上轉型對象調用該方法時,是調用的重寫方法。
2、向下轉型,一個已經向上轉型的子類對象可以使用強制類型轉換的格式,將父類引用類型轉爲子類引用各類型
使用格式:子類類型 變量名 =(子類類型) 父類類型的變量;
拓展:instanceof關鍵字,它的作用是用來判斷某個對象是否屬於某種數據類型。返回值類型爲布爾類型。
案例:
package cn.cou.pack;
public class TestPolymorphism { //此類可被同一項目中所有的類訪問
public static void main(String[] args) {
Human2 h = new Human2();
System.out.println("-----------父類:");
h.Hu();
h.stu();
Man1 man = new Man1();
System.out.println("-----------子類:");
man.Hu();
man.study();
Human2 m = new Man1();//自動向上轉型(編譯時系統認爲 m 是Human2的類型,只有在運行時才知道是Man1的類型)
System.out.println("----------子轉父:");
m.Hu(); //調用的是子類重寫的方法
m.stu(); //父類方法
//m.study(); //不能使用子類新增的成員變量和方法
if(m instanceof Man1)
System.out.println("m是Man1的類型");
else
System.out.println("m是Human2的類型");
Man1 n = (Man1) m;//強制向下轉型
System.out.println("----------父轉子:");
n.Hu(); //子類重寫的方法
n.stu(); //父類方法
n.study(); //子類方法
System.out.println(n instanceof Man1);//判斷n是否是Man1的類型
}
}
class Human2 {
void Hu() {
System.out.println("Hu");
}
void stu() {
System.out.println("學習!");
}
}
class Man1 extends Human2{ //子類Man1繼承父類Human2
void Hu() { //重寫Hu方法
System.out.println("Ma");
}
void study() {
System.out.println("好好學習天天向上!");
}
}
結果:
-----------父類:
Hu
學習!
-----------子類:
Ma
好好學習天天向上!
----------子轉父:
Ma
學習!
m是Man1的類型
----------父轉子:
Ma
學習!
好好學習天天向上!
true