今日任務(wù)
1喘蟆、能夠理解單例設(shè)計模式
2缓升、能夠獨立使用繼承
3、能夠獨立使用final關(guān)鍵字
1. 代碼塊
1.1.代碼塊的概念和格式
代碼塊蕴轨,就是使用大括號括起來的一段代碼港谊;
書寫格式:
{
代碼;
}
1.2.代碼塊的分類
1.2.1. 靜態(tài)代碼塊
靜態(tài)代碼塊橙弱,就是使用static修飾的代碼塊歧寺,直接寫在類中的代碼塊;
class Demo{
static{
System.out.pintln("靜態(tài)代碼塊")
}
}
靜態(tài)代碼塊的執(zhí)行順序:
注意:一般在開發(fā)中棘脐,靜態(tài)代碼塊都要寫在類的下面斜筐;
作用:
因為在類加載完成時,靜態(tài)代碼塊已經(jīng)執(zhí)行結(jié)束蛀缝,所以某些需要提前完成的工作顷链,可以放在靜態(tài)代碼塊中執(zhí)行;
1.2.2. 構(gòu)造代碼塊
構(gòu)造代碼塊屈梁,也是直接寫在類中的代碼塊嗤练;在構(gòu)造代碼塊前面不需要任何的關(guān)鍵字修飾;
class Demo{
{
System.out.pintln("構(gòu)造代碼塊")
}
}
構(gòu)造代碼塊執(zhí)行順序:
應(yīng)用:
原來凡是創(chuàng)建對象執(zhí)行構(gòu)造函數(shù)在讶,都要執(zhí)行構(gòu)造代碼塊煞抬,所以如果某些操作在所有構(gòu)造函數(shù)中都存在,那么可以提取到構(gòu)造代碼塊中构哺;
1.2.3. 局部代碼塊
局部代碼塊的寫法和構(gòu)造代碼塊一模一樣革答;不同的時,局部代碼塊只能寫在局部位置(函數(shù)中);
class Demo{
public static void main(String[] args){
{
System.out.pintln("局部代碼塊")
}
}
}
局部代碼塊的作用残拐,就是用來限定部分代碼的使用范圍的途茫;
2. 類中可以書寫的成員
class Demo{
//靜態(tài)成員變量
static int a = 10;
//非靜態(tài)成員變量
int b =20;
//靜態(tài)方法
static void show(){
System.out.pintln("靜態(tài)方法")
}
//非靜態(tài)方法
void func(){
System.out.pintln("非靜態(tài)方法")
}
//構(gòu)造方法
Demo(){
System.out.pintln("構(gòu)造方法")
}
//靜態(tài)代碼塊
static {
System.out.pintln("靜態(tài)方法")
}
{
System.out.pintln("構(gòu)造代碼塊")
}
}
3. 類加載和對象創(chuàng)建的過程
面試題:
class Demo {
int x;
int y = 3;
static int z = 10;
static {
System.out.println("z=" + z);
}
Demo() {
System.out.println("x=" + x);
System.out.println("y=" + y);
}
}
class DemoTest2 {
public static void main(String[] args) {
Demo d = new Demo();
}
}
3.1.類加載過程
1、JVM發(fā)現(xiàn)要使用一個類蹦骑,首先要到方法區(qū)中找慈省;如果找到了臀防,就直接使用眠菇,如果沒有找到,才會去找這個類的class文件袱衷,然后加載捎废;
(在找class文件時,是根據(jù)classpath配置的地址去找致燥;如果沒有配置登疗,就在當(dāng)前目錄找)
2、在硬盤上找到class文件后嫌蚤,就開始加載這個class辐益,根據(jù)書寫順序,先后將靜態(tài)成員加載到靜態(tài)區(qū)域脱吱,非靜態(tài)成員加載到非靜態(tài)區(qū)域智政;
3、接下來為所有的靜態(tài)成員變量分配變量空間箱蝠,同時賦默認(rèn)值续捂;
4、接下來根據(jù)書寫的順序為靜態(tài)成員變量顯示賦值宦搬,同時執(zhí)行靜態(tài)代碼塊牙瓢;
上面的步驟都執(zhí)行完畢,類加載完成间校;
3.2.對象的創(chuàng)建過程
1矾克、首先JVM在堆內(nèi)存中開辟空間;
2憔足、在對象空間中為類中的所有非靜態(tài)成員變量分配空間胁附,賦默認(rèn)值;
3四瘫、調(diào)用相應(yīng)的構(gòu)造函數(shù)進(jìn)棧汉嗽;
4、在執(zhí)行構(gòu)造函數(shù)中的代碼之前找蜜,先要執(zhí)行隱式三步:
a) super():調(diào)用父類構(gòu)造函數(shù)
b) 給對象空間中的非靜態(tài)成員變量顯示賦值
c) 執(zhí)行構(gòu)造代碼塊
5饼暑、完成隱式三步后,接下來開始執(zhí)行構(gòu)造函數(shù)中的代碼;
構(gòu)造函數(shù)結(jié)束出棧弓叛,對象創(chuàng)建完成彰居;
3.3.練習(xí)
下列代碼執(zhí)行結(jié)果是什么?為什么撰筷?
代碼一:
class Demo
{
static Demo demo = new Demo();
Demo(){
System.out.println("構(gòu)造函數(shù)");
}
}
class Test
{
public static void main(String[] args)
{
new Demo();
}
}
代碼二:
class Demo
{
Demo demo = new Demo();
Demo(){
System.out.println("構(gòu)造函數(shù)");
}
}
class Test
{
public static void main(String[] args)
{
new Demo();
}
}
4. 單例設(shè)計模式
4.1.單例設(shè)計(singleton)模式介紹
4.1.1. 設(shè)計模式:
就是對一些常見問題進(jìn)行歸納總結(jié)陈惰,并針對具體問題給出一套通用的解決辦法(強(qiáng)調(diào)的是解決問題的思想);在開發(fā)中毕籽,只要遇到這類問題抬闯,就可以直接使用這些設(shè)計模式解決問題;
最早起源于建筑領(lǐng)域关筒,在建筑領(lǐng)域把一些問題和經(jīng)驗進(jìn)行歸納總結(jié)溶握,形成一套可以用來在建筑領(lǐng)域解決大多數(shù)問題的方案;
后來計算機(jī)領(lǐng)域借鑒了建筑領(lǐng)域的設(shè)計模式蒸播,把計算機(jī)領(lǐng)域中經(jīng)常遇到的問題進(jìn)行歸納和總結(jié)睡榆,形成計算機(jī)領(lǐng)域23中設(shè)計模式;
4.1.2. 單例(單態(tài)袍榆、原子)設(shè)計模式:
在程序運行中胀屿,一個類最多只能有一個對象;
//需求:模擬月亮包雀,不管哪里調(diào)用的月亮宿崭,都是同一個對象;
class Moon//描述月亮
{
/*
要創(chuàng)建對象馏艾,有兩個條件:new關(guān)鍵字劳曹;構(gòu)造函數(shù);
要保證單例琅摩,就不能讓別人隨便創(chuàng)建對象铁孵;
在這兩個條件中,new關(guān)鍵字程序員無法控制房资;
就只能考慮不讓別人使用構(gòu)造函數(shù)蜕劝;
要想別人不能隨意使用構(gòu)造函數(shù),就需要將構(gòu)造函數(shù)私有化轰异;
*/
private Moon(){}
/*
私有化構(gòu)造函數(shù)岖沛,確實可以避免隨意創(chuàng)建對象;
但是還是得有一個對象搭独;
而構(gòu)造函數(shù)私有化后在別的地方無法創(chuàng)建對象婴削,
就只有在本類中創(chuàng)建這個唯一的對象;
創(chuàng)建的對象需要有一個變量接收,以后其他地方需要這個對象牙肝,
通過這個變量就可以獲取這個對象了唉俗;
因為在使用這個變量之前還沒有對象嗤朴,所以這個變量必須是靜態(tài)的;
為了保證數(shù)據(jù)的安全虫溜,不被外界修改雹姊,必須將他封裝起來(也就是私有化)
*/
private static Moon moon = new Moon();
/*
要想外界還能訪問到被封裝的數(shù)據(jù),必須向外提供一個公開的訪問方法
而且只有訪問這個方法之后才會有對象衡楞,所以這個方法應(yīng)該是靜態(tài)的
*/
public static Moon getMoon(){
return moon;
}
}
class Test
{
public static void main(String[] args)
{
Moon m1 = Moon.getMoon();
Moon m2 = Moon.getMoon();
System.out.println(m1);
System.out.println(m2);
System.out.println(m1 == m2);
}
}
4.2.單例設(shè)計模式的代碼模板
總結(jié)實現(xiàn)單例的步驟:
1吱雏、私有化構(gòu)造函數(shù);
2瘾境、在本類中創(chuàng)建唯一實例對象歧杏;
3、對外提供公開的訪問方法寄雀,獲取這個對象
這種方式叫做餓漢式得滤;
這種實現(xiàn)方式有點問題:
這種方式陨献,只要使用到這個類盒犹,就一定會創(chuàng)建對象,會造成內(nèi)存的浪費眨业;
好處是:保證對象的唯一性急膀;
解決辦法:懶漢式
原理:
懶漢式的問題:
多線程環(huán)境下,不能保證每次獲取的都是同一個對象龄捡;
好處:避免內(nèi)存浪費;
4.3.單例設(shè)計總結(jié)
設(shè)計模式:針對某一類問題的通用的解決辦法聘殖;
單例設(shè)計模式:解決程序運行中一個類最多只能有一個實例對象的問題奸腺;
單例實現(xiàn)的步驟:
1餐禁、私有構(gòu)造函數(shù)突照,避免其他類可以直接創(chuàng)建單例類的對象;
2讹蘑、在本類中創(chuàng)建唯一實例末盔,使用靜態(tài)成員變量保存座慰;為保證安全性版仔,私有化這個成員變量心剥;
3优烧、對外提供一個公開的靜態(tài)方法链峭,供其他類獲取本類的唯一實例弊仪;
單例的兩種實現(xiàn)方法:
餓漢式:在加載類的同時就創(chuàng)建了這個類的唯一實例励饵;
好處:可保證這個類的實例的唯一性役听;
弊端:如果只是使用這個類,但是暫時不需要它的對象甜滨,也會創(chuàng)建唯一實例瘤袖,造成內(nèi)存的浪費捂敌;
懶漢式:在第一次調(diào)用獲取實例的方法時才創(chuàng)建對象占婉;
好處:第一次調(diào)用獲取實例的方法時才創(chuàng)建對象锐涯,可以避免內(nèi)存的浪費;
弊端:多線程環(huán)境下不能保證實例的唯一性霎终;
5. 面向?qū)ο螅豪^承
繼承財產(chǎn)莱褒;
繼承皇位广凸;
繼承傳統(tǒng)谅海;
繼承都是發(fā)生在兩類事物之間的扭吁,這兩類事物都有關(guān)系侥袜,現(xiàn)實生活中是父子關(guān)系枫吧;
5.1.java中的繼承
概念:
java中的繼承,是使用extends關(guān)鍵字在兩個類之間建立的一種關(guān)系颁湖;
寫法:
class Fu{}
class Zi extends Fu{}//表示Zi類繼承里Fu類爷狈;
在繼承關(guān)系中,被其他類繼承的類鹿响,叫做父類(超類)惶我,如本例中的Fu類绸贡;
繼承其他類的類听怕,叫做子類(派生類)尿瞭,如本例中的Zi類翅睛;
作用:
繼承中子類可以直接擁有父類的成員;
繼承演示:
案例:使用java代碼描述人和學(xué)生的信息很魂;
問題:
要解決這種應(yīng)該是自身所有的屬性和行為重復(fù)的問題,應(yīng)該使用繼承拉岁;
結(jié)論:使用繼承可以提高代碼的復(fù)用性喊暖;
使用繼承可以在兩個類中建立一種關(guān)系陵叽;
使用注意:
1巩掺、繼承中胖替,父類的私有成員可以被子類繼承独令,但是不能直接被訪問使用燃箭;
2招狸、繼承中的兩個類裙戏,應(yīng)該有關(guān)系挽懦;
只有子類描述的事物是 父類描述的事物的特例的時候信柿,才可以使用繼承;
雖然在語法上进鸠,可以使用extends關(guān)鍵字在任意兩個類之間建立繼承關(guān)系客年,但是在開發(fā)中量瓜,只能是二者之間具有“是” 的關(guān)系的時候才使用繼承绍傲;
如果兩個類不具有這種“是”的關(guān)系烫饼,那么就應(yīng)該找他們共同的父類杠纵,然后將共同的信息放到共同的父類中比藻,然后讓兩個類分別繼承父類韩容;
魚和 蘋果 , 不具有 “是” 的關(guān)系哄辣,但是有共同的父類力穗,都屬于食物当窗,所以可以建立一個食物類崖面,然后讓他們分別繼承食物類巫员;
5.2.java類的繼承特點
5.2.1. 單一繼承
就是一個類只能直接繼承一個父類简识;
如果可以繼承兩個父類七扰,那么當(dāng)這兩個父類中都具有共同的屬性或行為時颈走,在子類中調(diào)用疫鹊,就不清楚到底會調(diào)用哪個(調(diào)用的不確定性)
5.2.2. 多重繼承
java中繼承中拆吆,父類可以再繼承其他類枣耀,叫做多重繼承捞奕;
一個類只能直接繼承一個父類颅围,但是可以有多個子類院促;
一個類的父類還可以繼承父類常拓;
5.3.繼承中的成員變量的特點
5.3.1. 子類直接擁有父類非私有成員變量
5.3.2. 子類中存在和父類中同名的成員變量茎辐,在子類中直接使用的是子類中定義的拖陆;
一般開發(fā)中丐黄,如果父類定義了某個成員變量灌闺,子類中一般不需要再定義桂对;
5.4.繼承中的成員方法
5.4.1. 子類直接擁有父類非私有成員方法
5.4.2. 子類中可以定義和父類中同樣的成員方法蕉斜,直接調(diào)用的是子類中定義的函數(shù)
結(jié)論:
如果子類中沒有定義和父類中一樣的成員變量和函數(shù),直接調(diào)用父腕,使用的是父類中定義的成員璧亮;
如果子類中定義了和父類中一樣的成員變量和函數(shù)枝嘶,直接調(diào)用群扶,使用都是子類中定義的成員穷当;
此時要使用父中定義的成員,需要通過super關(guān)鍵字調(diào)用铃岔;調(diào)用的格式是:
super.成員變量毁习;
super.成員函數(shù)名(參數(shù))纺且;
5.5.方法的重寫(override)
5.5.1. 重寫的概念
在子類中定義和父類中相同的函數(shù)载碌,直接調(diào)用函數(shù)時嫁艇,實際使用的是子類中的函數(shù),這種情況叫做方法的重寫(覆寫)退敦;
一般開發(fā)中霍掺,如果父類的功能不滿足子類需要粒督,子類都會重寫父類的函數(shù)坠陈;
5.5.2. 重寫的應(yīng)用
需求:描述手機(jī)這類事物仇矾。原始的手機(jī)和現(xiàn)代的手機(jī)
5.5.3. 重寫的注意事項
1、子類重寫的函數(shù)要和父類中的函數(shù)名要相同;
2敦锌、子類重寫的函數(shù)要和父類中的函數(shù)參數(shù)列表要相同乙墙;
3听想、子類重寫的函數(shù)要和父類中的函數(shù)返回值類型相同衔峰;
4垫卤、子類重寫的函數(shù)要和父類中的函數(shù)的訪問方式相同
(也就是說葫男,父類的方法是靜態(tài)的梢褐,重寫的方法也必須是靜態(tài)的盈咳;父類的方法不是靜態(tài)的,重寫的方法也必須不是靜態(tài)的)丈积;
5江滨、子類重寫的函數(shù)唬滑,訪問權(quán)限不能比父類的低;(可以和父類的訪問權(quán)限不同稻艰,但是不能比父類訪問權(quán)限低)
直接將父類中的函數(shù)復(fù)制粘貼到子類中连锯,然后修改方法體的代碼运怖,可以保證不會出現(xiàn)格式上的問題摇展;
5.6.繼承中構(gòu)造方法的使用
5.6.1. 子類實例化的過程
繼承中類的加載順序,是先加載父類祟滴,然后再加載子類垄懂;
1、為什么任何一個類(不包含Object)的構(gòu)造函數(shù)中都需要一個super() 語句漫谷?
因為除了Object類以外舔示,所有類都會繼承一個父類惕稻;繼承父類缩宜,那么子類實例化時就需要給父類中的成員變量顯示賦值锻煌,就需要用到父類中的構(gòu)造函數(shù)宋梧;
2狰挡、如果父類中沒有無參構(gòu)造函數(shù)捂龄,子類如何實例化释涛?
super()表示調(diào)用父類無參構(gòu)造函數(shù);如果父類中沒有無參構(gòu)造函數(shù)倦沧,就會報錯唇撬;
如何解決這個問題呢?
1窖认、在父類中添加一個無參構(gòu)造函數(shù);
2告希、在子類的構(gòu)造函數(shù)中顯示的調(diào)用父類有參構(gòu)造函數(shù)扑浸;
在子類中調(diào)用父類的構(gòu)造函數(shù),需要使用super關(guān)鍵字燕偶;格式是:super(參數(shù));
在子類構(gòu)造函數(shù)中使用super調(diào)用父類構(gòu)造函數(shù)需要注意喝噪,這個super語句必須寫在構(gòu)造函數(shù)的第一行;
3指么、子類構(gòu)造函數(shù)中酝惧,this() 和 super() 能否同時存在?
不能涧尿;因為他們都要寫在構(gòu)造函數(shù)的第一行系奉;
所以如果一個構(gòu)造函數(shù)中有this()語句,就沒有super()語句,super()存在于this調(diào)用的那個構(gòu)造函數(shù)里面姑廉;
4缺亮、如果一個類的構(gòu)造函數(shù)全部私有了,還可以有子類嗎桥言?
不能萌踱;因為在子類的構(gòu)造函數(shù)中一定要調(diào)用父類的構(gòu)造函數(shù);而一旦一個類的構(gòu)造函數(shù)都私有了号阿,就只能在本類中使用并鸵,其他類(也包括子類)都無法使用;
5.7.繼承總結(jié)
繼承的概念:通過extends關(guān)鍵字在兩個類之間建立的一種關(guān)系扔涧;其中繼承其他類的類叫做子類(派生類)园担,被其他類繼承的類叫做父類(超類);
繼承關(guān)系枯夜,一般用來表示子類和父類的 是的關(guān)系弯汰,即子類描述的事物是 父類描述的事物的一個特例;
繼承的格式:
class Fu{}//父類
class Zi **extends **Fu{}//子類
繼承的作用:子類可以直接擁有父類成員湖雹;其中咏闪,私有成員和構(gòu)造函數(shù)不參與繼承;
java中類繼承的特點:只支持單一繼承和多重繼承摔吏,不支持多繼承(一個類不能同時繼承多個類)
繼承中成員變量的特點:
子類中可以直接使用父類中定義的非私有的成員變量鸽嫂;
如果子類中定義了和父類中相同的成員變量纵装,直接調(diào)用,實際使用的是子類中定義的成員變量据某;要使用父類中定義的成員變量橡娄,需要使用關(guān)鍵字super,格式是:super.變量名哗脖;
繼承中一般函數(shù)的特點:
子類中可以直接使用父類中定義的非私有的一般函數(shù)瀑踢;
如果子類中定義了和父類中一樣的函數(shù),直接調(diào)用才避,實際使用的是子類定義的函數(shù);要使用父類中定義的一般函數(shù)氨距,需要使用關(guān)鍵字super桑逝,格式是:super.函數(shù)名(參數(shù));
方法重寫的概念:在繼承中,如果子類中定義了和父類中一樣的函數(shù)俏让,則子類對象實際使用的是子類中定義的函數(shù)楞遏,這種情況叫做函數(shù)的重寫;
子類重寫父類函數(shù)需要注意的事項:
1首昔、子類中重寫的函數(shù)寡喝,函數(shù)名、參數(shù)列表勒奇、返回值類型和是否靜態(tài)预鬓,必須和父類中函數(shù)相等;
2赊颠、子類中重寫的函數(shù)格二,訪問權(quán)限不能比父類中函數(shù)低;
繼承中子類實例化的特點:
1竣蹦、子類實例化時顶猜,實際只創(chuàng)建子類一個對象;
2痘括、子類對象中會為父類中的非靜態(tài)成員變量分配空間长窄;
3、在執(zhí)行子類的構(gòu)造函數(shù)時纲菌,必須要先調(diào)用父類的構(gòu)造函數(shù)挠日,作用是給父類的成員變量顯示賦值;
4驰后、子類調(diào)用父類的構(gòu)造函數(shù)肆资,需要使用super關(guān)鍵字,格式是:super(參數(shù));并且super語句必須在子類構(gòu)造函數(shù)的第一行灶芝;
5郑原、子類構(gòu)造函數(shù)中調(diào)用其他構(gòu)造函數(shù)的this語句不能和調(diào)用父類構(gòu)造函數(shù)的super語句共存唉韭;
super小結(jié):super,表示父類犯犁;作用是區(qū)分子類和父類的成員属愤,以及在子類的構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù);
6. final關(guān)鍵字
6.1.final簡介
final:表示最終的酸役,最后的住诸,主要用來修飾類、函數(shù)和變量涣澡;
final修飾類贱呐,直接寫在class關(guān)鍵自前面,表示這個類不能被繼承入桂;
final修飾函數(shù)奄薇,直接寫在函數(shù)的返回值類型前面,表示這個函數(shù)不能被重寫抗愁,但是可以被繼承馁蒂;
final修飾變量,表示這個變量的值不能被修改蜘腌;
6.2.final演示
6.2.1. 修飾類
6.2.2. 修飾函數(shù)
6.2.3. 修飾變量
因為被final修飾的變量的值不可改變沫屡,所以java中都使用它表示常量;
如果是常量撮珠,變量名的寫法是:所有字母全部大寫沮脖,多個單詞之間使用下劃線連接;
final int USER_AGE = 10;
被修飾的變量只是直接保存在變量中的值不能被修改劫瞳,所以如果修飾的變量的數(shù)據(jù)類型是引用數(shù)據(jù)類型倘潜,那么這個引用不能修改,但是引用指向的對象中的數(shù)據(jù)可以修改志于;
6.3.final總結(jié)
final:最終的涮因,最后的;可以修飾類伺绽、變量和函數(shù)养泡;
修飾類,表示該類不可被繼承奈应;格式是直接寫在class關(guān)鍵字前面澜掩;
修飾函數(shù),表示繼承這個類的子類中不能重寫這個函數(shù)杖挣;格式是直接寫在函數(shù)的返回值類型前面
修飾變量肩榕,表示該變量的值不可改變;格式是直接寫在變量的數(shù)據(jù)類型前面惩妇;
注意:如果修飾的是引用類型的變量株汉,則變量中保存的引用不可改變筐乳,但是引用指向的堆內(nèi)存中的數(shù)據(jù)可以改變;