這集主要解釋什么是面向?qū)ο蟆?/div>
這集給我的沖擊很大,以前我是用C語言寫代碼,使用的面向過程的思維去設(shè)計程序。面向?qū)ο髮τ谖襾碚f是一個全新編程思維。
剛接觸這種思維,可能是因為以前習(xí)慣性用面向過程的思維去設(shè)計程序,現(xiàn)在有些轉(zhuǎn)不過彎來。
1、首先確定誰來做,其次確定怎么去做。
2、首先考慮全局,再考慮局部。
3、首先考慮抽象,再考慮具體。
如:
我們用面向?qū)ο蟮乃季S去給一棟建筑設(shè)計一座電梯。首先我們先抽象有這個電梯,然后這個電梯有高度、寬度、有門、有按鍵的屬性,它還會有開門、關(guān)門、上升、下降等行為。確定好這些后,那么再考慮開門的方法(行為),這門要在什么時候開,怎么開才安全,門在什么時候后關(guān),怎么關(guān)才安全,電梯什么時候上升、什么時候下降,怎么上升、怎么下降,要上升或下降到第幾層等這些行為(這時候相當(dāng)于定義好這個類)。定義好這些后,再根據(jù)這棟建筑去具體化這個電梯,例如這建筑有幾層樓,每層樓高度是多少等,這關(guān)乎電梯的上升、下降的高度等。(這個時候相當(dāng)于引用電梯這個類創(chuàng)建出這個對象,并初始化這個對象的一些參數(shù))。不同的建筑需要具體化合適這棟建筑的電梯。
第13集 類和對象
類是抽象的,對象是具體的。
類里面有狀態(tài)、行為。
類里面的變量就是記錄狀態(tài),而里面的函數(shù)則是行為(方法)。
舉個例子:
類相當(dāng)于是指明狗這種種類的一系列特征:身高、體重、毛色等一些屬性,還有怎么吠叫、怎么吃飯等一些行為。它是一個抽象的東西,就好像我們說到狗,腦子里面就會想起狗的這些特征,滿足這些特征才會被我們認(rèn)為是狗,但并不具體到是哪一種狗,哈巴狗?藏獒?還是流浪狗?它們都是狗。
而對象則是具體到哪一種狗,藏獒和哈巴狗叫的聲音不太一樣,跑的也不一樣,喜歡吃的食物也不同。
類和對象就好像我們需要一條狗(狗這種種類),這就選擇了狗這個類。至于是什么狗,要看自己的需求,假設(shè)我們要一條藏獒,那么它應(yīng)該是很彪悍的,發(fā)出的叫聲應(yīng)該是讓人很害怕的。那么這時候我們創(chuàng)建這個類的對象,設(shè)置這只狗的身高、體重、毛的顏色等一些屬性,然后我們要這只狗吠叫,就使用里面吠叫的方法。
定義類的方法:
Class 類名
{
屬性;—> 屬性也叫成員變量,用來描述類的狀態(tài),如上面狗的身高、體重、毛色等。
方法;—> 方法也叫成員方法,用來描述類的行為,如上面狗的吠叫,吃飯等。。。
}
生成對象的方法和流程。
例:Dog dog = new Dog();
Dog dog:系統(tǒng)會在棧內(nèi)存空間創(chuàng)建Dog的一個引用名,為dog。
new Dog():系統(tǒng)會在堆內(nèi)存空間創(chuàng)建Dog的一個對象。
=:賦值號就是把在堆內(nèi)存空間創(chuàng)建的Dog對象關(guān)聯(lián)到dog 這個引用名。
當(dāng)我們使用的時候就是需要用引用名去操作這個對象。
第14集:
下面這個例子很形象的表現(xiàn)出類與對象的關(guān)系。
class Dog // 定義狗這一類
{
String Zhonglei; // 狗的品種
String name; // 狗的名字
int age; // 狗的年齡
String color; // 狗的毛色
String Xingbie; // 性別
void jump()
{
System.out.Println(“跳啊跳啊。。。我拼命跳。。!);
}
}
class Test
{
Public static void main(String args[])
{
Dog Mydog = new Dog(); // 創(chuàng)建一只狗
Mydog.Zhonglei = “牧羊犬”; // 我喜歡這品種
Mydog.name = “小黑”; // 這狗就叫小黑吧。
Mydog.colcr = “黑色”; // 它的毛色是黑色的
Mydog.age = 1; // 它1歲了
Mydog.Xingbie = “雄性”; // 公的
Mydog.jump(); // 這只1歲的黑色小黑跳起來了。
Dog Mydog1= new Dog(); // 小黑太孤單了,給它創(chuàng)建個伴侶吧。
Mydog.Zhonglei = “牧羊犬”;
Mydog.name = “小白”; // 這狗就叫小白吧。
Mydog.colcr = “白色”; // 它的毛色是白色的
Mydog.age = 1; // 它也是1歲。
Mydog.Xingbie = “雌性”; // 母的
Mydog.jump(); // 這只1歲的白色小狗也開心跳起來了。
}
}
用著這種設(shè)計思想,感覺自己就像是上帝,哈哈。
Mydog 和 Mydog1 都是狗,但它們不是同一只狗,而是兩只不太一樣的狗。像上面的例子,還可以再創(chuàng)建一只藏獒(創(chuàng)建對象),藏獒和牧羊犬都是同一個物種,但卻不是同一個品種。很靈活。 Dog就是抽象的,而創(chuàng)建的對象則是具體的。就上面的例子來說,?臻g內(nèi)會存放兩個對象名,堆空間內(nèi)存放著兩個對象。
匿名對象:
如:new Dog().jump();
因為沒有給引用名,所以它是一次性的,不知道匿名對象算不算是創(chuàng)建了一只狗又扔了它。。。真狠心。。。。。
第15集:
函數(shù)重載:一個類中的有多個函數(shù)名一樣,但參數(shù)數(shù)量、類型不同的函數(shù)。系統(tǒng)會根據(jù)我們所傳遞的參數(shù)去判斷我們要調(diào)用的是哪一個函數(shù)。
構(gòu)造函數(shù):必須和類名相同,利用函數(shù)重載,可以有多個構(gòu)造函數(shù)。構(gòu)造函數(shù)會在new創(chuàng)建一個對象時自動被調(diào)用,調(diào)用哪個構(gòu)造函數(shù)取決自己new 類名(參數(shù))所傳遞的參數(shù),一般用于給成員變量賦初始值。如果沒有寫構(gòu)造函數(shù),編譯器會自動添加一個無參數(shù)、無函數(shù)體的空構(gòu)造函數(shù)。構(gòu)造函數(shù)是木有返回值滴。
第16集 (this的作用和使用方法)
this(參數(shù)):調(diào)用本類中構(gòu)造函數(shù),只能寫在函數(shù)中的第一句。
this.變量:在函數(shù)中訪問本類中的變量,一般區(qū)分函數(shù)中有和類中同名的變量。
this.函數(shù):調(diào)用本類中的函數(shù)。
第17集(關(guān)于靜態(tài) static)
靜態(tài)變量和靜態(tài)函數(shù)可以在不new(創(chuàng)建)對象的情況下直接調(diào)用。
例:
class Test
{
static int i ;
static void fun()
{
System.out.println(“static fun Run....”);
}
}
class main
{
public static void main(String args[])
{
Test.i = 10;
Test.fun();
}
}
被聲明為static的變量或函數(shù)都會在該類被裝載時會被創(chuàng)建在堆棧里。也就是說不需要我們new,內(nèi)存中已經(jīng)有它們的存在。而且它們都是被所有這類的對象所共享。
靜態(tài)變量或靜態(tài)函數(shù),之所以可以在未用new在堆空間里創(chuàng)建新的對象時,卻仍可以通過類名使用,是因為它在程序運行時已經(jīng)在堆內(nèi)存分配好空間,也就是說無論有沒有new,那空間都已經(jīng)分配好并且不會變動。
如視頻中的例子:在Person類里面的static int i; 在類第一次被裝載時 i 這個變量已經(jīng)在堆空間分配好并且不會變動,所以當(dāng)我們使用這個靜態(tài)變量時不需要重新new對象,即使new了對象,也不會再為它在堆空間重新分配內(nèi)存,無論我們new了多少個Person對象,它們都是共享同一個 i 空間。所以當(dāng)改動 i 的值時,自然而然的看起來是像影響到其他對象里面 的 i 值。
而 int i;這種非靜態(tài)變量則必須是在調(diào)用new時才會在堆空間分配好內(nèi)存。而且每次new的空間位置都不一樣,都是一份拷貝,每一份拷貝都關(guān)聯(lián)不同的Person對象,這樣就變成即使修改了第一個Person對象里的i,也不會影響第二個Person對象里面的i。
關(guān)于匿名靜態(tài)函數(shù):
匿名靜態(tài)函數(shù)沒有名字的函數(shù)。
static
{
System.out.println(“我是匿名靜態(tài)函數(shù)”);
}
匿名靜態(tài)函數(shù)在使用類的時候會被調(diào)用,而構(gòu)造函數(shù)則是在new時被調(diào)用。
1、Test.i = 10 或 Test.fun() 匿名靜態(tài)函數(shù)會被調(diào)用
2、Test t = new Test() 匿名靜態(tài)函數(shù)會被調(diào)用后,構(gòu)造函數(shù)才會被調(diào)用。
第18集 繼承
通過關(guān)鍵字 extends 來繼承某一類,被繼承的類為父類,繼承的類為子類。
子類擁有父類所有的成員變量和成員函數(shù),但不擁有構(gòu)造函數(shù)。
子類可以有自己的成員變量和函數(shù),感覺是在父類的基礎(chǔ)上是增加狀態(tài)和行為。是父類的一種擴(kuò)展。
例子:
class myzl extends Gog // myzi 是子類的名字,Gog是父類,是已經(jīng)存在的類
{
}
第19集super關(guān)鍵字
由于子類是不能繼承父類的構(gòu)造函數(shù),所以可以使用super這個關(guān)鍵字去調(diào)用父類的構(gòu)造函數(shù),當(dāng)然這個super不但可以調(diào)用父類的構(gòu)造函數(shù)還可以在父子類之間函數(shù)名、變量相同的情況下,在子類調(diào)用父類的函數(shù)或訪問父類的變量。super和this差不多,都是用于區(qū)分同名函數(shù)或變量。
super() :調(diào)用父類的無參構(gòu)造函數(shù)
super (參數(shù)):調(diào)用父類的有參構(gòu)造函數(shù)
super和this的異同:(網(wǎng)上資料)
1)super(參數(shù)):調(diào)用基類中的某一個構(gòu)造函數(shù)(應(yīng)該為構(gòu)造函數(shù)中的第一條語句)
2)this(參數(shù)): 調(diào)用本類中另一種形成的構(gòu)造函數(shù)(應(yīng)該為構(gòu)造函數(shù)中的第一條語句)
3)super:它引用當(dāng)前對象的直接父類中的成員(用來訪問直接父類中被隱藏的父類中成員 數(shù)據(jù)或函數(shù),基類與派生類中有相同成員定義時如:super.變量名,super.成員 函數(shù)據(jù)名(實參)
4)this:它代表當(dāng)前對象名(在程序中易產(chǎn)生二義性之處,應(yīng)使用this來指明當(dāng)前對象;
如果函數(shù)的形參與類中的成員數(shù)據(jù)同名,這時需用this來指明成員變量名)
5)調(diào)用super()必須寫在子類構(gòu)造方法的第一行,否則編譯不通過。每個子類構(gòu)造方法
的第一條語句,都是隱含地調(diào)用super(),如果父類沒有這種形式的構(gòu)造函數(shù),那么
在編譯的時候就會報錯。
6)super()和this()類似,區(qū)別是,super()從子類中調(diào)用父類的構(gòu)造方法,this()在同一類內(nèi)
調(diào)用其它方法。
7)super()和this()均需放在構(gòu)造方法內(nèi)第一行。
8)盡管可以用this調(diào)用一個構(gòu)造器,但卻不能調(diào)用兩個。
9)this和super不能同時出現(xiàn)在一個構(gòu)造函數(shù)里面,因為this必然會調(diào)用其它的構(gòu)造函數(shù),
其它的構(gòu)造函數(shù)必然也會有super語句的存在,所以在同一個構(gòu)造函數(shù)里面有相同
的語句,就失去了語句的意義,編譯器也不會通過。
10)this()和super()都指的是對象,所以,均不可以在static環(huán)境中使用。包括:static變
量,static方法,static語句塊。
11)從本質(zhì)上講,this是一個指向本對象的指針, 然而super是一個Java關(guān)鍵字。
第20集
函數(shù)的override(覆蓋、重寫)
需要滿足兩個條件:
1、在具有父子關(guān)系的兩個類當(dāng)中。
2、父類和子類都有一個同定義的函數(shù)(返回值類型、函數(shù)名和參數(shù)列表等完全相同)
當(dāng)父類中的某A函數(shù)完全無法滿足我們的要求時,我們可以在子類上重寫父類的A函數(shù),這個A函數(shù)會完全覆蓋父類的A函數(shù),當(dāng)執(zhí)行A函數(shù)時,父類的函數(shù)是不會被執(zhí)行的。
當(dāng)然,很少情況下會遇到父類A函數(shù)完全無法滿足我的要求,若是遇到還不如重新寫一個函數(shù)。當(dāng)然,如果是修改以及完成的源碼,因為后面的代碼已經(jīng)寫好調(diào)用了這個A函數(shù)名,那么這時候我們可以重寫A函數(shù),而不必要修改后續(xù)的代碼調(diào)用。
一般遇到的是父類的A函數(shù)能滿足一些基本的要求,但還是缺少一些功能,這時候我們可以先通過super.A() 來直接調(diào)用父類的代碼,然后下面再添加需要的一些功能,這樣當(dāng)子類的A函數(shù)被調(diào)用時,會執(zhí)行super.A(),而super.A()會是調(diào)用父類的A函數(shù),那么父類的A函數(shù)執(zhí)行完后就會執(zhí)行接下來我們寫的代碼了。
例:
class Person
{
String name;
int age;
void OutMsg()
{
System.out.println("父類 OutMsg");
System.out.println("我的名字是" + name + "我的年齡是" + age);
}
}
class Student extends Person
{
String address;
void OutMsg()
{
// 若這里是和父類的代碼一樣則可以換成 super.OutMsg();
// 這樣就可以直接調(diào)用父類的OutMsg函數(shù),避免重復(fù)代碼
System.out.println("子類 OutMsg");
System.out.println("名字是" + name + "年齡是" + age);
// 這是子類新增加的代碼
System.out.println("我的地址是" + address);
}
}
class Test
{
public static void main(String[] args)
{
Student s = new Student();
s.name = "張三楓";
s.age = 20;
s.address = "廣東";
s.OutMsg();
}
}
輸出:
D:\MyJava>javac *.java
D:\MyJava>java Test
子類 OutMsg
名字是張三楓年齡是20
我的地址是廣東
================================================ 華麗的分割線 ================================================
2013-12-28 日 更新
[color=#3306f9,strength=3)"]
================================================ 華麗的分割線 ================================================[color=#3306f9,strength=3)"]
第21集 對象的轉(zhuǎn)型
這一集特讓我糾結(jié)!作者說的不是很詳細(xì),而且給出的結(jié)論也跟我自己做實驗的不符。最最最重要的是,作者介紹了對象的轉(zhuǎn)型卻沒有舉個例子說明其有什么作用。讓我感覺這玩意是不是沒啥用的。。。
對象向上轉(zhuǎn)型:將子類的對象賦值給父類的引用。
如:Student(子類)繼承 Person(父類)
Student s = new Student(); // 創(chuàng)建一個子類對象 s指向這個對象
Person p = s; //通過子類的引用將Student對象賦值給父類的引用
此時s和p指向的是同一個對象。那么是否能把p當(dāng)成s去使用呢?看下面的例子。
class Person
{
String name;
int age;
String yiyang;
// 父類有的
void Person_fun()
{
System.out.println("父類:Person_fun執(zhí)行");
}
void OutMsg()
{
System.out.println("父類:OutMsgyiyang = "+yiyang);
System.out.println("父類:我的名字是" + name + "我的年齡是" + age);
}
}
class Studentextends Person
{
String address;
String yiyang; //和父類同名的
void Student_fun()
{
System.out.println("子類:Student_fun 執(zhí)行super.yiyang = " + super.yiyang);
}
void OutMsg()
{
super.OutMsg(); // 調(diào)用父類的OutMsg函數(shù)
System.out.println("子類:我的地址是address=" +address + " yiyang=" + yiyang); //這是子類新增加的代碼
}
}
class Test
{
publicstatic void main(String[] args)
{
System.out.println("---------------邪惡的分割線-------------");
System.out.println("以下是父類的對象操作。。。。");
System.out.println("------------------------------------------");
Person p1 = new Person();
p1.yiyang = "父類yiyang";
p1.name = " 父類";
p1.age = 21;
//p1.address = "廣州"; // 由于Person類沒有這個變量 當(dāng)然編譯報錯
p1.OutMsg(); //直接調(diào)用Person類的函數(shù)OutMsg();
//p1.Student_fun(); // 由于Person類沒有這個函數(shù) 當(dāng)然編譯報錯
p1.Person_fun(); //直接調(diào)用Person類的函數(shù)OutMsg();
System.out.println("\n\n---------------邪惡的分割線-------------");
System.out.println("以下是子類的對象操作。。。。");
System.out.println("------------------------------------------\n");
Student s1 = new Student();
s1.yiyang = "子類yiyang"; // 與父類同名變量,賦值的是子類的變量
s1.name = " 子類"; // 子類沒有。賦值的是父類的變量
s1.age = 22; //同上
s1.address = "深圳"; // 父類沒有,賦值的是子類的變量
s1.OutMsg(); //該函數(shù)被重寫(覆蓋)所以調(diào)用的是子類的函數(shù)
s1.Student_fun(); //子類獨有,調(diào)用的是子類的
s1.Person_fun(); // 子類沒有,調(diào)用的是父類的
System.out.println("\n\n---------------邪惡的分割線-------------");
System.out.println("以下是對象向上轉(zhuǎn)型操作。。。。");
System.out.println("------------------------------------------\n");
Student s = new Student();
Person p = s; // 對象的向上轉(zhuǎn)型
p.yiyang = "上轉(zhuǎn)—yiyang"; // 與子類同名,實際上賦值的是父類的成員變量
p.name = " 對象向上轉(zhuǎn)型"; //只有父類有這個成員變量 賦值的是父類的成員變量
p.age = 23; //賦值的是父類的成員變量 賦值的是父類的成員變量
//p.address = "廣東"; // 在父類沒有這個成員變量,編譯會報錯這行
p.OutMsg(); // 被復(fù)寫(覆蓋)的函數(shù),調(diào)用的是子類的OutMsg()
//p.Student_fun(); // 在父類沒有這個成員函數(shù),編譯會報錯這行
p.Person_fun(); //可以調(diào)用父類的成員函數(shù)
}
}
用java編譯輸出:
如果沒有紅色字體沒有被注釋則輸出:
D:\MyJava>javacTest.java
Test.java:14: 錯誤: 找不到符號
p1.address = "廣州"; // 由于Person類沒有這個變量 當(dāng)然編譯報錯
^
符號: 變量 address
位置: 類型為Person的變量 p1
Test.java:17: 錯誤: 找不到符號
p1.Student_fun(); // 由于Person類沒有這個函數(shù) 當(dāng)然編譯報錯
^
符號: 方法 Student_fun()
位置: 類型為Person的變量 p1
Test.java:49: 錯誤: 找不到符號
p.address = "廣東"; // 在父類沒有這個成員變量,編譯會報錯這
行
^
符號: 變量 address
位置: 類型為Person的變量 p
Test.java:52: 錯誤: 找不到符號
p.Student_fun(); // 在父類沒有這個成員函數(shù),編譯會報錯這
行
^
符號: 方法 Student_fun()
位置: 類型為Person的變量 p
4 個錯誤
如果注釋了紅色字體則成功被編譯。輸出的執(zhí)行結(jié)果是:
D:\MyJava>java Test
--------------- 邪惡的分割線 -------------
以下是父類的對象操作。。。。
------------------------------------------
父類:OutMsgyiyang = 父類yiyang // 這個就不用解釋了,非常普通的類操作
父類:我的名字是 父類我的年齡是21
父類:Person_fun 執(zhí)行
--------------- 邪惡的分割線 -------------
以下是子類的對象操作。。。。
------------------------------------------
父類:OutMsgyiyang = null // 這里的yiyang是父類的,沒給父類yiyang賦值自然是null
父類:我的名字是 子類我的年齡是22 //子類沒有同名變量,所以是給父類賦值
子類:我的地址是address=深圳 yiyang=子類yiyang
子類:Student_fun執(zhí)行super.yiyang = null //同名變量,賦值的是子類的,而不是父類的
父類:Person_fun 執(zhí)行
--------------- 邪惡的分割線 -------------
以下是對象向上轉(zhuǎn)型操作。。。。
------------------------------------------
父類:OutMsgyiyang = 上轉(zhuǎn)—yiyang
父類:我的名字是 對象向上轉(zhuǎn)型我的年齡是23
子類:我的地址是address=null yiyang=null //可以發(fā)現(xiàn)子類的變量并沒有被賦值
父類:Person_fun 執(zhí)行
我們可以總結(jié)一下,對象的向上轉(zhuǎn)型,在訪問變量和函數(shù)的時候,和直接操作父類對象相比,當(dāng)父類對象調(diào)用不存在的函數(shù)和變量時,編譯器直接報錯。向上轉(zhuǎn)型對象也是。唯一和直接操作父類對象不同的是,在調(diào)用被子類重寫(覆蓋)的函數(shù)時,向上轉(zhuǎn)型對象操作的是子類的函數(shù),而操作父類對象則是直接調(diào)用父類的函數(shù)。
下面是作者給出的結(jié)論:
一個引用能夠調(diào)用哪些成員(變量和函數(shù)),取決于這個引用的類型
一個引用調(diào)用的是哪一個方法,取決于這個引用所指向的對象
(讓我郁悶的是,函數(shù)和方法不是指一樣?xùn)|西么,很矛盾。而且第二個結(jié)論是有前提的,那就是那個方法(函數(shù))要被子類重寫過才成立.)
下面我們看看對象的向下轉(zhuǎn)型,由于視頻并沒有做過多介紹,所以唯有自己做實驗。
向下轉(zhuǎn)型 – 將父類的對象賦值給子類的引用
例:Student s1 =new Student();
Personp = s1;
Students2 = (Student)p;
或:Person p =new Student();
Students2 = (Student)p;
在Test.java文件中加入以下代碼:
class Test
{
publicstatic void main(String[] args)
{
//......
//這代碼和上面的一樣,就不寫出了。
//......
System.out.println("\n\n---------------邪惡的分割線-------------");
System.out.println("以下是對象向下轉(zhuǎn)型操作。。。。");
System.out.println("------------------------------------------\n");
Studentss = new Student();
Personpx = ss;
Studentsx = (Student)px;
sx.yiyang= "下轉(zhuǎn)-yiyang";
sx.name= "對象向下轉(zhuǎn)型";
sx.age= 25;
sx.address= "惠州";
sx.OutMsg();
sx.Student_fun();
sx.Person_fun();
}
}
看看輸出的結(jié)果:
發(fā)現(xiàn)向下轉(zhuǎn)型和直接使用子類沒啥區(qū)別。。。擦......