封裝:
封裝是把過程和數據包圍起來,對數據的訪問只能通過已定義的接口。面向對象計算始於這個基本概念,即現實世界可以被描繪成一系列完全自治、封裝的對象,這些對象通過一個受保護的接口訪問其他對象。封裝是一種信息隱藏技術,在java中通過關鍵字private實現封裝。什么是封裝?封裝把對像的所有組成部分組合在一起,封裝定義程序如何引用對象的數據,封裝實際上使用方法將類的數據隱藏起來,控制用戶對類的修改和訪問數據的程度。
繼承
繼承的好處:
1,提高了代碼的復用性。
2,讓類與類之間產生了關系,給第三個特征多態提供了前提。
java中支持單繼承,一個子類織女呢個有一個直接父類。
java支持多層繼承,當要使用一個繼承體系時,1,查看該體系中的頂層類,交接該體系的基本功能。
2,創建體系中的最子類對象,完成功能的使用。
什么時候定義繼承?
當類與類之間存在着所屬關系的時候就定義繼承。xxx是yyy中的一種,xxx extends yyy
所屬關系:is a
當本類的成員和局部變量同名用this區分。
當子父類中成員變量同名時用super區分父類。
this代表本類對象的引用
super代表父類的一個空間
重寫:當子父類中出現相同方法時,會先運行子類中的方法。
重寫的特點:方法名一樣,訪問修飾符權限不小於父類,返回類型一致,參數列表一致。
什么時候用重寫?
當對一個類進行子類的擴展時,子類需要保留父類的功能聲明,
但是要定義子類中該功能的特有內容時,就使用重寫操作完成。
子父類中的構造方法的特點:
在子類構造對象時,發現,訪問子類構造函數時,父類也運行了。
原因:在子類構造方法中的第一行有一個默認的隱士語句。super();
調用父類中的空參數構造函數。
子類的實例化過程:子類中的構造方法都會訪問父類中的無參構造方法
什么是多態
- 面向對象的三大特性:封裝、繼承、多態。從一定角度來看,封裝和繼承幾乎都是為多態而准備的。這是我們最后一個概念,也是最重要的知識點。
- 多態的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用)
- 實現多態的技術稱為:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。
- 多態的作用:消除類型之間的耦合關系。
- 現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;如果當前在 Word 下彈出的就是 Word 幫助;在 Windows 下彈出的就是 Windows 幫助和支持。同一個事件發生在不同的對象上會產生不同的結果。
下面是多態存在的三個必要條件,要求大家做夢時都能背出來!
多態存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類對象。
多態的好處:
1.可替換性(substitutability)。多態對已存在代碼具有可替換性。例如,多態對圓Circle類工作,對其他任何圓形幾何體,如圓環,也同樣工作。
2.可擴充性(extensibility)。多態對代碼具有可擴充性。增加新的子類不影響已存在類的多態性、繼承性,以及其他特性的運行和操作。實際上新加子類更容易獲得多態功能。例如,在實現了圓錐、半圓錐以及半球體的多態基礎上,很容易增添球體類的多態性。
3.接口性(interface-ability)。多態是超類通過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現的。如圖8.3 所示。圖中超類Shape規定了兩個實現多態的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere為了實現多態,完善或者覆蓋這兩個接口方法。
4.靈活性(flexibility)。它在應用中體現了靈活多樣的操作,提高了使用效率。
5.簡化性(simplicity)。多態簡化對應用軟件的代碼編寫和修改過程,尤其在處理大量對象的運算和操作時,這個特點尤為突出和重要。
Java中多態的實現方式:接口實現,繼承父類進行方法重寫,同一個類中進行方法重載。
Java中的繼承、封裝、多態
繼承的理解:
1、繼承是面向對象的三大特征之一,也是實現代碼復用的重要手段。Java的繼承具有單繼承的特點,每個子類只有一個直接父類。
2、Java的繼承通過extends關鍵字來實現,實現繼承的類被稱為子類,被繼承的類稱為父類(有的也稱其為基類、超類),父類和子類的關系,是一種一般和特殊的關系。就像是水果和蘋果的關系,蘋果繼承了水果,蘋果是水果的子類,水果是蘋果的父類,則蘋果是一種特殊的水果。
3、Java使用extends作為繼承的關鍵字,extends關鍵字在英文是擴展的意思,而不是繼承。為什么國內把extends翻譯成繼承呢?除了與歷史原因有關外,把extends翻譯成為繼承也是有其道理的:子類擴展父類,將可以獲得父類的全部屬性和方法,這與漢語中得繼承(子輩從父輩那里獲得一筆財富成為繼承)具有很好的類似性。值得指出的是:Java的子類不能獲得父類的構造器。
4、實例:
class BaseClass{
public double weight;
public void info() {
System.out.println("我的體重是"+weight+"千克");
}
}
public class ExtendsDemo001 extends BaseClass {
public static void main(String[] args) {
//創建ExtendsDemo001對象
ExtendsDemo001 ed = new ExtendsDemo001();
//ExtendsDemo001本身沒有weight屬性,但是ExtendsDemo001的父類有weight屬性,也可以訪問ExtendsDemo001對象的屬性
ed.weight = 56;
//調用ExtendsDemo001對象的info()方法
ed.info();
}
}
打印結果為:我的體重是56.0千克
5、Java類只能有一個父類。這句話是錯誤的,應該這樣說:Java類只能有一個直接父類,可以有無限多個間接父類,如:
class Fruit extends Plant{…….}
class Apple extends Fruit {…….}
重寫父類的方法:
1、 大部分的時候,子類總是以父類為基礎,額外添加新的屬性和方法。但有一種情況例外:子類需要重寫父類的方法。例如鳥類都包含了飛翔的方法,其中鴕鳥是一種特殊的鳥類,因此鴕鳥也是鳥的子類,因此它也將從鳥類獲得飛翔方法,但這個飛翔方法明顯不適合鴕鳥,為此,鴕鳥需要重寫鳥類的方法。
2、 如下代碼可以幫助理解重寫:
1) class Bird{
2) //Bird類的fly()方法
3) private void fly(){
4) System.out.println("我要在天空中飛翔");
5) }
6) }
7) public class OcerrideTest extends Bird{
8) //重寫Bird類的fly()方法
9) public void fly(){
10) System.out.println("我只能在地上奔跑");
11) }
12) public static void main(String[] args) {
13) //創建OcerrideTest對象
14) OcerrideTest ot = new OcerrideTest();
15) ot.fly();
16) }
17) }
打印結果為:我只能在地上奔跑
這種子類包含父類同名方法的現象被稱為方法重寫,也被稱為方法覆蓋(Override)。
方法的重寫要遵循“兩同兩小一大”規則:
⑴ “兩同”:方法名相同;形參列表相同。
⑵ “兩小”:子類方法之返回類型應比父類方法返回值類型更小或相等;子類方法聲明拋出的異常類應比父類方法聲明拋出的異常類更小或相等。
⑶ 子類方法的訪問權限應比父類方法更大或相等
尤其需要指出的是:覆蓋方法和被覆蓋方法要么都是類方法,要么都是實例方法,不能一個是類方法,一個是實例方法,例如下面的代碼將會有編譯錯誤:
Class BaseClass{
public static void test(){…….}
}
Class SubClass extends BaseClass{
public void test(){………….}
}
若想調用父類中的fly()方法,則只需在子類中fly()方法中加上如下代碼即可:
super.fly();
注意:super和this一樣,都不能出現在static的方法中
調用父類構造器
1、看如下程序定義的Basehe Sub類,其中Sub類是Base類的子類,程序在Sub類的構造器中使用super來調用Base構造器里的初始化代碼。
class Base{
public double size;
public String name;
public Base(double size, String name){
this.size=size;
this.name=name;
}
}
public class Sub extends Base{
public String color;
public Sub(double size, String name, String color){
//在子類構造器中調用父類構造器,使用super調用來實現
super(size,name);
this.color = color;
}
public static void main(String[] args) {
Sub s = new Sub(5.6,"測試對象","紅色");
System.out.println(s.size+"------"+s.name+"------"+s.color);
}
}
打印結果為:5.6------測試對象------紅色
靜態初始化塊
1、如果定義初始化塊時使用了static修飾符,則這個初始化塊,就變成了靜態初始化塊,也被稱作為類初始化塊。靜態初始化塊是類相關的,系統將在類初始化階段執行靜態初始化塊,而不是在創建對象時才執行。因此靜態初始化塊總是比普通初始化塊先執行
封裝的理解:
1、封裝(Encapsulation)是面向對象的三大特征之一,它指的是將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象內部信息,而是通過該類所提供的方法來實現對內部信息的操作和訪問。
2、掌握了訪問控制符的用法之后,下面通過使用合理的訪問控制來定義一個Person類,這個Person類就實現了良好的封裝。代碼如下:
public class Person {
public static void main(String[] args) {
Person p = new Person();
p.setAge(10);
System.out.println(p.getAge());
}
//將屬性使用private修飾,將這些屬性隱藏起來
private String name;
private int age;
//提供方法來操作name屬性
public void setName(String name) {
//對姓名執行合理的校驗
if(name.length() > 6 || name.length() < 2){
System.out.println("您的姓名不符合要求");
}else{
this.name = name;
}
}
public String getName() {
return this.name;
}
//提供方法來操作age屬性
public void setAge(int age) {
if(age>100 || age<0){
System.out.println("您的年齡必須要在0~100之間");
}else{
this.age = age;
}
}
public int getAge() {
return this.age;
}
}
運行結果為:10
多態的理解:
1、多態(Polymorphism)是面向對象的三大特征之一。
2、Java引用變量有兩個類型:一個是編譯時類型,一個是運行時類型。編譯時的類型由聲明該變量時使用的類型決定,運行時的類型由實際賦給該變量的對象決定。如果編譯時類型和運行時類型不一致,就會出現所謂的多態(Polymorphism)
先看下面的程序:
class SuperClass{
public int book = 6;
public void base() {
System.out.println("父類的普通方法base()");
}
public void test(){
System.out.println("父類中北覆蓋的方法");
}
}
public class PloymorphismTest001 extends SuperClass{
//重新定義一個book實例屬性,覆蓋父類的book實例屬性
public String book = "Java瘋狂講義";
public void test() {
System.out.println("子類中覆蓋父類的方法");
}
private void Dmeo() {
System.out.println("子類中普通的方法");
}
//主方法
public static void main(String[] args) {
//下面編譯時類型和運行時類型完全一樣,因此不存在多態
SuperClass sc = new SuperClass();
System.out.println("book1= "+sc.book);//打印結果為:6
//下面兩次調用將執行SuperClass的方法
sc.base();
sc.test();
//下面編譯時類型和運行時類型完全一樣,因此不存在多態
PloymorphismTest001 pt = new PloymorphismTest001();
System.out.println("book2= "+pt.book);//打印結果為:Java瘋狂講義
//下面調用將執行從父類繼承到的base方法
pt.base();
//下面調用將執行當前類的test方法
pt.test();
//下面編譯時類型和運行時類型不一樣,多態發生
SuperClass sscc = new PloymorphismTest001();
//結果表明訪問的是父類屬性
System.out.println("book3= "+sscc.book);//打印結果為:6
//下面調用將執行從父類繼承到得base方法
sscc.base();
//下面調用將執行當前類的test方法
sscc.test();
//因為sscc的編譯類型是SuperClass,SuperClass類沒有提供Demo()方法
//所以下面代碼編譯時會出現錯誤
//sscc.Demo();
}
}
程序運行結果為:
book1= 6
父類的普通方法base()
父類中北覆蓋的方法
book2= Java瘋狂講義
父類的普通方法base()
子類中覆蓋父類的方法
book3= 6
父類的普通方法base()
子類中覆蓋父類的方法
上面程序的main方法中顯示創建而來3個引用變量,對於前兩個引用變量sc和pt,它們編譯時類型和運行時類型完全相同,因此調用它們的屬性和方法非常正常,完全沒有問題。但第三個引用變量sscc,則比較特殊,它編譯時類型是SuperClass ,而運行時類型是PloymorphismTest001,當調用該引用變量的test方法時,實際上是執行PloymorphismTest001類中覆蓋后的test方法,這就是多態。
當把一個子類對象直接賦給父類引用變量,例如上面的SuperClass sscc = new PloymorphismTest001();這個sscc引用變量的編譯時類型是SuperClass,而運行時類型是PloymorphismTest001,當運行時調用該引用變量的方法時,其方法行為總是像子類方法的行為,而不是像父類方法行為,這將出現相同類型的變量、執行同一個方法時呈現出不同的行為特征,這就是多態。
特別提醒:
與方法不同的是,對象的屬性則不具備多態性,如上面的sscc引用變量,程序中輸出它的book屬性時,並不是輸出PloymorphismTest001類里定義的實例屬性,而是輸出SuperClass類的實例屬性。
注意:
我們通過Object p = new Person()代碼定義一個變量p,則這個p只能調用Object類的方法,而不能調用Person類里定義的方法。
在沒有好好地研習面向對象
剛學Java語言時,就很難理解為什么要有接口這個概念,雖說是可以實現所謂的多繼承,可一個只有方法名,沒有方法體的東西,我實現它又有什么用呢?我從它那什么也得不到,除了一些方法名,我直接在具體類里加入這些方法不就行了嗎?
為什么一定要有抽象類這個概念?為什么就不能把這個父類寫成一個具體的類,子類再繼承它不就可以了嗎?何必弄一個抽象類出來,還要弄一些沒有方法體的抽象方法,弄得又象接口又象類的,讓人捉摸不定。
當我開始
我還是說說我現在對面向對象思想的理解吧,不一定正確全面,但我想應該還算是比以前略有進步吧。
面向對象思想,我覺得最關鍵的就是抽象。
一個軟件設計的好壞,我想很大程度上取決於它的整體架構,而這個整體架構其實就是你對整個宏觀商業業務的抽象框架,當代表業務邏輯的高層抽象層結構 合理時,你底層的具體實現需要考慮的就僅僅是一些算法和一些具體的業務實現了。當你需要再開發另一個相近的項目時,你以前的抽象層說不定還可以再次利用 呢,面對對象的設計,復用的重點其實應該是抽象層的復用,而不是具體某一個代碼塊的復用,是不是一下子感覺自己對復用理解的高度又上升了一層?^_^
說到了抽象,我就不能不提到曾讓我頭痛的Java接口和Java抽象類了,這也是本文我想說的重點。
既然面向對象設計的重點在於抽象,那Java接口和Java抽象類就有它存在的必然性了。
Java接口和Java抽象類代表的就是抽象類型,就是我們需要提出的抽象層的具體表現。OOP面向對象的編程,如果要提高程序的復用率,增加程序 的可維護性,可擴展性,就必須是面向接口的編程,面向抽象的編程,正確地使用接口、抽象類這些太有用的抽象類型做為你結構層次上的頂層。
Java接口和Java抽象類有太多相似的地方,又有太多特別的地方,究竟在什么地方,才是它們的最佳位置呢?把它們比較一下,你就可以發現了。
1、Java接口和Java抽象類最大的一個區別,就在於Java抽象類可以提供某些方法的部分實現,而Java接口不可以,這大概就是Java抽象類唯一的優點吧,但這個優點非常有用。
如果向一個抽象類里加入一個新的具體方法時,那么它所有的子類都一下子都得到了這個新方法,而Java接口做不到這一點,如果向一個Java接口里加入一 個新方法,所有實現這個接口的類就無法成功通過編譯了,因為你必須讓每一個類都再實現這個方法才行,這顯然是Java接口的缺點。
2、一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中,而由於Java語言的單繼承性,所以抽象類作為類型定義工具的效能大打折扣。
在這一點上,Java接口的優勢就出來了,任何一個實現了一個Java接口所規定的方法的類都可以具有這個接口的類型,而一個類可以實現任意多個Java接口,從而這個類就有了多種類型。
3、從第2點不難看出,Java接口是定義混合類型的理想工具,混合類表明一個類不僅僅具有某個主類型的行為,而且具有其他的次要行為。
4、結合1、2點中抽象類和Java接口的各自優勢,具精典的設計模式就出來了:聲明類型的
在Java語言API中用了這種模式,而且全都遵循一定的命名規范:Abstract +接口名。
Java接口和Java抽象類的存在就是為了用於具體類的實現和繼承的,如果你准備寫一個具體類去繼承另一個具體類的話,那你的設計就有很大問題了。Java抽象類就是為了繼承而存在的,它的抽象方法就是為了強制子類必須去實現的。
使用Java接口和抽象Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。而不要用具體Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。
我想,如果你編的代碼里面連一個接口和抽象類都沒有的話,也許我可以說你根本沒有用到任何設計模式,任何一個設計模式都是和抽象分不開的,而抽象與Java接口和抽象Java類又是分不開的。
理解抽象,理解Java接口和抽象Java類,我想就應該是真正開始用面向對象的思想去分析問題,解決問題了吧。