第三章 面向對象
3.1面向對象的概念
3.3.1理解面向對象
1.面向對象是相對面向過程而言
2.面向對象和麪向過程的區別
面向過程就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。
面向對象是把構成問題事務分解成各個對象,建立對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。
3.3.2面向對象的特點
1、是一種符合人們思考習慣的思想
2、可以將複雜的事情簡單化
3、將程序員從執行者轉換成了指揮者
4、完成需求時:
l先要去找具體所需的功能的對象來用
l如果該對象不存在,那麼創建一個具有所需功能的對象
l這樣簡化開發並提高了複用性
3.3.3面向對象開發,設計,特徵
1、開發的過程:其實就是不斷創建對象,使用對象,指揮對象做事情
2、設計的過程:其實就是在管理和維護對象之間的關係
3、面向對象的特徵:
封裝(encapsulation):封裝是面向對象的核心思想,將對象的屬性和行爲封裝起來,而它的載體是類,類通常對客戶隱藏細節。
繼承(inheritance):使用已存在的類的定義作爲建立新技術的基礎,新類的定義可以增加新的數據或功能,也可以使用父類的功能。
多態(polymorphism):動態綁定機制,用來決定會使用被重寫的方法名的那個方法的行爲。
3.2類與對象的關係
3.2.1類與對象的關係
1、對象——類的實例
它是實際存在的該類事物的個體。面向對象程序設計思想是以對象來思考問題,首先將現實世界的屍體抽離爲對象,然後考慮對象具備的行爲和屬性。
2、類:類是同一類事物的統稱,是構造對象時依賴的規範,
3.2.2類的定義
1、類定義的內容:
⑴、成員變量:也叫成員域,是對象的屬性
①、類變量:被該類全部共享的變量,也稱靜態成員域,常用於保存所有對象共有的常數值。
②、實例變量:也稱非靜態成員域
⑵、方法:也叫成員函數,是對象的行爲。
①、類方法:即使該類對象沒有對象存在時,也可以執行類方法。它也用static進行聲明,因此也被稱爲靜態方法
②、實例方法
3.2.3成員變量和局部變量的區別?
1.作用範圍:成員變量可以被public,protect,private,static等修飾符修飾,局部變量不能被控制修飾符及static修飾;兩者都可以定義成final型
2.是否有默認值:成員變量有,局部變量木有
3.生命週期:成員變量隨對象的建立而存在,隨其消失而消失;局部變量隨所屬區域的執行而產生,隨其釋放而釋放
4.存儲位置:成員變量存儲在堆,局部變量存儲在棧
3.2.4匿名對象
1、其實就是定義對象的簡寫格式:new 對象名();
2、使用場景:①、當對象方法僅進行一次調用時,就可以簡化成匿名對象
②、可以作爲實際參數進行傳遞
3.2.5參數傳遞——值調用
1、值調用:將實際參數的內容拷貝到被調用方法。
2、基本數據類型:創建時存儲於棧中。在調用時,會將將其copy拷貝到被調用方法的棧中。然後方法method根據傳過來的參數進行方法的執行。
3、引用數據類型:引用類型的引用存儲於棧中,而對象則是存儲與堆中。在調用時,會將對象的地址拷貝到被調用方法的棧中。然後方法method根據傳過來的參數進行方法的執行。
3.3 封裝
1、定義:隱藏對象的屬性和實現細節,對外提供公共的訪問方式
class Person{ //隱藏age這個屬性,一旦有屬性,幾乎都封裝, private int age; //提供對外訪問方式,是爲了可控 public void setAge(int a) { age = a; } public int getAge(){ return age; }
2、好處
n將變化隔離。
n便於使用
n提高重用性
n提高安全性
3 原則
l將不需要對外提供的內容都隱藏起來。
l把屬性都隱藏,提供對外訪問方式。
4、private關鍵字
n作用:利用private關鍵字,可以修飾成員,私有的內容只在本類中有效。
n封裝與私有的關係:私有是封裝的子集,私有僅僅是封裝的一種體現而已
n在java中最小的封裝體是函數。
3.4 構造函數
1、特點:⑴函數名與類名相同
⑵不定義返回值類型,沒有具體的返回值
2、作用:給對象進行初始化,創建對象都必須要通過構造函數初始化
3、默認構造函數:一個類中如果沒有定義過構造函數,那麼該類會有一個默認的空參構造函數,如果定義了,類中就沒有默認的啦。
4、與一般函數的區別:⑴構造函數:對象創建時,就會調用與之對應的構造函數,對對象進行初始化,一般函數:對象創建後,需要函數功能時才調用。
⑵構造函數在對象創建時會調用,而且只調用一次;一般函數在對象創建後,可調用多次。
5、重載:
什麼時候使用構造函數?在描述事物的時候,該事物一創建,就具備的一些內容,這些內容定義在構造函數中。
如果構造函數有多個,用於對不同對象進行針對性的初始化,多個函數在類中是以重載的形式來體現的。
6、注意:⑴、方法的名稱、形式參數的類型及其順序形成了方法的簽名,簽名是唯一
⑵、在構造函數前,不能加返回類型。
⑶、一般函數不可以調用構造函數,原因是構造函數是對對象初始化函數,不能被調用,非得調用就得用:new 構造函數();但這是廢話、
⑷、構造函數中也有return語句。
3.5 this關鍵字
1、使用場景
⑴、當定義類中功能時,該類函數內部要用到調用該函數的對象時,這是用this來表示這個對象,即本類功能內部用到本類對象的時候,用this。
①、區分同名的局部變量與成員變量,this代表本類的對象,到底代表哪一個?this代表它所在函數所屬對象的引用。簡單說:哪個對象在調用this所在的函數,this就代表哪個對象
②、在本類方法中使用對象,用this。
③、凡是被對象調用的方法,在內存中都有this所屬
⑵用於構造函數間的相互調用
2、注意:this只能放在構造函數中第一個,因爲初始化動作要先執行
3、代碼示例
class Person{ private int age; private String name; /*public Person(int age) { this.age = age; }*/ Person(String name){ this.name = name;//將局部的賦給對象的變量 } Person(String name, int age){ //Person(name);這是一般函數間的調用 this(name);//這是構造函數間的互相調用,在這裏,this(name)相當於p(name),也就是new Person(name); this.age = age; } public void shout(){ System.out.println("My age is "+age+"\nMy name is "+name); } /* * 需求:給人定義一個用於比較年齡是否相同的功能 * public boolean compare(Person p){ return this.age == p.age; }*/ } public class AboutThisKey { public static void main(String[] args) { /*Person p1 = new Person(20); Person p2 = new Person(25); boolean b = p1.compare(p2); System.out.println(b); */ Person p = new Person("lisi",30); // Person p1 = new Person("zhangsan"); p.shout(); // p1.shout(); //爲什麼p1不打印My age is 0 My name is lisi,而p不打印My age is 0 My name is zhangsan //在一個類裏面,成員之間互相調用時都是對象完成的,所以shout放完的是某一個對象中的name和age,即name和age之前省略了this } }
3.6 static關鍵字
1、共享數據:static用於修飾成員(成員函數,成員變量),這些成員都是被對象共享的成員。
2、特點
①、隨類的加載而加載,消失而消失。
②、優先於對象而存在
③、被所有對象所共享
④、可以直接被類名所調用
代碼示例
class Chinese
{
static String country = "中國";//共享數據
String name;
int age;
void singOurCountry(){
System.out.println("啊,親愛的"+country);
}
}
public class AboutStaticKey {
public static void main(String[] args) {
System.out.println(Chinese.country);
//當成員被靜態修飾後,除了可以被對象調用之外,還可以被類名調用
Chinese ch = new Chinese();
System.out.println("Chinese country is " + ch.country);
ch.singOurCountry();
}
}
3、實例變量與類變量的區別
①、存儲位置:前者隨對象的建立而存在於堆內存中;後者隨着類的加載而存在於方法區中
②、生命週期;前者隨着對象的消失而消失;後者隨着類的的消失而消失
③、調用方式:前者可以被對象及類名調用,但建議用類名調用;後者只能被對象調用
4、靜態的使用注意事項:
①、靜態的方法只能訪問靜態成員
②、靜態方法中不可以使用this或super關鍵字
③、靜態省略的是類名,成員變量省略的是this
④、主函數是靜態的
public static void main(String[] args)
1)、public:代表着該函數的權限最大
2)、static:代表主函數歲類的加載而加載就已經存在
3)、void:主函數沒有返回值
4)、main:不是關鍵字,是一個特殊的單詞,可以被jvm識別。
5)、String[] args:函數的參數,參數類型是一個數組,該數組中的元素師字符串,字符串類型的數組。
5 、靜態的利弊
①、利:對對象的共享數據進行單獨空間的存儲,節省空間,可以被類名直接調用。
②、弊:生命週期過長,而且訪問有侷限性
6、什麼時候使用靜態?
①、靜態變量:如果對象中出現共享數據時,該數據被靜態修飾,對象中的特有數據定義在堆內存中
②、靜態函數:函數是否用靜態修飾,就看該函數功能是否需要訪問對象中的特有數據,需要,該功能爲非靜態;不需要,就定義成靜態
7、靜態代碼塊
①、格式:static{
code;
}
②、特點:隨類的加載而執行,且只執行一次,並優先於主函數而執行
③、作用:給類進行初始化
8、面試題
class StaticCode { StaticCode(){ //因爲沒有創建過與之相關的對象,從而沒有被執行過,所以不打印b System.out.println("b"); } static{//靜態代碼塊給類初始化 System.out.println("a"); } {//構造代碼塊給對象初始化,其優先級比構造函數高,因爲構造函數有針對性,而構造代碼塊可以初始化所有對象 System.out.println("c"); } StaticCode(int x){//構造函數給對應的對象初始化 System.out.println("d"); } public static void show(){ System.out.println("show run"); } } class StaticCodeDemo { public static void main(String[] args) { new StaticCode(4); } } 問打印結果是?爲什麼? 結果:a c d,
3.7 單例設計模式
1、設計模式:解決一類問題最行之有效的方法
2、單例設計模式:保證內存中只有一個對象
3、原因:以配置文件爲例,
4、如何保證對象的唯一性?
1)、不允許其他程序用new建立該類對象
2)、爲了讓其他程序可以訪問到該類對象,在本類中,定義一個對象
3)、爲了讓其他程序對自定義對象可以訪問,可以對外提供一些訪問方式
5、以上3部怎麼用代碼體現?
1)、將構造函數私有化
2)、在類中創建一個本類對象
3)、提供一個方法可以獲得該對象
6、模式:
①餓漢式:定義單例時,建議使用餓漢式
class Singleton{ private int num; public int getNum() { return num; } public void setNum(int num) { this.num = num; } private Singleton(){} private static Singleton s = new Singleton(); public static Singleton getInstance(){ return s; } }
②懶漢式:對象的延時加載,懶漢式在面試的時候考的最多
class Singleton{ private int num; public int getNum() { return num; } public void setNum(int num) { this.num = num; } private Singleton(){} private static Singleton s = null; public static Singleton getInstance(){ if(s==null){ synchronized(Singleton.class){ if(s==null) s = new Singleton(); } } return s; } } public class SingletonPattern { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); s1.setNum(30); System.out.println(s2.getNum()); } }
3.8 繼承
3.8.1繼承的概述
1、繼承:Java繼承是使用已存在的類的定義作爲基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。
2、繼承的優點
①、提高了代碼的複用性
②、讓類與類之間產生了關係,有了這關係,纔有了多態的特性
注意:千萬別因爲要獲取其他類 的功能,簡化代碼而繼承,必須是類與類之間有所屬關係纔可以繼承。
3、父類,也稱爲基類,或超類
4、 聚集關係:關聯的一種形式,代表兩個類之間的整體 / 局部關係。聚集暗示着整體在概念上處於比局部更高的一個級別,而關聯暗示兩個類在概念上位於相同的級別。
有兩種特殊聚集:共享聚集和組合聚集。
l共享聚集:它的“部分”對象可以是多個任意“整體”對象的一部分
l組合聚集:整體擁有各部分,部分與整體共存。
3.8.2繼承的特點
1、java語言:只支持單繼承,不支持多繼承,因爲多繼承容易帶來安全隱患:當多個父類中定義相同功能,而功能的內容不同,子類不確定要運行哪一個但是java保留了這種機制,並以另一種體現形式來完成表示,多實現
2、java支持多層繼承,也就是一個繼承體系。如何使用一個繼承體系中的功能?想要使用體系,先查閱體系父類的描述,因爲父類定義了該體系的共性功能,通過了解共性功能,就可以知道該體系的基本功能。在具體調用時,要創建最子類對象,其原因是父類有可能創建不了對象,另外創建子類可以使用更多的功能。
3、子父類出現,類成員的特點:
⑴、變量:如果子父類中出現非私有的同名成員變量時,子類要訪問本類中的變量時用this,子類要訪問父類中的變量時用super。他們倆的用法幾乎一致,只是倆代表的引用不同
⑵、函數:當子類出現和父類一模一樣的函數時,子類對象調用該函數會運行子類函數的內容,如同父類的函數被覆蓋了一樣,也叫重寫或覆蓋。
當子類繼承了父類,沿襲了父類的功能,到子類中,但是子類雖具備該功能,但是功能的內容和父類不一致,這時,只需覆蓋父類功能,保留父類的功能定義,並重寫功能內容。
通過函數的複寫可以提高擴展性
覆蓋注意事項:
1)、子類的權限必須大於等於父類的權限,纔可以覆蓋。
2)、靜態只能覆蓋靜態
3)、重載只看同名函數的參數列表,而重寫的子父類方法必須一模一樣
⑶、構造函數:
子父類的構造函數是不可能一樣的,因爲構造函數的函數名與類名一致。在對子類對象進行初始化時,父類的構造函數也會運行。其原因是在子類構造函數默認的第一行有一條隱式的語句super(注意當第一行是this(),就沒有super()),super會訪問父類中空參數的構造函數。
爲什麼子類一定要訪問父類的構造函數?因爲父類中的數據子類可以直接獲取,所以子類對象在建立時,需要先查看父類是如何對這些數據進行初始化的的,所以子類在對象初始化時,要先訪問父類的構造函數。如果要訪問父類中指定構造函數,可以通過手動定義super語句來。
3.8.3 super關鍵字
1、應用
⑴、在子類的構造方法內部引用父類的構造方法,如果要引用super的話,必須把super放在函數的首位.
class Base { Base() { System.out.println("Base"); } } public class Checket extends Base { Checket() { super();//調用父類的構造方法,一定要放在方法的首個語句 System.out.println("Checket"); } public static void main(String argv[]) { Checket c = new Checket(); } }
⑵、在子類中調用父類中的成員方法或成員變量
class Country { String name; void value() { name = "China"; } } class City extends Country { String name; void value() { name = "Hufei"; super.value();//不調用此方法時,super.name返回的是父類的成員變量的值null System.out.println(name); System.out.println(super.name); } public static void main(String[] args) { City c=new City(); c.value(); } }
⑶、用super直接傳遞參數
class Fu{ int num; Fu(int num){ System.out.println("fu run"+" "+num); } void show(){ System.out.println("fu show"); } } class Zi extends Fu{ Zi(){ //super(); super(4);// 傳遞參數 System.out.println("zi run"); } void show(){ System.out.println("zi show"); } }
2、注意:
⑴、super語句必須放在子類構造函數的第一行
⑵、super和this都只能定義在第一行,所以只能定義一個。
⑶、通過super初始化父類內容時,子類的成員變量並未顯示初始化,等super()父類完畢後,才進行成員變量顯示初始化
3.8.4實例化過程
1、子類的實例化過程:
子類的所有的構造函數,默認都會訪問父類中的空參數的構造函數。因爲子類每一個構造函數的第一行都有一句隱式的super();當父類中沒有空參數的構造函數時,子類必須手動通過super語句形式來指定要訪問的父類中的構造函數。當然,子類的構造函數第一行也可以手動指定this語句來訪問本類中的構造函數,子類中至少會有一個構造函數會訪問父類中的構造函數
2、對象實例化過程——以Person p = new Person();爲例
lJvm會讀取指定路徑下的Person.class文件,並加載進內存,並會先加載Person的父類
l在堆內存中開闢空間,分配地址。
l在對象空間中,對對象中的屬性進行默認初始化
l調用對應的構造函數進行初始化
l在構造函數中,第一行會先到調用父類中構造函數進行初始化
l父類初始化完畢後,再對子類的屬性進行顯示初始化
l在進行子類構造函數的特定初始化
l完畢後,將地址值賦給引用變量
3.8.6 final關鍵字
1、final修飾的類不可以繼承
2、final修飾的方法不可以覆蓋
3、final修飾的變量是一個常量,只能賦值一次,且只固定顯示化賦值
4、注意:類變量只有在final的修飾下,值纔會固定,否則可以修改,static只能說明該值被共享,但不規定是固定的。