黑馬程式設計師——Java面向物件(二)之封裝、繼承、多型、介面等
-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------
五、面向物件的特徵
面向物件主要有三大特徵:
1.特徵一 —— 封裝
1)定義:是指隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。
2)好處:
a)將變化隔離。
b)便於使用。
c)提高重用性。
d)提高安全性。
3)封裝原則:
a)將不需要對外提供的內容都隱藏起來。
b)把屬性都隱藏,提供公共方法對其訪問。4)封裝表現形式(private):
什麼是private?
private(私有)是一種許可權修飾符,用於修飾類中的成員(成員變數,成員函式、建構函式)。
注:私有隻在本類中有效,僅僅只是封裝的一種表現形式。
封裝的作用:將成員變數私有化,對外提供對應的set,get方法對其進行訪問,提高對資料訪問的安全性。如果對建構函式進行私有,則不能對該類建立物件。
5)程式碼例項:
在該例項中,將Person類中age和name通過私有進行隱藏,而提供setAge()、setName()和getAge()、getName()的公共訪問方式分別進行設定和獲取,因此很好體現了封裝的特性。執行結果如下圖://建立一個Person類,用來描述人的事物 class Person { //將age私有化 private int age; //將name私有化 private String name; //設定年齡 public void setAge(int age) { if(age>0&&age<120) { this.age=age; } else System.out.println("非法年齡"); } //設定姓名 public void setName(String name) { this.name=name; } //獲取年齡 public int getAge() { return age; } //獲取姓名 public String getName() { return name; } } class PersonDemo { public static void main(String[] args) { //建立person物件 Person p=new Person(); p.setAge(40);//設定年齡 p.setName("張三");//設定姓名 System.out.println("age="+p.getAge());//獲取年齡並列印 System.out.println("name="+p.getName());//獲取姓名並列印 } }
2.特徵二 —— 繼承
1)定義:當多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只需通過一種方式去獲取這些共性內容即可,而這種方式就稱為繼承。
2)作用:
a)提高了程式碼的複用性。
b)讓類與類之間產生了關係,有了這個關係,才有了多型。 注:千萬不要為了獲取其他類中的功能,簡化程式碼而繼承。必須是類與類之間有所屬關係才可以繼承。
3)特點:
a)java中類與類之間只支援單繼承,不支援多繼承,即只能繼承一個類,但有一種情況例外,就是介面可以實現多繼承。
b)當子類和父類存在一模一樣的函式(包括返回值型別)時,子類的函式會覆蓋父類的相同函式。
c)子類訪問本類中的引用,用this,子類訪問父類中的引用,用super。如果存在多級繼承,那麼super會先訪問直接父類,沒有再找上一級,即逐級訪問。
d)子類要成功覆蓋父類,那麼子類的許可權必須大於或等於父類的許可權,靜態只能覆蓋靜態。
e)子類繼承父類,只延襲父類非私有內容。4)子父類建構函式:
a)子類建構函式預設第一行會存在super()語句,用來訪問父類建構函式,如果父類的建構函式為自定義建構函式,且非預設形式,則子類需指定訪問父類的建構函式。
b)父類建構函式優先子類建構函式初始化。
c)子類所有的建構函式,預設都會訪問父類中空引數的建構函式,因為每一個建構函式內的第一行都有一句隱式super(),子類的建構函式第一行也可以手動指定this語句來訪問本類中的建構函式,但子類至少有一個建構函式會訪問父類的建構函式。5)問題思考:
a)為什麼類與類之間只支援單繼承?
因為如果一個類可以同時繼承多個類,多個類中可能出現相同函式,但功能不同,那麼在子類中就可能存在多個相同函式,但一個類中是不允許存在多個相同函式,只能通過覆蓋,但java無法確定多個父類中的同名函式的覆蓋順序,所以是不允許多繼承。
b)為什麼this和super不能同時在建構函式當中?
因為this和super語句必須位於建構函式的第一行,所以不可能同時存在建構函式當中。
c)為什麼this和super語句必須位於第一行?
因為this和super語句是用來進行初始化,初始化動作要先執行。6)程式碼例項:
//定義一個Person類
class Person
{
//定義人的屬性,姓名和年齡
String name;
int age;
//初始化物件的屬性
Person(String name,int age)
{
this.name=name;
this.age=age;
System.out.println(name+"----"+age);
}
//人都具有吃飯的功能
public void eat()
{
System.out.println("吃飯");
}
}
//定義一個Student類,並繼承Person類
class Student extends Person
{
Student(String name,int age)
{
super(name,age);//由於父類已經有name和age的屬性,因此只需呼叫父類建構函式進行初始化即可
}
//學生具有特有的聽課功能
public void listen()
{
System.out.println("聽課");
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
//建立一個學生物件,並初始化
Student stu= new Student("lisi",20);
//呼叫繼承父類的吃飯功能
stu.eat();
//呼叫學生的特有的聽課功能
stu.listen();
}
}
執行結果如下圖:
在該例項中,由於學生屬於人中的一員,因此可通過繼承人來獲取共有的方法和屬性,提高了程式碼的複用性,但學生自身也有其特有的功能,因此將這些特有的功能定義在學生類中。
3.抽象類
1)什麼是抽象類?
抽象類是指不能夠直接建立例項物件的類,用關鍵字abstract進行修飾。通常抽象類都會存在無法定義功能主體的方法。
2)特點:
a)抽象方法指只能定義在抽象類中,只有功能定義,沒有功能主體(即沒有大括號及內容),同一個類中抽象方法可以被非抽象方法呼叫。
b)抽象只能修飾方法和類,且抽象方法和抽象類都必須被abstract關鍵字修飾。
c)抽象類不可以用new建立物件。
d)抽象類中的方法要被使用,必須由子類複寫其所有抽象方法後,才能建立子類物件呼叫,如只覆蓋了部分方法,那麼子類還是一個抽象類。
3)問題思考
抽象類與一般類有什麼區別?
抽象類既可以定義抽象方法,也可以定義非抽象方法,而一般類只能定義非抽象方法,但抽象類不能被例項化,而一般類可以被例項化。
4)程式碼示例:
//定義一個抽象類Animal
abstract class Animal
{
//定義抽象方法eat()
public abstract void eat();
}
//定義一個Cat類,並繼承Animal
class Cat extends Animal
{
//複寫父類的eat()方法
public void eat()
{
System.out.println("吃魚");
}
}
//定義一個Dog類,並繼承Animal
class Dog extends Animal
{
//複寫父類的eat()方法
public void eat()
{
System.out.println("吃骨頭");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
//建立一個Cat物件,並呼叫物件的eat()方法
Cat c=new Cat();
c.eat();
//建立一個Dog物件,並呼叫物件的eat()方法
Dog d=new Dog();
d.eat();
}
}
執行結果如下圖:
在上面的例項中可以看到,Animal就是一個抽象類,因為Cat類和Dog類都有各自的吃的方法,因此對吃的方法進行向上抽取,就得到了一個吃的抽象方法,而吃的抽象方法就必須存在抽象類中,子類要建立例項物件就必須複寫Animal中的eat()方法,因此抽象類還具有強制繼承的子類去做一些事情的功能。
4.介面
1)概念:當抽象類中的方法都是抽象的,那麼把這樣的類的形式就描述為介面。介面的定義用關鍵字interface,子類實現父類用關鍵字implements。
2)格式:
interface 介面名
{
定義常量:public static final 返回值型別 常量名稱
定義方法:public abstract 返回值型別 方法名稱(形參1,形參2,....) { }
}
注:上面常量和方法的格式也可部分或全部省略,java會預設自動補上,但不可省略返回值型別和名稱。
3)特點:
a)介面不可以建立物件,如果被子類實現,需要覆蓋介面的全部抽象方法後,子類才可以例項化,否則子類是一個抽象類。
b)介面中只能存在成員常量和抽象的成員方法,兩者可以同時存在或只存在一個,或都不存在。
c)類對介面可以多實現,介面對介面可以多繼承。
d)介面是對外暴露的規則。
e)介面是程式的功能擴充套件。
4)程式碼示例:
//定義一個介面Animal
interface Animal
{
//定義抽象方法eat()
public abstract void eat();
}
//定義一個Cat類,並實現Animal
class Cat implements Animal
{
//複寫父類的eat()方法
public void eat()
{
System.out.println("吃魚");
}
}
class InterfaceDemo
{
public static void main(String[] args)
{
//建立一個Cat物件,並呼叫物件的eat()方法
Cat c=new Cat();
c.eat();
}
}
程式執行的結果如下圖:
5.特徵三 —— 多型
1)定義:某類事物具有的多種表現形態。
2)前提:
a)必須是類與類之間必須有關係,要麼繼承,要麼實現。
b)通常情況還應存在覆蓋。
3)體現:
a)父類的引用指向子類物件;
b)父類的引用也可以接受自己的子類物件。4)利弊:
好處:多型的出現大大提高了程式的擴充套件性,降低了程式的耦合性。
弊端:雖然提高了程式的擴充套件性,但是隻能使用父類的引用訪問父類中的成員。5)特點:
a)非靜態成員函式:
編譯時期:如果父類的引用指向子類物件,那麼該引用呼叫的方法必須父類要有,否則編譯不通過。
執行時期:父類的引用先查詢子類的方法,如沒有則執行父類的方法,其實執行的都是子類物件的方法。
b)成員變數:無論編譯還是執行,都看左邊,即引用所屬的類如果所屬父類就呼叫父類的成員變數,即使子類有覆蓋也一樣。
c)靜態成員方法:無論編譯還是執行,都參考左邊。也就是說引用所屬的類為父類就呼叫父類的方法,否則呼叫子類的方法。
6)程式碼例項:
//定義一個抽象類Animal
abstract class Animal
{
//動物都具有吃的功能
public abstract void eat();
}
//定義一個類Cat,並繼承Animal
class Cat extends Animal
{
//複寫父類的eat()方法
public void eat()
{
System.out.println("吃魚");
}
//定義特有的方法
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
//定義一個類Dog,並繼承Animal
class Dog extends Animal
{
//複寫父類的eat()方法
public void eat()
{
System.out.println("吃骨頭");
}
//定義特有的方法
public void kanJia()
{
System.out.println("看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Animal a= new Dog();//型別提升,多型的體現
function(a);
Cat c=new Cat();
function(c);
}
//父類引用只能訪問父類的方法,如果訪問的方法子類已經覆蓋或實現,則執行子類的方法,
//如果要訪問子類的特有方法,那麼需要把父類引用強轉成子類。該方法體現了多型的性質。
public static void function(Animal a)
{
a.eat();
//判斷動物a是否為cat
if(a instanceof Cat)
{
/*強制將父類的引用,轉成子類型別,向下轉型.
強制轉換的是父類的引用而不是父類的物件,只有指向子類的
父類的引用才可以進行強制轉換,多型自始至終都是子類物件
在做著變化。
*/
Cat c= (Cat)a;
c.catchMouse();
}
}
}