6、面向對象編程
6.1面向對象編程基礎
(1)面向對象概述
1.對象:在現實社會中隨處可見的一種食物都是對象,對象是食物存在的實體。通常將對象分爲動態和靜態兩種。靜態即屬性,動態即行爲。
2.類:類就是同一類事物的統稱。
3.面向對象程序設計的特點
3.1封裝--封裝是面向對象編程的核心思想。將對象的屬性和行爲封裝起來,其載體爲類,類通常對對象隱藏其實現細節,這就是封裝思想。採用封裝思想保證了類內部數據結構的完整性,應用該類的用戶不能輕易地直接操作此數據結構,之恩能夠執行類允許公開的數據,這樣避免了外部操作對內部數據的影響,提高了程序的可維護性。
3.2繼承--類與類之間的關係,繼承是其一種,繼承主要是利用特定對象之間的共有屬性
3.3多態--將父類對象應用於子類的特徵就是多態。
(2)類與對象
1.成員變量--在java中對象的屬性也稱爲成員變量,成員變量的定義與普通變量的定義一樣。語法 數據類型 變量名[= 值] 其中[= 值]是可選內容,定義變量時可以爲其賦值也可以不賦值。
2.成員方法--在java中,成員方法對應於類對象的行爲,它主要用來定義類可執行的操作,他是包含一系列語句的代碼塊。
2.1成員方法的定義--語法:[權限修飾符][返回值類型]方法名([參數類型 參數名])[throws 異常類型]{
…//方法體
return 返回值;
}
2.2成員方法的參數--調用方法時可以給該方法傳遞一個或多個值,傳給內部方法叫實參,在方法內部接受實參的變量叫形參,形參的聲明語法與變量的聲明語法一樣,形參只在方法內部有效
2.2.1值參數--表明實參和形參之間按值傳遞,當值參數被調用時編譯器爲形參分配存儲單元,然後將對應的實參的值複製到形參中,由於是值類型的傳遞方式,所以在方法中對值類型的形參的修改不會影響到實參。
2.2.2引用參數--如果在給方法傳遞參數時,參數的類型是數組或其他的引用類型,那麼在方法中對參數的修改會反映到原有的數組或其他引用類型上,這種類型的參數稱之爲引用參數。
2.2.3不定長參數--聲明方法時,如果有若干個相同類型的參數,可以定義爲不定長參數,
方法:權限修飾符 返回值類型 方法名(參數類型 …參數名)
2.3成員方法的使用
代碼實現:
public class Leopard {
public voidgaze(String target) {// 凝視。目標是參數target
System.out.println("獵豹凝視:"+ target);
}
public voidrun() {// 奔跑
System.out.println("獵豹開始奔跑");
}
public booleancatchPrey(String prey) {// 捕捉獵物,返回捕捉是否成功
System.out.println("獵豹開始捕捉"+ prey);
returntrue;// 返回成功
}
public voideat(String meat) {// 吃肉,參數是肉
System.out.println("獵豹吃"+ meat);
}
public voidsleep() {// 睡覺
System.out.println("獵豹睡覺");
}
public staticvoid main(String[] args) {
Leopard liebao= new Leopard();
liebao.gaze("羚羊");
liebao.run();
liebao.catchPrey("羚羊");
liebao.eat("羚羊肉");
liebao.sleep();
}
}
結果:
獵豹凝視:羚羊
獵豹開始奔跑
獵豹開始捕捉羚羊
獵豹吃羚羊肉
獵豹睡覺
3.構造方法--構造方法是一個與類同名的方法,對象的創建就是通過構造方法完成的。
構造方法的特點:(1)構造方法沒有返回類型,也不能定義爲void(2)構造方法的名稱要與本類的名稱相同(3)構造方法的主要作用就是完成對象的初始化工作,他能把定義對象的參數傳遞給對象成員。
4.局部變量--在成員方法中定義一個變量,那麼這個變量就稱爲局部變量,局部變量在方法中被執行時創建,方法結束時銷燬。
5.局部變量的有效範圍--局部變量的有效範圍從該變量的聲明開始到該變量的結束爲止。
6.對象的創建--在java語言中使用new創建對象。語法 Test test =new Test();
7.訪問對象的屬性和行爲--用戶使用new操作符創建一個對象後,可以使用“對象.類成員”來獲取對象的屬性和行爲。當對象獲取類成員時,也相應的獲取了對象的屬性和行爲
8.對象的銷燬--java會對一下兩種情況的對象進行銷燬(1)對象引用超過其作用範圍,(2)講對象賦值爲null。
9.this關鍵字--明確引用的是類成員還是局部變量或者方法參數等。
(3)static 關鍵字--由static 修飾的變量、常量和方法被稱作靜態變量、靜態方法、靜態常量。
1.靜態變量--java程序中把共享的變量用static修飾,該變量就是靜態變量。
2.靜態常量--在java中用 final static 修飾一個成員變量,這個成員變量就是一個靜態常量。
3.靜態方法--java中用static修飾的方法就是靜態方法。
4.靜態代碼塊--用static修飾的代碼區域爲靜態代碼塊。
(4)類的主方法--主方法是類的入口點,他定義了程序從何處開始;主方法提供對程序控制流向的控制;
主方法的語法:public static void main(String [] args){
//方法體
}
6.2面向對象核心技術
(1)類的封裝
代碼實現:
public classRestaurant4 {
privateCook2 cook = new Cook2();// 餐廳封裝的廚師類
publicvoid takeOrder(String dish) {// 下單
cook.cooking(dish);//通知廚師做菜
System.out.println("您的菜好了,請慢用。");
}
publicString saySorry() {// 拒絕顧客請求
return"抱歉,餐廳不提供此項服務。";
}
publicstatic void main(String[] args) {
Restaurant4water = new Restaurant4();// 創建餐廳對象,爲顧客提供服務
System.out.println("**請讓廚師爲我做一份香辣肉絲。***");
water.takeOrder("香辣肉絲");//服務員給顧客下單
System.out.println("**你們的廚師叫什麼名字?***");
System.out.println(water.saySorry());//服務員給顧客善意的答覆
System.out.println("**請讓廚師給我切一點蔥花。***");
System.out.println(water.saySorry());///服務員給善意的答覆顧客
}
}
結果
**請讓廚師爲我做一份香辣肉絲。***
Tom Cruise洗蔬菜
Tom Cruise切蔥花
Tom Cruise開始烹飪香辣肉絲
您的菜好了,請慢用。
**你們的廚師叫什麼名字?***
抱歉,餐廳不提供此項服務。
**請讓廚師給我切一點蔥花。***
抱歉,餐廳不提供此項服務。
封裝的思想就是對客戶隱藏其細節,
(2)類的繼承--及城市程序開發中重要的概念,使整個程序架構具備一定的彈性,減少開發週期,提高軟件的可維護型和可擴展性。
1.extends關鍵字--java中讓一個類繼承另一個類,用extends關鍵字,語法:子類 extends 父類
2.方法的重寫
2.1重寫的實現--重寫就是在子類中將父類的成員方法的名稱保留,重新編寫成員方法的實現內容,更改成員方法的存儲權限,或是修改成員方法的返回值類型。在繼承中還有一種特殊的重寫方式,子類與父類的成員方法返回值,方法名稱、參數類型及個數完全相同,唯一不同的世方法實現內容,這種特殊的重寫方法叫重構。
2.2super關鍵字--用來在方法重寫後調用父類的屬性和方法。
3.所有類的父類——Object類--所有類都直接或間接的繼承了java.lang.Object類,下面介紹幾個重要方法
3.1getClass()方法--他會返回對象執行時的class實例,然後使用此實例調用getName方法獲取類的名稱
3.2toString()方法--將一個對象返回爲字符串形式,會返回一個String實例
3.3equals()方法--比較的是兩個對象的實際內容
(3)類的多態
1.方法的重載--在同一個類中允許同時存在一個以上的同名方法,只要這些方法的參數個數或類型不同即可。
代碼實現:
public classOverLoadTest {
//定義一個方法
publicstatic int add(int a) {
returna;
}
//定義與第一個方法參數個數不同的方法
publicstatic int add(int a, int b) {
returna + b;
}
//定義與第一個方法相同名稱、參數類型不同的方法
publicstatic double add(double a, double b) {
returna + b;
}
//定義一個成員方法
publicstatic int add(int a, double b) {
return(int) (a + b);
}
//這個方法與前一個方法參數次序不同
publicstatic int add(double a, int b) {
return(int) (a + b);
}
//定義不定長參數
publicstatic int add(int... a) {
ints = 0;
//根據參數個數循環操作
for(int i = 0; i < a.length; i++) {
s+= a[i];// 將每個參數的值相加
}
returns;// 將計算結果返回
}
publicstatic void main(String args[]) {
System.out.println("調用add(int)方法:" + add(1));
System.out.println("調用add(int,int)方法:" + add(1,2));
System.out.println("調用add(double,double)方法:" + add(2.1,3.3));
System.out.println("調用add(int a,double b)方法:" + add(1, 3.3));
System.out.println("調用add(doublea, int b) 方法:" + add(2.1, 3));
System.out.println("調用add(int...a)不定長參數方法:"+ add(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println("調用add(int...a)不定長參數方法:" + add(2, 3, 4));
}
}
結果:
調用add(int)方法:1
調用add(int,int)方法:3
調用add(double,double)方法:5.4
調用add(int a, double b)方法:4
調用add(double a, int b) 方法:5
調用add(int... a)不定長參數方法:45
調用add(int... a)不定長參數方法:9
2.向上轉型--將具體類轉換爲抽象類
代碼實現
classQuadrangle { //四邊形類
publicstatic void draw(Quadrangle q) {//四邊形類中方法
//SomeSentence
}
}
public classParallelogram extends Quadrangle {
publicstatic void main(String args[]) {
Parallelogramp=new Parallelogram();
draw(p);
}
}
3.向下轉型--將抽象類轉化爲具體類
代碼
classQuadrangle {
publicstatic void draw(Quadrangle q) {
//SomeSentence
}
}
public classParallelogram extends Quadrangle {
publicstatic void main(String args[]) {
draw(newParallelogram());
//將平行四邊形類對象看作是四邊形對象,稱爲向上轉型操作
Quadrangleq = new Parallelogram();
Parallelogramp = (Parallelogram) q; // 將父類對象賦予子類對象
////將父類對象賦予子類對象,並強制轉換爲子類型
//Parallelogramp = (Parallelogram) q;
}
}
4.instanceof關鍵字--判斷是否一個類實現了某個接口,也可以用它來判斷一個實例對象是否屬於一個類
代碼實現
class Quadrangle {
publicstatic void draw(Quadrangle q) {
//SomeSentence
}
}
class Square extends Quadrangle {
//SomeSentence
}
class Anything {
//SomeSentence
}
public class Parallelogram extendsQuadrangle {
publicstatic void main(String args[]) {
Quadrangleq = new Quadrangle(); // 實例化父類對象
//判斷父類對象是否爲Parallelogram子類的一個實例
if(q instanceof Parallelogram) {
Parallelogramp = (Parallelogram) q; // 進行向下轉型操作
}
//判斷父類對象是否爲Parallelogram子類的一個實例
if(q instanceof Square) {
Squares = (Square) q; // 進行向下轉型操作
}
//由於q對象不爲Anything類的對象,所以這條語句是錯誤的
//System.out.println(q instanceof Anything);
}
}
(4)抽象類與接口
1.抽象類與抽象方法--定義抽象類時,需要使用abstract關鍵字
代碼實現:
public abstract class Market {
publicString name;//商場名稱
public String goods;//商品名稱
public abstract void shop();//抽象方法,用來輸出信息
}
public classTaobaoMarket extends Market {
@Override
publicvoid shop() {
//TODO Auto-generated method stub
System.out.println(name+"網購"+goods);
}
}
public classWallMarket extends Market {
@Override
publicvoid shop() {
//TODO Auto-generated method stub
System.out.println(name+"實體店購買"+goods);
}
}
/**
* 使用抽象類模擬“去商場買衣服”的案例,然後通過派生類確定到底去哪個商場買衣服,買什麼樣的衣服。
*/
public classGoShopping {
publicstatic void main(String[] args) {
Marketmarket = new WallMarket();// 使用派生類對象創建抽象類對象
market.name= "沃爾瑪";
market.goods= "七匹狼西服";
market.shop();
market= new TaobaoMarket();// 使用派生類對象創建抽象類對象
market.name= "淘寶";
market.goods= "韓都衣舍花裙";
market.shop();
}
}
結果:
沃爾瑪實體店購買七匹狼西服
淘寶網購韓都衣舍花裙
使用抽象類和抽象方法時,需要遵循以下原則
1)在抽象類中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的類必須被定義爲抽象類
2)抽象類不能直接實例化,即使抽象類中沒有聲明抽象方法,也不能實例化
3)抽象類被繼承後,子類需要實現其中所有的抽象方法
4)如果繼承抽象類的子類也被聲明爲抽象類,則可以不用實現父類中所有的抽象方法
2.接口的聲明及實現--接口是抽象類的延伸,接口中所有方法都沒有方法體
代碼實現:
interfacedrawTest { // 定義接口
publicvoid draw(); // 定義方法
}
// 定義平行四邊形類,該類實現了drawTest接口
classParallelogramgleUseInterface implements drawTest {
publicvoid draw() { // 由於該類實現了接口,所以需要覆蓋draw()方法
System.out.println("平行四邊形.draw()");
}
}
// 定義正方形類,該類實現了drawTest接口
classSquareUseInterface implements drawTest {
publicvoid draw() {
System.out.println("正方形.draw()");
}
}
public classQuadrangleUseInterface { // 定義四邊形類
publicstatic void main(String[] args) {
drawTest[]d = { // 接口也可以進行向上轉型操作
newSquareUseInterface(), new ParallelogramgleUseInterface() };
for(int i = 0; i < d.length; i++) {
d[i].draw();// 調用draw()方法
}
}
}
結果
正方形.draw()
平行四邊形.draw()
3.多重繼承--java中實現多重繼承,須使用接口
代碼實現:
public interfaceIFather {// 定義一個接口
voidsmoking();// 抽菸的方法
voidgoFishing();// 釣魚的方法
}
public interfaceIMather {// 定義一個接口
voidwatchTV();// 看電視的方法
voidcooking();// 做飯的方法
}
public classMe implements IFather, IMather {// 繼承IFather接口和IMather接口
@Override
publicvoid watchTV() {// 重寫watchTV()方法
System.out.println("我喜歡看電視");
}
@Override
publicvoid cooking() {// 重寫cook()方法
System.out.println("我喜歡做飯");
}
@Override
publicvoid smoking() {// 重寫smoke()方法
System.out.println("我喜歡抽菸");
}
@Override
publicvoid goFishing() {// 重寫goFishing()方法
System.out.println("我喜歡釣魚");
}
publicstatic void main(String[] args) {
IFatherfather = new Me();// 通過子類創建IFather接口對象
System.out.println("爸爸的愛好:");
//使用接口對象調用子類中實現的方法
father.smoking();
father.goFishing();
IMathermather = new Me();// 通過子類創建IMather接口對象
System.out.println("\n媽媽的愛好:");
//使用接口對象調用子類中實現的方法
mather.cooking();
mather.watchTV();
}
}
結果:
爸爸的愛好:
我喜歡抽菸
我喜歡釣魚
媽媽的愛好:
我喜歡做飯
我喜歡看電視
4.區分抽象類與接口--抽象類是對根源的抽象,接口是對動作的抽象
抽象類與接口的不同 | ||
比較項 | 抽象類 | 接口 |
方法 | 可以有非抽象類 | 所有方法都是抽象方法 |
屬性 | 屬性中可以有非靜態常量 | 所有的屬性都是靜態常量 |
構造方法 | 有構造方法 | 沒有構造方法 |
繼承 | 一個類只能繼承一個父類 | 一個類可以同時實現多個接口 |
被繼承 | 一個類只能繼承一個父類 | 一個接口可以同時繼承多個接口 |
(5)訪問控制
1.訪問控制符
Java語言中的訪問控制符權限 | ||||
| Public | Protected | Default(缺省) | Private |
本類 | 可見 | 可見 | 可見 | 可見 |
本類所在包 | 可見 | 可見 | 可見 | 不可見 |
其他包中的子類 | 可見 | 可見 | 不可見 | 不可見 |
其他包中的非子類 | 可見 | 不可見 | 不可見 | 不可見 |
使用訪問控制符時,需要遵循以下原則:
1)大部分頂級類都是用public修飾;
2)如果某個類主要用作其他類的父類,該類中包含的大部分方法只是希望被其子類重寫,要不想被外界直接調用,則應該使用protected修飾;
3)類中的絕大部分屬性都應該使用private修飾,除非一些static或者類似全局變量的屬性,才考慮使用public修飾;
4)當定義的方法只是用於輔助實現該類的其他方法(即工具方法),應該使用private修飾;
5)希望允許其他類自由調用的方法應該使用public修飾。
2.java類包--類包不僅可以解決類名衝突問題,還可以在開發龐大的應用程序是,幫助開發者管理龐大的應用程序組件,方便軟件複用
3.final關鍵字
3.1final類--定義爲final不能被繼承。
3.2final方法--final方法不能被重寫,可以防止子類修改該類的定義與實現方式,同時定義final的方法執行效率遠高於非final方法。
3.3final變量--用於變量聲明,一旦該變量被設定,就不可以在改變該變量的值。
(6)內部類
1.成員內部類
1.1成員內部類的簡介--在一個類中使用內部類,可以直接存取其所在類的私有成員變量。
語法:public class OuterClass{//外部類
private class InnerClass{//內部類
//……
}
}
1.2內部類向上轉型爲接口
代碼實現:
interfaceOutInterface { // 定義一個接口
publicvoid f();
}
public classInterfaceInner {
publicstatic void main(String args[]) {
OuterClass2 out = new OuterClass2 (); // 實例化一個OuterClass2對象
//調用doit()方法,返回一個OutInterface接口
OutInterfaceoutinter = out.doit();
outinter.f();// 調用f()方法
}
}
classOuterClass2 {
//定義一個內部類實現OutInterface接口
privateclass InnerClass implements OutInterface {
InnerClass(Strings) { // 內部類構造方法
System.out.println(s);
}
publicvoid f() { // 實現接口中的f()方法
System.out.println("訪問內部類中的f()方法");
}
}
publicOutInterface doit() { // 定義一個方法,返回值類型爲OutInterface接口
returnnew InnerClass("訪問內部類構造方法");
}
}
1.3使用this關鍵字獲取內部類與外部類的引用--在外部類與內部類的成員變量名稱相同時可以使用this關鍵字
2.局部內部類--可以在類的局部位置進行定義。
代碼實現:
interface OutInterface2 { // 定義一個接口
}
class OuterClass3 {
publicOutInterface2 doit(final String x) { // doit()方法參數爲final類型
//在doit()方法中定義一個內部類
classInnerClass2 implements OutInterface2 {
InnerClass2(Strings) {
s= x;
System.out.println(s);
}
}
returnnew InnerClass2("doit");
}
}
內部類被定義在了doit()方法內部。在doit()方法外部無法訪問該內部類,但該內部類可以訪問當前代碼塊的常量以及此外部類的所有成員
3.匿名內部類
代碼實現
interfaceOutInterface2 { // 定義一個接口
}
classOuterClass4 {
publicOutInterface2 doit() { // 定義doit()方法
returnnew OutInterface2() { // 聲明匿名內部類
privateint i = 0;
publicint getValue() {
returni;
}
};
}
}
使用匿名內部類應該遵循以下原則:
1)匿名類沒有構造方法
2)匿名類不能定義靜態的成員
3)匿名類不能用private、public、protected、static、final、abstract等修飾符
4)只可以創建一個匿名類實例。
4.靜態內部類--在內部類前加static修飾,該內部類就是靜態內部類(在程序開發中比較少見)
5.內部類的繼承--需要設置專門的語法來完成
代碼實現
public classOutputInnerClass extends ClassA.ClassB { // 繼承內部類ClassB
publicOutputInnerClass(ClassA a) {
a.super();
}
}
class ClassA{
classClassB {
}
}
在某個類繼承內部類時,必須硬性的給予這個類一個帶參數的構造方法,並且該構造方法的參數必須是該內部類的外部類引用,就像例子中的ClassA a,同時在構造方法體重使用a.super()語句