網(wǎng)站建設(shè)技術(shù)部獎懲制度快速建站網(wǎng)站
學(xué)習(xí)面向?qū)ο髢?nèi)容的三條主線
- Java類及類的成員:(重點)屬性、方法、構(gòu)造器;(熟悉)代碼塊、內(nèi)部類
- 面向?qū)ο蟮奶卣?#xff1a;封裝、繼承、多態(tài)、(抽象)
- 其他關(guān)鍵字的使用:this、super、package、import、static、final、interface、abstract等
1. 面向?qū)ο缶幊谈攀?/h2>
1.1 程序設(shè)計的思路
面向?qū)ο?#xff0c;是軟件開發(fā)中的一類編程風(fēng)格、開發(fā)范式。除了面向?qū)ο?/code>,還有
面向過程
、指令式編程
和函數(shù)式編程
。在所有的編程范式中,我們接觸最多的還是面向過程和面向?qū)ο髢煞N。
類比:史書類型
- 紀(jì)傳體:以人物傳記為中心,“本紀(jì)”敘述帝王,“世家”記敘王侯封國和特殊人物,“列傳”記敘民間人物。
- 編年體:按年、月、日順序編寫。
- 國別體:是一部分國記事的歷史散文,分載多國歷史。
早期先有面向過程思想,隨著軟件規(guī)模的擴大,問題復(fù)雜性的提高,面向過程的弊端
越來越明顯,出現(xiàn)了面向?qū)ο笏枷氩⒊蔀槟壳爸髁鞯姆绞健?/p>
1. 面向過程的程序設(shè)計思想(Process-Oriented Programming),簡稱POP
- 關(guān)注的焦點是
過程
:過程就是操作數(shù)據(jù)的步驟。如果某個過程的實現(xiàn)代碼重復(fù)出現(xiàn),那么就可以把這個過程抽取為一個函數(shù)
。這樣就可以大大簡化冗余代碼,便于維護(hù)。 - 典型的語言:C語言
- 代碼結(jié)構(gòu):以
函數(shù)
為組織單位。 - 是一種“
執(zhí)行者思維
”,適合解決簡單問題。擴展能力差、后期維護(hù)難度較大。
2. 面向?qū)ο蟮某绦蛟O(shè)計思想( Object Oriented Programming),簡稱OOP
- 關(guān)注的焦點是
類
:在計算機程序設(shè)計過程中,參照現(xiàn)實中事物,將事物的屬性特征、行為特征抽象出來,用類來表示。 - 典型的語言:Java、C#、C++、Python、Ruby和PHP等
- 代碼結(jié)構(gòu):以
類
為組織單位。每種事物都具備自己的屬性
和行為/功能
。 - 是一種“
設(shè)計者思維
”,適合解決復(fù)雜問題。代碼擴展性強、可維護(hù)性高。
1.2 由實際問題考慮如何設(shè)計程序
思考1:如何開車?
面向過程思想思考問題時,我們首先思考“怎么按步驟實現(xiàn)?
”并將步驟對應(yīng)成方法,一步一步,最終完成。 這個適合簡單任務(wù)
,不需要過多協(xié)作
的情況。針對如何開車,可以列出步驟:
面向過程適合簡單、不需要協(xié)作的事務(wù),重點關(guān)注如何執(zhí)行。
思考2:如何造車?
造車太復(fù)雜,需要很多協(xié)作
才能完成。此時我們思考的是“車怎么設(shè)計?
”,而不是“怎么按特定步驟造車的問題”。這就是思維方式的轉(zhuǎn)變,前者就是面向?qū)ο笏枷?。所?#xff0c;面向?qū)ο?Oriented-Object)思想更契合人的思維模式。
用面向?qū)ο笏枷胨伎肌叭绾卧O(shè)計車”:
自然地,我們就會從“車由什么組成”開始思考。發(fā)現(xiàn),車由如下結(jié)構(gòu)組成:
我們找輪胎廠完成制造輪胎的步驟,發(fā)動機廠完成制造發(fā)動機的步驟,…;這樣,大家可以同時進(jìn)行車的制造,最終進(jìn)行組裝,大大提高了效率。但是,具體到輪胎廠的一個流水線操作,仍然是有步驟的,還是離不開面向過程思維!
因此,面向?qū)ο罂梢詭椭覀儚暮暧^上把握、從整體上分析整個系統(tǒng)。 但是,具體到實現(xiàn)部分的微觀操作(就是一個個方法),仍然需要面向過程的思路去處理。
注意: 我們千萬不要把面向過程和面向?qū)ο髮α⑵饋?。他們是相輔相成的。面向?qū)ο箅x不開面向過程!
類比舉例1:
當(dāng)需求單一,或者簡單時,我們一步步去操作沒問題,并且效率也挺高。
可隨著需求的更改,功能的增多,發(fā)現(xiàn)需要面對每一個步驟很麻煩了,這時就開始思索,**能不能把這些步驟和功能進(jìn)行封裝,封裝時根據(jù)不同的功能,進(jìn)行不同的封裝,功能類似的封裝在一起。**這樣結(jié)構(gòu)就清晰了很多。用的時候,找到對應(yīng)的類就可以了。這就是面向?qū)ο蟮乃枷搿?/p>
類比舉例2:人把大象裝進(jìn)冰箱
- 面向過程
1.打開冰箱2.把大象裝進(jìn)冰箱3.把冰箱門關(guān)住
- 面向?qū)ο?/li>
人{打開(冰箱){冰箱.開門(); }操作(大象){大象.進(jìn)入(冰箱);}關(guān)閉(冰箱){ 冰箱.關(guān)門(); }
}冰箱{開門(){ } 關(guān)門(){ }
}大象{進(jìn)入(冰箱){ }
}
2. Java語言的基本元素:類和對象
2.1 引入
人認(rèn)識世界,其實就是面向?qū)ο蟮摹1热?#xff0c;我們認(rèn)識一下美人魚(都沒見過)
經(jīng)過“仔細(xì)學(xué)習(xí)”,發(fā)現(xiàn)美人魚通常具備一些特征:
- 女孩
- 有魚尾
- 美麗
這個總結(jié)的過程,其實是抽象化
的過程。抽象出來的美人魚的特征,可以歸納為一個美人魚類
。而圖片中的都是這個類呈現(xiàn)出來的具體的對象
。
2.2 類和對象概述
類(Class)
和對象(Object)
是面向?qū)ο蟮暮诵母拍睢?br /> 1、什么是類
類:具有相同特征的事物的抽象描述,是抽象的
、概念上的定義。
2、什么是對象
對象:實際存在的該類事物的每個個體
,是具體的
,因而也稱為實例(instance)
。
可以理解為:類 => 抽象概念的人
;對象 => 實實在在的某個人
3、類與對象的關(guān)系錯誤理解
曰:“白馬非馬,可乎?”
曰:“可?!?曰:“何哉?”
曰:“馬者,所以命形也。白者,所以命色也。命色者,非命形也,故曰白馬非馬?!?
2.3 類的成員概述
面向?qū)ο蟪绦蛟O(shè)計的重點是
類的設(shè)計
類的設(shè)計,其實就是
類的成員的設(shè)計
- 現(xiàn)實世界的生物體,大到鯨魚,小到螞蟻,都是由最基本的
細(xì)胞
構(gòu)成的。同理,Java代碼世界是由諸多個不同功能的類
構(gòu)成的。
-
現(xiàn)實生物世界中的細(xì)胞又是由什么構(gòu)成的呢?細(xì)胞核、細(xì)胞質(zhì)、…
Java中用類class來描述事物也是如此。類,是一組相關(guān)屬性
和行為
的集合,這也是類最基本的兩個成員。- 屬性:該類事物的狀態(tài)信息。對應(yīng)類中的
成員變量
- 成員變量 <=> 屬性 <=> Field
- 行為:該類事物要做什么操作,或者基于事物的狀態(tài)能做什么。對應(yīng)類中的
成員方法
- (成員)方法 <=> 函數(shù) <=> Method
- 屬性:該類事物的狀態(tài)信息。對應(yīng)類中的
-
舉例:
2.4 面向?qū)ο笸瓿晒δ艿娜襟E(重要)
步驟1:類的定義
類的定義使用關(guān)鍵字:class。格式如下:
[修飾符] class 類名{屬性聲明;方法聲明;
}
舉例1:
public class Person{//聲明屬性ageint age ; //聲明方法showAge()public void eat() { System.out.println("人吃飯");}
}
舉例2:
public class Dog{//聲明屬性String type; //種類String nickName; //昵稱String hostName; //主人名稱//聲明方法public void eat(){ //吃東西System.out.println("狗狗進(jìn)食"); }
}
public class Person{String name;char gender;Dog dog;//喂寵物public void feed(){dog.eat();}
}
步驟2:對象的創(chuàng)建
- 創(chuàng)建對象,使用關(guān)鍵字:new
- 創(chuàng)建對象語法:
//方式1:給創(chuàng)建的對象命名
//把創(chuàng)建的對象用一個引用數(shù)據(jù)類型的變量保存起來,這樣就可以反復(fù)使用這個對象了
類名 對象名 = new 類名();//方式2:
new 類名()//也稱為匿名對象
- 舉例:
class PersonTest{public static void main(String[] args){//創(chuàng)建Person類的對象Person per = new Person();//創(chuàng)建Dog類的對象Dog dog = new Dog();}
}
步驟3:對象調(diào)用屬性或方法
-
對象是類的一個實例,必然具備該類事物的屬性和行為(即方法)。
-
使用"
對象名.屬性
" 或 "對象名.方法
"的方式訪問對象成員(包括屬性和方法)
舉例1:
//聲明Animal類
public class Animal { //動物類public int legs;public void eat() {System.out.println("Eating.");}public void move() {System.out.println("Move.");}
}
//聲明測試類
public class AnimalTest {public static void main(String args[]) {//創(chuàng)建對象Animal xb = new Animal();xb.legs = 4;//訪問屬性System.out.println(xb.legs);xb.eat();//訪問方法xb.move();//訪問方法}
}
圖示理解:
舉例2:針對前面步驟1的舉例2:類的實例化(創(chuàng)建類的對象)
public class Game{public static void main(String[] args){Person p = new Person();//通過Person對象調(diào)用屬性p.name = "康師傅";p.gender = '男';p.dog = new Dog(); //給Person對象的dog屬性賦值//給Person對象的dog屬性的type、nickname屬性賦值p.dog.type = "柯基犬";p.dog.nickName = "小白";//通過Person對象調(diào)用方法p.feed();}
}
2.5 匿名對象 (anonymous object)
- 我們也可以不定義對象的句柄,而直接調(diào)用這個對象的方法。這樣的對象叫做匿名對象。
- 如:new Person().shout();
- 使用情況
- 如果一個對象只需要進(jìn)行一次方法調(diào)用,那么就可以使用匿名對象。
- 我們經(jīng)常將匿名對象作為實參傳遞給一個方法調(diào)用。
3. 對象的內(nèi)存解析
3.1 JVM內(nèi)存結(jié)構(gòu)劃分
HotSpot Java虛擬機的架構(gòu)圖如下。其中我們主要關(guān)心的是運行時數(shù)據(jù)區(qū)部分(Runtime Data Area)。
其中:
堆(Heap)
:此內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存。這一點在Java虛擬機規(guī)范中的描述是:所有的對象實例以及數(shù)組都要在堆上分配。
棧(Stack)
:是指虛擬機棧。虛擬機棧用于存儲局部變量等。局部變量表存放了編譯期可知長度的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,是對象在堆內(nèi)存的首地址)。 方法執(zhí)行完,自動釋放。
方法區(qū)(Method Area)
:用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。
3.2 對象內(nèi)存解析
舉例:
class Person { //類:人String name;int age;boolean isMale;
}public class PersonTest { //測試類public static void main(String[] args) {Person p1 = new Person();p1.name = "趙同學(xué)";p1.age = 20;p1.isMale = true;Person p2 = new Person();p2.age = 10;Person p3 = p1;p3.name = "郭同學(xué)";}
}
內(nèi)存解析圖:
說明:
- 堆:凡是new出來的結(jié)構(gòu)(對象、數(shù)組)都放在堆空間中。
- 對象的屬性存放在堆空間中。
- 創(chuàng)建一個類的多個對象(比如p1、p2),則每個對象都擁有當(dāng)前類的一套"副本"(即屬性)。當(dāng)通過一個對象修改其屬性時,不會影響其它對象此屬性的值。
- 當(dāng)聲明一個新的變量使用現(xiàn)有的對象進(jìn)行賦值時(比如p3 = p1),此時并沒有在堆空間中創(chuàng)建新的對象。而是兩個變量共同指向了堆空間中同一個對象。當(dāng)通過一個對象修改屬性時,會影響另外一個對象對此屬性的調(diào)用。
面試題:對象名中存儲的是什么呢?
答:對象地址
public class StudentTest{public static void main(String[] args){System.out.println(new Student());//Student@7852e922Student stu = new Student();System.out.println(stu);//Student@4e25154fint[] arr = new int[5];System.out.println(arr);//[I@70dea4e}
}
直接打印對象名和數(shù)組名都是顯示“類型@對象的hashCode值",所以說類、數(shù)組都是引用數(shù)據(jù)類型,引用數(shù)據(jù)類型的變量中存儲的是對象的地址,或者說指向堆中對象的首地址。
4. 類的成員之一:成員變量(field)
4.1 如何聲明成員變量
- 語法格式:
[修飾符1] class 類名{[修飾符2] 數(shù)據(jù)類型 成員變量名 [= 初始化值];
}
- 說明:
- 位置要求:必須在類中,方法外
- 修飾符2(暫不考慮)
- 常用的權(quán)限修飾符有:private、缺省、protected、public
- 其他修飾符:static、final
- 數(shù)據(jù)類型
- 任何基本數(shù)據(jù)類型(如int、Boolean) 或 任何引用數(shù)據(jù)類型。
- 成員變量名
- 屬于標(biāo)識符,符合命名規(guī)則和規(guī)范即可。
- 初始化值
- 根據(jù)情況,可以顯式賦值;也可以不賦值,使用默認(rèn)值
示例:
public class Person{private int age; //聲明private變量 agepublic String name = “Lila”; //聲明public變量 name
}
4.2 成員變量 vs 局部變量
1、變量的分類:成員變量與局部變量
- 在方法體外,類體內(nèi)聲明的變量稱為成員變量。
- 在方法體內(nèi)部等位置聲明的變量稱為局部變量。
其中,static可以將成員變量分為兩大類,靜態(tài)變量和非靜態(tài)變量。其中靜態(tài)變量又稱為類變量,非靜態(tài)變量又稱為實例變量或者屬性。接下來先學(xué)習(xí)實例變量。
2、成員變量 與 局部變量 的對比
-
相同點
- 變量聲明的格式相同: 數(shù)據(jù)類型 變量名 = 初始化值
-
變量必須先聲明、后初始化、再使用。
- 變量都有其對應(yīng)的作用域。只在其作用域內(nèi)是有效的
-
不同點
1、聲明位置和方式
(1)實例變量:在類中方法外
(2)局部變量:在方法體{}中或方法的形參列表、代碼塊中
2、在內(nèi)存中存儲的位置不同
(1)實例變量:堆
(2)局部變量:棧
3、生命周期
(1)實例變量:和對象的生命周期一樣,隨著對象的創(chuàng)建而存在,隨著對象被GC回收而消亡,
而且每一個對象的實例變量是獨立的。
(2)局部變量:和方法調(diào)用的生命周期一樣,每一次方法被調(diào)用而在存在,隨著方法執(zhí)行的結(jié)束而消亡,
而且每一次方法調(diào)用都是獨立。
4、作用域
(1)實例變量:通過對象就可以使用,本類中直接調(diào)用,其他類中“對象.實例變量”
(2)局部變量:出了作用域就不能使用
5、修飾符(后面來講)
(1)實例變量:public,protected,private,final,volatile,transient等
(2)局部變量:final
6、默認(rèn)值
(1)實例變量:有默認(rèn)值
(2)局部變量:沒有,必須手動初始化。其中的形參比較特殊,靠實參給它初始化。
3、對象屬性的默認(rèn)初始化賦值
當(dāng)一個對象被創(chuàng)建時,會對其中各種類型的成員變量自動進(jìn)行初始化賦值。
4、舉例
class Person {//人類//1.屬性String name;//姓名int age = 1;//年齡boolean isMale;//是否是男性public void show(String nation) {//nation:局部變量String color;//color:局部變量color = "yellow";}
}//測試類
class PersonTest {public static void main(String[] args) {Person p = new Person();p.show("CHN");}
}
5. 類的成員之二:方法(method)
5.1 方法的引入
《街霸》游戲中,每次人物出拳、出腳或跳躍等動作都需要編寫50-80行的代碼,在每次出拳、出腳或跳躍的地方都需要重復(fù)地編寫這50-80行代碼,這樣程序會變得很臃腫
,可讀性也非常差。為了解決代碼重復(fù)編寫的問題,可以將出拳、出腳或跳躍的代碼提取出來放在一個{}中,并為這段代碼起個名字,這樣在每次的出拳、出腳或跳躍的地方通過這個名字來調(diào)用這個{}的代碼就可以了。
上述過程中,所提取出來的代碼可以被看作是程序中定義的一個方法,程序在需要出拳、出腳或跳躍時調(diào)用該方法即可。
5.2 方法(method、函數(shù))的理解
-
方法
是類或?qū)ο笮袨樘卣鞯某橄?#xff0c;用來完成某個功能操作。在某些語言中也稱為函數(shù)
或過程
。 -
將功能封裝為方法的目的是,可以
實現(xiàn)代碼重用,減少冗余,簡化代碼
-
Java里的方法
不能獨立存在
,所有的方法必須定義在類里。 -
舉例1:
- Math.random()的random()方法
- Math.sqrt(x)的sqrt(x)方法
- System.out.println(x)的println(x)方法
- new Scanner(System.in).nextInt()的nextInt()方法
- Arrays類中的binarySearch()方法、sort()方法、equals()方法
-
舉例2:
public class Person{private int age;public int getAge() { //聲明方法getAge()return age; }public void setAge(int i) { //聲明方法setAgeage = i; //將參數(shù)i的值賦給類的成員變量age} }
5.3 如何聲明方法
1、聲明方法的語法格式
[修飾符] 返回值類型 方法名([形參列表])[throws 異常列表]{方法體的功能代碼
}
(1)一個完整的方法 = 方法頭 + 方法體。
- 方法頭就是
[修飾符] 返回值類型 方法名([形參列表])[throws 異常列表]
,也稱為方法簽名
。通常調(diào)用方法時只需要關(guān)注方法頭就可以,從方法頭可以看出這個方法的功能和調(diào)用格式。 - 方法體就是方法被調(diào)用后要執(zhí)行的代碼。對于調(diào)用者來說,不了解方法體如何實現(xiàn)的,并不影響方法的使用。
(2)方法頭可能包含5個部分
-
修飾符:可選的。方法的修飾符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面會一一學(xué)習(xí)。
- 其中,權(quán)限修飾符有public、protected、private。在講封裝性之前,我們先默認(rèn)使用pulbic修飾方法。
- 其中,根據(jù)是否有static,可以將方法分為靜態(tài)方法和非靜態(tài)方法。其中靜態(tài)方法又稱為類方法,非靜態(tài)方法又稱為實例方法。咱們在講static前先學(xué)習(xí)實例方法。
-
返回值類型: 表示方法運行的結(jié)果的數(shù)據(jù)類型,方法執(zhí)行后將結(jié)果返回到調(diào)用者。
- 無返回值,則聲明:void
- 有返回值,則聲明出返回值類型(可以是任意類型)。與方法體中“
return 返回值
”搭配使用
-
方法名:屬于標(biāo)識符,命名時遵循標(biāo)識符命名規(guī)則和規(guī)范,“見名知意”
-
形參列表:表示完成方法體功能時需要外部提供的數(shù)據(jù)列表。可以包含零個,一個或多個參數(shù)。
- 無論是否有參數(shù),()不能省略
- 如果有參數(shù),每一個參數(shù)都要指定數(shù)據(jù)類型和參數(shù)名,多個參數(shù)之間使用逗號分隔,例如:
- 一個參數(shù): (數(shù)據(jù)類型 參數(shù)名)
- 二個參數(shù): (數(shù)據(jù)類型1 參數(shù)1, 數(shù)據(jù)類型2 參數(shù)2)
- 參數(shù)的類型可以是基本數(shù)據(jù)類型、引用數(shù)據(jù)類型
-
throws 異常列表:可選,在【異常處理】章節(jié)再講
(3)方法體:方法體必須有{}括起來,在{}中編寫完成方法功能的代碼
(4)關(guān)于方法體中return語句的說明:
-
return語句的作用是結(jié)束方法的執(zhí)行,并將方法的結(jié)果返回去
-
如果返回值類型不是void,方法體中必須保證一定有 return 返回值; 語句,并且要求該返回值結(jié)果的類型與聲明的返回值類型一致或兼容。
-
如果返回值類型為void時,方法體中可以沒有return語句,如果要用return語句提前結(jié)束方法的執(zhí)行,那么return后面不能跟返回值,直接寫return ; 就可以。
-
return語句后面就不能再寫其他代碼了,否則會報錯:Unreachable code
補充:方法的分類:按照是否有形參及返回值
2、類比舉例
、代碼示例:**
/*** 方法定義案例演示*/
public class MethodDefineDemo {/*** 無參無返回值方法的演示*/public void sayHello(){System.out.println("hello");}/*** 有參無返回值方法的演示* @param length int 第一個參數(shù),表示矩形的長* @param width int 第二個參數(shù),表示矩形的寬* @param sign char 第三個參數(shù),表示填充矩形圖形的符號*/public void printRectangle(int length, int width, char sign){for (int i = 1; i <= length ; i++) {for(int j=1; j <= width; j++){System.out.print(sign);}System.out.println();}}/*** 無參有返回值方法的演示* @return*/public int getIntBetweenOneToHundred(){return (int)(Math.random()*100+1);}/*** 有參有返回值方法的演示* @param a int 第一個參數(shù),要比較大小的整數(shù)之一* @param b int 第二個參數(shù),要比較大小的整數(shù)之二* @return int 比較大小的兩個整數(shù)中較大者的值*/public int max(int a, int b){return a > b ? a : b;}
}
5.4 如何調(diào)用實例方法
方法通過方法名被調(diào)用,且只有被調(diào)用才會執(zhí)行。
1、方法調(diào)用語法格式
對象.方法名([實參列表])
2、示例
舉例1:
/*** 方法調(diào)用案例演示*/
public class MethodInvokeDemo {public static void main(String[] args) {//創(chuàng)建對象MethodDefineDemo md = new MethodDefineDemo();System.out.println("-----------------------方法調(diào)用演示-------------------------");//調(diào)用MethodDefineDemo類中無參無返回值的方法sayHellomd.sayHello();md.sayHello();md.sayHello();//調(diào)用一次,執(zhí)行一次,不調(diào)用不執(zhí)行System.out.println("------------------------------------------------");//調(diào)用MethodDefineDemo類中有參無返回值的方法printRectanglemd.printRectangle(5,10,'@');System.out.println("------------------------------------------------");//調(diào)用MethodDefineDemo類中無參有返回值的方法getIntBetweenOneToHundredmd.getIntBetweenOneToHundred();//語法沒問題,就是結(jié)果丟失int num = md.getIntBetweenOneToHundred();System.out.println("num = " + num);System.out.println(md.getIntBetweenOneToHundred());//上面的代碼調(diào)用了getIntBetweenOneToHundred三次,這個方法執(zhí)行了三次System.out.println("------------------------------------------------");//調(diào)用MethodDefineDemo類中有參有返回值的方法maxmd.max(3,6);//語法沒問題,就是結(jié)果丟失int bigger = md.max(5,6);System.out.println("bigger = " + bigger);System.out.println("8,3中較大者是:" + md.max(8,9));}
}
舉例2:
//1、創(chuàng)建Scanner的對象
Scanner input = new Scanner(System.in);//System.in默認(rèn)代表鍵盤輸入//2、提示輸入xx
System.out.print("請輸入一個整數(shù):"); //對象.非靜態(tài)方法(實參列表)//3、接收輸入內(nèi)容
int num = input.nextInt(); //對象.非靜態(tài)方法()
5.5 使用的注意點
(1)必須先聲明后使用,且方法必須定義在類的內(nèi)部
(2)調(diào)用一次就執(zhí)行一次,不調(diào)用不執(zhí)行。
(3)方法中可以調(diào)用類中的方法或?qū)傩?#xff0c;不可以在方法內(nèi)部定義方法。
正確示例:
類{方法1(){}方法2(){}
}
錯誤示例:
類{方法1(){方法2(){ //位置錯誤}}
}
5.6 關(guān)鍵字return的使用
- return在方法中的作用:
- 作用1:結(jié)束一個方法
- 作用2:結(jié)束一個方法的同時,可以返回數(shù)據(jù)給方法的調(diào)用者
- 注意點:在return關(guān)鍵字的直接后面不能聲明執(zhí)行語句
5.7 方法調(diào)用內(nèi)存分析
-
方法
沒有被調(diào)用
的時候,都在方法區(qū)
中的字節(jié)碼文件(.class)中存儲。 -
方法
被調(diào)用
的時候,需要進(jìn)入到棧內(nèi)存
中運行。方法每調(diào)用一次就會在棧中有一個入棧
動作,即給當(dāng)前方法開辟一塊獨立的內(nèi)存區(qū)域,用于存儲當(dāng)前方法的局部變量的值。 -
當(dāng)方法執(zhí)行結(jié)束后,會釋放該內(nèi)存,稱為
出棧
,如果方法有返回值,就會把結(jié)果返回調(diào)用處,如果沒有返回值,就直接結(jié)束,回到調(diào)用處繼續(xù)執(zhí)行下一條指令。 -
棧結(jié)構(gòu):先進(jìn)后出,后進(jìn)先出。
舉例分析:
public class Person {public static void main(String[] args) {Person p1 = new Person();p1.eat();}public static void eat() {sleep();System.out.println("人:吃飯");}public static void sleep(){System.out.println("人:睡覺");doSport();}public static void doSport(){System.out.println("人:運動");}
}
內(nèi)存分析:
6. 對象數(shù)組
數(shù)組的元素可以是基本數(shù)據(jù)類型,也可以是引用數(shù)據(jù)類型。當(dāng)元素是引用類型中的類時,我們稱為對象數(shù)組。
1、案例
定義類Student,包含三個屬性:學(xué)號number(int),年級state(int),成績score(int)。 創(chuàng)建20個學(xué)生對象,學(xué)號為1到20,年級和成績都由隨機數(shù)確定。
問題一:打印出3年級(state值為3)的學(xué)生信息。
問題二:使用冒泡排序按學(xué)生成績排序,并遍歷所有學(xué)生信息
提示:
-
生成隨機數(shù):Math.random(),返回值類型double;
-
四舍五入取整:Math.round(double d),返回值類型long。
/** 定義類Student,包含三個屬性:學(xué)號number(int),年級state(int),成績score(int)。*/
public class Student {int number;//學(xué)號int state;//年級int score;//成績public void info(){System.out.println("number : " + number + ",state : " + state + ",score : " + score);}}
public class StudentTest {public static void main(String[] args) {// Student s1 = new Student();// s1.number = 1;// s1.state = (int)(Math.random() * 6 + 1);//[1,6]// s1.score = (int)(Math.random() * 101);//[0,100]//// Student s2 = new Student();// s2.number = 2;// s2.state = (int)(Math.random() * 6 + 1);//[1,6]// s2.score = (int)(Math.random() * 101);//[0,100]//// //....// 對象數(shù)組// String[] arr = new String[10];// 數(shù)組的創(chuàng)建Student[] students = new Student[20];// 通過循環(huán)結(jié)構(gòu)給數(shù)組的屬性賦值for (int i = 0; i < students.length; i++) {// 數(shù)組元素的賦值students[i] = new Student();// 數(shù)組元素是一個對象,給對象的各個屬性賦值students[i].number = (i + 1);students[i].state = (int) (Math.random() * 6 + 1);// [1,6]students[i].score = (int) (Math.random() * 101);// [0,100]}// 問題一:打印出3年級(state值為3)的學(xué)生信息。for (int i = 0; i < students.length; i++) {if (students[i].state == 3) {
// System.out.println(
// "number:" + students[i].number + ",state:" + students[i].state + ",score:" + students[i].score);students[i].info();}}System.out.println("******************************");// 問題二:使用冒泡排序按學(xué)生成績排序,并遍歷所有學(xué)生信息// 排序前for (int i = 0; i < students.length; i++) {
// System.out.println(
// "number:" + students[i].number + ",state:" +
// students[i].state + ",score:" + students[i].score);students[i].info();}System.out.println();// 排序:for (int i = 0; i < students.length - 1; i++) {for (int j = 0; j < students.length - 1 - i; j++) {if (students[j].score > students[j + 1].score) {Student temp = students[j];students[j] = students[j + 1];students[j + 1] = temp;}}}// 排序后:for (int i = 0; i < students.length; i++) {
// System.out.println(
// "number:" + students[i].number + ",state:" +
// students[i].state + ",score:" + students[i].score);students[i].info();}}}
內(nèi)存解析:
2、注意點
對象數(shù)組,首先要創(chuàng)建數(shù)組對象本身,即確定數(shù)組的長度,然后再創(chuàng)建每一個元素對象,如果不創(chuàng)建,數(shù)組的元素的默認(rèn)值就是null
,所以很容易出現(xiàn)空指針異常NullPointerException
。
3、練習(xí)
(1)定義矩形類Rectangle,包含長、寬屬性,area()返回矩形面積的方法,perimeter()返回矩形周長的方法,String getInfo()返回圓對象的詳細(xì)信息(如:長、寬、面積、周長等數(shù)據(jù))的方法
(2)在測試類中創(chuàng)建長度為3的Rectangle[]數(shù)組,用來裝3個矩形對象,并給3個矩形對象的長分別賦值為10,20,30,寬分別賦值為5,15,25,遍歷輸出
public class Rectangle {double length;double width;public double area(){//面積return length * width;}public double perimeter(){//周長return 2 * (length + width);}public String getInfo(){return "長:" + length +",寬:" + width +",面積:" + area() +",周長:" + perimeter();}
}
public class ObjectArrayTest {public static void main(String[] args) {//聲明并創(chuàng)建一個長度為3的矩形對象數(shù)組Rectangle[] array = new Rectangle[3];//創(chuàng)建3個矩形對象,并為對象的實例變量賦值,//3個矩形對象的長分別是10,20,30//3個矩形對象的寬分別是5,15,25//調(diào)用矩形對象的getInfo()返回對象信息后輸出for (int i = 0; i < array.length; i++) {//創(chuàng)建矩形對象array[i] = new Rectangle();//為矩形對象的成員變量賦值array[i].length = (i+1) * 10;array[i].width = (2*i+1) * 5;//獲取并輸出對象對象的信息System.out.println(array[i].getInfo());}}
}
內(nèi)存解析:
7. 再談方法
7.1 方法的重載(overload)
7.1.1 概念及特點
- 方法重載:在同一個類中,允許存在一個以上的同名方法,只要它們的參數(shù)列表不同即可。
- 參數(shù)列表不同,意味著參數(shù)個數(shù)或參數(shù)類型的不同
- 重載的特點:與修飾符、返回值類型無關(guān),只看參數(shù)列表,且參數(shù)列表必須不同。(參數(shù)個數(shù)或參數(shù)類型)。調(diào)用時,根據(jù)方法參數(shù)列表的不同來區(qū)別。
- 重載方法調(diào)用:JVM通過方法的參數(shù)列表,調(diào)用匹配的方法。
- 先找個數(shù)、類型最匹配的
- 再找個數(shù)和類型可以兼容的,如果同時多個方法可以兼容將會報錯
7.1.2 示例
舉例1:
//System.out.println()方法就是典型的重載方法,其內(nèi)部的聲明形式如下:
public class PrintStream {public void println(byte x)public void println(short x)public void println(int x)public void println(long x)public void println(float x)public void println(double x)public void println(char x)public void println(double x)public void println()}public class HelloWorld{public static void main(String[] args) {System.out.println(3);System.out.println(1.2f);System.out.println("hello!");}
}
? 舉例2:
//返回兩個整數(shù)的和
public int add(int x,int y){return x+y;
}//返回三個整數(shù)的和
public int add(int x,int y,int z){return x+y+z;
}
//返回兩個小數(shù)的和
public double add(double x,double y){return x+y;
}
? 舉例3:方法的重載和返回值類型無關(guān)
public class MathTools {//以下方法不是重載,會報錯public int getOneToHundred(){return (int)(Math.random()*100);}public double getOneToHundred(){return Math.random()*100;}
}
7.1.3 練習(xí)
**練習(xí)1:**判 斷與void show(int a,char b,double c){}
構(gòu)成重載的有:
a)void show(int x,char y,double z){} // nob)int show(int a,double c,char b){} // yesc) void show(int a,double c,char b){} // yesd) boolean show(int c,char b){} // yese) void show(double c){} // yesf) double show(int x,char y,double z){} // nog) void shows(){double c} // no
練習(xí)2:編寫程序,定義三個重載方法并調(diào)用。
-
方法名為mOL。
-
三個方法分別接收一個int參數(shù)、兩個int參數(shù)、一個字符串參數(shù)。分別執(zhí)行平方運算并輸出結(jié)果,相乘并輸出結(jié)果,輸出字符串信息。
-
在主類的main ()方法中分別用參數(shù)區(qū)別調(diào)用三個方法。
練習(xí)3:定義三個重載方法max(),第一個方法求兩個int值中的最大值,第二個方法求兩個double值中的最大值,第三個方法求三個double值中的最大值,并分別調(diào)用三個方法。
7.2 可變個數(shù)的形參
在**JDK 5.0 中提供了Varargs(variable number of arguments)**機制。即當(dāng)定義一個方法時,形參的類型可以確定,但是形參的個數(shù)不確定,那么可以考慮使用可變個數(shù)的形參。
格式:
方法名(參數(shù)的類型名 ...參數(shù)名)
舉例:
//JDK 5.0以前:采用數(shù)組形參來定義方法,傳入多個同一類型變量
public static void test(int a ,String[] books);//JDK5.0:采用可變個數(shù)形參來定義方法,傳入多個同一類型變量
public static void test(int a ,String...books);
特點:
-
可變參數(shù):方法參數(shù)部分指定類型的參數(shù)個數(shù)是可變多個:0個,1個或多個
-
可變個數(shù)形參的方法與同名的方法之間,彼此構(gòu)成重載
-
可變參數(shù)方法的使用與方法參數(shù)部分使用數(shù)組是一致的,二者不能同時聲明,否則報錯。
-
方法的參數(shù)部分有可變形參,需要放在形參聲明的最后
-
在一個方法的形參中,最多只能聲明一個可變個數(shù)的形參
案例分析:
案例1:n個字符串進(jìn)行拼接,每一個字符串之間使用某字符進(jìn)行分割,如果沒有傳入字符串,那么返回空字符串""
public class StringTools {String concat(char seperator, String... args){String str = "";for (int i = 0; i < args.length; i++) {if(i==0){str += args[i];}else{str += seperator + args[i];}}return str;}
}
public class StringToolsTest {public static void main(String[] args) {StringTools tools = new StringTools();System.out.println(tools.concat('-'));System.out.println(tools.concat('-',"hello"));System.out.println(tools.concat('-',"hello","world"));System.out.println(tools.concat('-',"hello","world","java"));}
}
案例2:求n個整數(shù)的和
public class NumberTools {public int total(int[] nums){int sum = 0;for (int i = 0; i < nums.length; i++) {sum += nums[i];}return sum;}public int sum(int... nums){int sum = 0;for (int i = 0; i < nums.length; i++) {sum += nums[i];}return sum;}
}
public class TestVarParam {public static void main(String[] args) {NumberTools tools = new NumberTools();System.out.println(tools.sum());//0個實參System.out.println(tools.sum(5));//1個實參System.out.println(tools.sum(5,6,2,4));//4個實參System.out.println(tools.sum(new int[]{5,6,2,4}));//傳入數(shù)組實參System.out.println("------------------------------------");System.out.println(tools.total(new int[]{}));//0個元素的數(shù)組System.out.println(tools.total(new int[]{5}));//1個元素的數(shù)組System.out.println(tools.total(new int[]{5,6,2,4}));//傳入數(shù)組實參}
}
案例3:如下的方法彼此構(gòu)成重載
public class MathTools {//求兩個整數(shù)的最大值public int max(int a,int b){return a>b?a:b;}//求兩個小數(shù)的最大值public double max(double a, double b){return a>b?a:b;}//求三個整數(shù)的最大值public int max(int a, int b, int c){return max(max(a,b),c);}//求n個整數(shù)的最大值public int max(int... nums){int max = nums[0];//如果沒有傳入整數(shù),或者傳入null,這句代碼會報異常for (int i = 1; i < nums.length; i++) {if(nums[i] > max){max = nums[i];}}return max;}/* //求n整數(shù)的最大值public int max(int[] nums){ //編譯就報錯,與(int... nums)無法區(qū)分int max = nums[0];//如果沒有傳入整數(shù),或者傳入null,這句代碼會報異常for (int i = 1; i < nums.length; i++) {if(nums[i] > max){max = nums[i];}}return max;}*//* //求n整數(shù)的最大值public int max(int first, int... nums){ //當(dāng)前類不報錯,但是調(diào)用時會引起多個方法同時匹配int max = first;for (int i = 0; i < nums.length; i++) {if(nums[i] > max){max = nums[i];}}return max;}*/
}
7.3 方法的參數(shù)傳遞機制
7.3.1 形參和實參
- 形參(formal parameter):在定義方法時,方法名后面括號()中聲明的變量稱為形式參數(shù),簡稱形參。
- 實參(actual parameter):在調(diào)用方法時,方法名后面括號()中的使用的值/變量/表達(dá)式稱為實際參數(shù),簡稱實參。
7.3.2 參數(shù)傳遞機制:值傳遞
Java里方法的參數(shù)傳遞方式只有一種:值傳遞
。 即將實際參數(shù)值的副本(復(fù)制品)傳入方法內(nèi),而參數(shù)本身不受影響。
-
形參是基本數(shù)據(jù)類型:將實參基本數(shù)據(jù)類型變量的“數(shù)據(jù)值”傳遞給形參
-
形參是引用數(shù)據(jù)類型:將實參引用數(shù)據(jù)類型變量的“地址值”傳遞給形參
7.3.3 舉例
1、形參是基本數(shù)據(jù)類型
案例:編寫方法,交換兩個整型變量的值
public class Test {public static void main(String[] args) {int m = 10;int n = 20;System.out.println("m = " + m + ", n = " + n);//交換m和n的值
// int temp = m;
// m = n;
// n = temp;ValueTransferTest1 test = new ValueTransferTest1();test.swap(m, n);System.out.println("m = " + m + ", n = " + n);}public void swap(int m,int n){int temp = m;m = n;n = temp;}}
內(nèi)存解析:
2、形參是引用數(shù)據(jù)類型
public class Test {public static void main(String[] args) {Data d1 = new Data();d1.m = 10;d1.n = 20;System.out.println("m = " + d1.m + ", n = " + d1.n);//實現(xiàn) 換序ValueTransferTest2 test = new ValueTransferTest2();test.swap(d1);System.out.println("m = " + d1.m + ", n = " + d1.n);}public void swap(Data data){int temp = data.m;data.m = data.n;data.n = temp;}
}
class Data{int m;int n;
}
內(nèi)存解析:
7.3.4 練習(xí)
練習(xí)1:判斷如下程序輸出的結(jié)果
public class AssignNewObject {public void swap(MyData my){my = new MyData(); //考慮堆空間此新創(chuàng)建的對象,和main中的data對象是否有關(guān)int temp = my.x;my.x = my.y;my.y = temp;}public static void main(String[] args) {AssignNewObject tools = new AssignNewObject();MyData data = new MyData();data.x = 1;data.y = 2;System.out.println("交換之前:x = " + data.x +",y = " + data.y);//tools.swap(data);//調(diào)用完之后,x與y的值交換?System.out.println("交換之后:x = " + data.x +",y = " + data.y);//}
}class MyData{int x ;int y;
}
練習(xí)2:如下操作是否可以實現(xiàn)數(shù)組排序
public class ArrayTypeParam {//冒泡排序,實現(xiàn)數(shù)組從小到大排序public void sort(int[] arr){for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if(arr[j] > arr[j+1]){int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}}//打印數(shù)組的元素public void print(int[] arr){for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]+" ");}System.out.println();}public static void main(String[] args) {ArrayTypeParam tools = new ArrayTypeParam();int[] nums = {4,3,1,6,7};System.out.println("排序之前:");tools.print(nums);tools.sort(nums);//對nums數(shù)組進(jìn)行排序System.out.println("排序之后:");tools.print(nums);//輸出nums數(shù)組的元素}
}
練習(xí)3:通過內(nèi)存結(jié)構(gòu)圖,寫出如下程序的輸出結(jié)果
//棧:每個方法在調(diào)用時,都會有以棧幀的方法壓入棧中。棧幀中保存了當(dāng)前方法中聲明的變量:方法內(nèi)聲明的,形參
//堆:存放new出來的"東西":對象(成員變量在對象中)、數(shù)組實體(數(shù)組元素)。
//注意:變量前如果聲明有類型,那么這就是一個新的剛要定義的變量。如果變量前沒有聲明類型,那就說明此變量在之前已經(jīng)聲明過。
public class TransferTest3 {public static void main(String args[]) {TransferTest3 test = new TransferTest3();test.first();}public void first() {int i = 5;Value v = new Value();v.i = 25;second(v, i);System.out.println(v.i);}public void second(Value v, int i) {i = 0;v.i = 20;Value val = new Value();v = val;System.out.println(v.i + " " + i);}
}class Value {int i = 15;
}
內(nèi)存解析:
練習(xí)4:貌似是
考查方法的參數(shù)傳遞
//法一:public static void method(int a, int b) {// 在不改變原本題目的前提下,如何寫這個函數(shù)才能在main函數(shù)中輸出a=100,b=200? a = a * 10;b = b * 20;System.out.println(a);System.out.println(b);System.exit(0);}//法二:public static void method(int a, int b) {PrintStream ps = new PrintStream(System.out) {@Overridepublic void println(String x) {if ("a=10".equals(x)) {x = "a=100";} else if ("b=10".equals(x)) {x = "b=200";}super.println(x);}};System.setOut(ps);}
練習(xí)5:將對象作為參數(shù)傳遞給方法
(1)定義一個Circle類,包含一個double型的radius屬性代表圓的半徑,一個findArea()方法返回圓的面積。
(2)定義一個類PassObject,在類中定義一個方法printAreas(),該方法的定義如下:public void printAreas(Circle c, int time),在printAreas方法中打印輸出1到time之間的每個整數(shù)半徑值,以及對應(yīng)的面積。例如,times為5,則輸出半徑1,2,3,4,5,以及對應(yīng)的圓面積。
(3)在main方法中調(diào)用printAreas()方法,調(diào)用完畢后輸出當(dāng)前半徑值。程序運行結(jié)果如圖所示。
7.4 遞歸(recursion)方法
舉例1:
舉例2:
從前有座山,山上有座廟,廟里有個老和尚,老和尚在給小和尚講故事,講的啥?從前有座山,山上有座廟,廟里有個老和尚,老和尚在給小和尚講故事,講的啥?從前有座山,山上有座廟,廟里有個老和尚,老和尚在給小和尚講故事,講的啥?從前有座山,山上有座廟,廟里有個老和尚,老和尚在給小和尚講故事,講的啥?......
老和尚沒了,廟塌了,小和尚還俗結(jié)婚了。
遞歸方法調(diào)用:方法自己調(diào)用自己的現(xiàn)象就稱為遞歸。
**遞歸的分類:**直接遞歸、間接遞歸。
-
直接遞歸:方法自身調(diào)用自己。
public void methodA(){methodA(); }
-
間接遞歸:可以理解為A()方法調(diào)用B()方法,B()方法調(diào)用C()方法,C()方法調(diào)用A()方法。
public static void A(){B(); }public static void B(){C(); }public static void C(){A(); }
說明:
- 遞歸方法包含了一種
隱式的循環(huán)
。 - 遞歸方法會
重復(fù)執(zhí)行
某段代碼,但這種重復(fù)執(zhí)行無須循環(huán)控制。 - 遞歸一定要向
已知方向
遞歸,否則這種遞歸就變成了無窮遞歸,停不下來,類似于死循環(huán)
。最終發(fā)生棧內(nèi)存溢出
。
舉例:
舉例1:計算1 ~ n的和
public class RecursionDemo {public static void main(String[] args) {RecursionDemo demo = new RecursionDemo();//計算1~num的和,使用遞歸完成int num = 5;// 調(diào)用求和的方法int sum = demo.getSum(num);// 輸出結(jié)果System.out.println(sum);}/*通過遞歸算法實現(xiàn).參數(shù)列表:int 返回值類型: int */public int getSum(int num) {/* num為1時,方法返回1,相當(dāng)于是方法的出口,num總有是1的情況*/if(num == 1){return 1;}/*num不為1時,方法返回 num +(num-1)的累和遞歸調(diào)用getSum方法*/return num + getSum(num-1);}
}
代碼執(zhí)行圖解:
舉例2:遞歸方法計算n!
public int multiply(int num){if(num == 1){return 1;}else{return num * multiply(num - 1);}
}
舉例3:已知有一個數(shù)列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整數(shù),求f(10)的值。
public int f(int num){if(num == 0){return 1;}else if(num == 1){return 4;}else{return 2 * f(num - 1) + f(num - 2);}
}
舉例4:已知一個數(shù)列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),其中n是大于0的整數(shù),求f(10)的值。
public int func(int num){if(num == 20){return 1;}else if(num == 21){return 4;}else{return func(num + 2) - 2 * func(num + 1);}
}
舉例5:計算斐波那契數(shù)列(Fibonacci)的第n個值,斐波那契數(shù)列滿足如下規(guī)律,
1,1,2,3,5,8,13,21,34,55,....
即從第三個數(shù)開始,一個數(shù)等于前兩個數(shù)之和。假設(shè)f(n)代表斐波那契數(shù)列的第n個值,那么f(n)滿足:
f(n) = f(n-2) + f(n-1);
//使用遞歸的寫法int f(int n) {//計算斐波那契數(shù)列第n個值是多少if (n < 1) {//負(fù)數(shù)是返回特殊值1,表示不計算負(fù)數(shù)情況return 1;}if (n == 1 || n == 2) {return 1;}return f(n - 2) + f(n - 1);}//不用遞歸int fValue(int n) {//計算斐波那契數(shù)列第n個值是多少if (n < 1) {//負(fù)數(shù)是返回特殊值1,表示不計算負(fù)數(shù)情況return 1;}if (n == 1 || n == 2) {return 1;}//從第三個數(shù)開始, 等于 前兩個整數(shù)相加int beforeBefore = 1; //相當(dāng)于n=1時的值int before = 1;//相當(dāng)于n=2時的值int current = beforeBefore + before; //相當(dāng)于n=3的值//再完后for (int i = 4; i <= n; i++) {beforeBefore = before;before = current;current = beforeBefore + before;/*假設(shè)i=4beforeBefore = before; //相當(dāng)于n=2時的值before = current; //相當(dāng)于n=3的值current = beforeBefore + before; //相當(dāng)于n = 4的值假設(shè)i=5beforeBefore = before; //相當(dāng)于n=3的值before = current; //相當(dāng)于n = 4的值current = beforeBefore + before; //相當(dāng)于n = 5的值....*/}return current;}
8. 關(guān)鍵字:package、import
8.1 package(包)
package,稱為包,用于指明該文件中定義的類、接口等結(jié)構(gòu)所在的包。
8.1.1 語法格式
package 頂層包名.子包名 ;
舉例:pack1\pack2\PackageTest.java
package pack1.pack2; //指定類PackageTest屬于包pack1.pack2public class PackageTest{public void display(){System.out.println("in method display()");}
}
說明:
- 一個源文件只能有一個聲明包的package語句
- package語句作為Java源文件的第一條語句出現(xiàn)。若缺省該語句,則指定為無名包。
- 包名,屬于標(biāo)識符,滿足標(biāo)識符命名的規(guī)則和規(guī)范(全部小寫)、見名知意
- 包通常使用所在公司域名的倒置:com.atguigu.xxx。
- 大家取包名時不要使用"
java.xx
"包
- 包對應(yīng)于文件系統(tǒng)的目錄,package語句中用 “.” 來指明包(目錄)的層次,每.一次就表示一層文件目錄。
- 同一個包下可以聲明多個結(jié)構(gòu)(類、接口),但是不能定義同名的結(jié)構(gòu)(類、接口)。不同的包下可以定義同名的結(jié)構(gòu)(類、接口)
8.1.2 包的作用
- 包可以包含類和子包,劃分
項目層次
,便于管理 - 幫助
管理大型軟件
系統(tǒng):將功能相近的類劃分到同一個包中。比如:MVC的設(shè)計模式 - 解決
類命名沖突
的問題 - 控制
訪問權(quán)限
8.1.3 應(yīng)用舉例
舉例1:某航運軟件系統(tǒng)包括:一組域?qū)ο?、GUI和reports子系統(tǒng)
舉例2:MVC設(shè)計模式
MVC是一種軟件構(gòu)件模式,目的是為了降低程序開發(fā)中代碼業(yè)務(wù)的耦合度。
MVC設(shè)計模式將整個程序分為三個層次:視圖模型(Viewer)層
,控制器(Controller)層
,與數(shù)據(jù)模型(Model)層
。這種將程序輸入輸出、數(shù)據(jù)處理,以及數(shù)據(jù)的展示分離開來的設(shè)計模式使程序結(jié)構(gòu)變的靈活而且清晰,同時也描述了程序各個對象間的通信方式,降低了程序的耦合性。
視圖層viewer:顯示數(shù)據(jù),為用戶提供使用界面,與用戶直接進(jìn)行交互。>相關(guān)工具類 view.utils>自定義view view.ui控制層controller:解析用戶請求,處理業(yè)務(wù)邏輯,給予用戶響應(yīng)>應(yīng)用界面相關(guān) controller.activity>存放fragment controller.fragment>顯示列表的適配器 controller.adapter>服務(wù)相關(guān)的 controller.service>抽取的基類 controller.base模型層model:主要承載數(shù)據(jù)、處理數(shù)據(jù)>數(shù)據(jù)對象封裝 model.bean/domain>數(shù)據(jù)庫操作類 model.dao>數(shù)據(jù)庫 model.db
8.1.4 JDK中主要的包介紹
java.lang
----包含一些Java語言的核心類,如String、Math、Integer、 System和Thread,提供常用功能
java.net
----包含執(zhí)行與網(wǎng)絡(luò)相關(guān)的操作的類和接口。
java.io
----包含能提供多種輸入/輸出功能的類。
java.util
----包含一些實用工具類,如定義系統(tǒng)特性、接口的集合框架類、使用與日期日歷相關(guān)的函數(shù)。
java.text
----包含了一些java格式化相關(guān)的類
java.sql
----包含了java進(jìn)行JDBC數(shù)據(jù)庫編程的相關(guān)類/接口
java.awt
----包含了構(gòu)成抽象窗口工具集(abstract window toolkits)的多個類,這些類被用來構(gòu)建和管理應(yīng)用程序的圖形用戶界面(GUI)。
8.2 import(導(dǎo)入)
為了使用定義在其它包中的Java類,需用import語句來顯式引入指定包下所需要的類。相當(dāng)于import語句告訴編譯器到哪里去尋找這個類
。
8.2.1 語法格式
import 包名.類名;
8.2.2 應(yīng)用舉例
import pack1.pack2.Test; //import pack1.pack2.*;表示引入pack1.pack2包中的所有結(jié)構(gòu)public class PackTest{public static void main(String args[]){Test t = new Test(); //Test類在pack1.pack2包中定義t.display();}
}
8.2.3 注意事項
-
import語句,聲明在包的聲明和類的聲明之間。
-
如果需要導(dǎo)入多個類或接口,那么就并列顯式多個import語句即可
-
如果使用
a.*
導(dǎo)入結(jié)構(gòu),表示可以導(dǎo)入a包下的所有的結(jié)構(gòu)。舉例:可以使用java.util.*的方式,一次性導(dǎo)入util包下所有的類或接口。 -
如果導(dǎo)入的類或接口是java.lang包下的,或者是當(dāng)前包下的,則可以省略此import語句。
-
如果已經(jīng)導(dǎo)入java.a包下的類,那么如果需要使用a包的子包下的類的話,仍然需要導(dǎo)入。
-
如果在代碼中使用不同包下的同名的類,那么就需要使用類的全類名的方式指明調(diào)用的是哪個類。
-
(了解)
import static
組合的使用:調(diào)用指定類或接口下的靜態(tài)的屬性或方法
9. 面向?qū)ο筇卣饕?#xff1a;封裝性(encapsulation)
9.1 為什么需要封裝?
- 我要用洗衣機,只需要按一下開關(guān)和洗滌模式就可以了。有必要了解洗衣機內(nèi)部的結(jié)構(gòu)嗎?有必要碰電動機嗎?
- 我要開車,我不需要懂離合、油門、制動等原理和維修也可以駕駛。
- 客觀世界里每一個事物的內(nèi)部信息都隱藏在其內(nèi)部,外界無法直接操作和修改,只能通過指定的方式進(jìn)行訪問和修改。
隨著我們系統(tǒng)越來越復(fù)雜,類會越來越多,那么類之間的訪問邊界必須把握好,面向?qū)ο蟮拈_發(fā)原則要遵循“高內(nèi)聚、低耦合
”。
高內(nèi)聚、低耦合是軟件工程中的概念,也是UNIX 操作系統(tǒng)設(shè)計的經(jīng)典原則。
內(nèi)聚,指一個模塊內(nèi)各個元素彼此結(jié)合的緊密程度;耦合指一個軟件結(jié)構(gòu)內(nèi)不同模塊之間互連程度的度量。內(nèi)聚意味著重用和獨立,耦合意味著多米諾效應(yīng)牽一發(fā)動全身。
而“高內(nèi)聚,低耦合”的體現(xiàn)之一:
高內(nèi)聚
:類的內(nèi)部數(shù)據(jù)操作細(xì)節(jié)自己完成,不允許外部干涉;低耦合
:僅暴露少量的方法給外部使用,盡量方便外部調(diào)用。
9.2 何為封裝性?
所謂封裝,就是把客觀事物封裝成抽象概念的類,并且類可以把自己的數(shù)據(jù)和方法只向可信的類或者對象開放,向沒必要開放的類或者對象隱藏信息。
通俗的講,把該隱藏的隱藏起來,該暴露的暴露出來。這就是封裝性的設(shè)計思想。
9.3 Java如何實現(xiàn)數(shù)據(jù)封裝
-
實現(xiàn)封裝就是控制類或成員的可見性范圍。這就需要依賴訪問控制修飾符,也稱為權(quán)限修飾符來控制。
-
權(quán)限修飾符:
public
、protected
、缺省
、private
。具體訪問范圍如下:
修飾符 | 本類內(nèi)部 | 本包內(nèi) | 其他包的子類 | 其他包非子類 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
- 具體修飾的結(jié)構(gòu):
- 外部類:public、缺省
- 成員變量、成員方法、構(gòu)造器、成員內(nèi)部類:public、protected、缺省、private
9.4 封裝性的體現(xiàn)
9.4.1 成員變量/屬性私有化
概述:私有化類的成員變量,提供公共的get和set方法,對外暴露獲取和修改屬性的功能。
實現(xiàn)步驟:
① 使用 private
修飾成員變量
private 數(shù)據(jù)類型 變量名 ;
代碼如下:
public class Person {private String name;private int age;private boolean marry;
}
② 提供 getXxx
方法 / setXxx
方法,可以訪問成員變量,代碼如下:
public class Person {private String name;private int age;private boolean marry;public void setName(String n) {name = n;}public String getName() {return name;}public void setAge(int a) {age = a;}public int getAge() {return age;}public void setMarry(boolean m){marry = m;}public boolean isMarry(){return marry;}
}
③ 測試:
public class PersonTest {public static void main(String[] args) {Person p = new Person();//實例變量私有化,跨類是無法直接使用的/* p.name = "張三";p.age = 23;p.marry = true;*/p.setName("張三");System.out.println("p.name = " + p.getName());p.setAge(23);System.out.println("p.age = " + p.getAge());p.setMarry(true);System.out.println("p.marry = " + p.isMarry());}
}
成員變量封裝的好處:
- 讓使用者只能通過事先預(yù)定的方法來
訪問數(shù)據(jù)
,從而可以在該方法里面加入控制邏輯,限制對成員變量的不合理訪問。還可以進(jìn)行數(shù)據(jù)檢查,從而有利于保證對象信息的完整性。 便于修改
,提高代碼的可維護(hù)性。主要說的是隱藏的部分,在內(nèi)部修改了,如果其對外可以的訪問方式不變的話,外部根本感覺不到它的修改。例如:Java8->Java9,String從char[]轉(zhuǎn)為byte[]內(nèi)部實現(xiàn),而對外的方法不變,我們使用者根本感覺不到它內(nèi)部的修改。
開心一笑:
A man and woman are in a computer programming lecture. The man touches the woman's breasts."Hey!" she says. "Those are private!"The man says, "But we're in the same class!"
9.4.2 私有化方法
public class ArrayUtil {public int max(int[] arr) {int maxValue = arr[0];for(int i = 1;i < arr.length;i++){if(maxValue < arr[i]){maxValue = arr[i];}}return maxValue;}public int min(int[] arr){int minValue = arr[0];for(int i = 1;i < arr.length;i++){if(minValue > arr[i]){minValue = arr[i];}}return minValue;}public int sum(int[] arr) {int sum = 0;for(int i = 0;i < arr.length;i++){sum += arr[i];}return sum;}public int avg(int[] arr) {int sumValue = sum(arr);return sumValue / arr.length;}// 創(chuàng)建一系列重載的上述方法// public double max(double[] arr){}// public float max(float[] arr){}// public byte max(byte[] arr){}public void print(int[] arr) {for(int i = 0;i < arr.length;i++){System.out.print(arr[i] + " ");}System.out.println();}public int[] copy(int[] arr) {int[] arr1 = new int[arr.length];for(int i = 0;i < arr.length;i++){arr1[i] = arr[i];}return arr1;}public void reverse(int[] arr) {for(int i = 0,j = arr.length - 1;i < j;i++,j--){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}public void sort(int[] arr,String desc) {if("ascend".equals(desc)){//if(desc.equals("ascend")){for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {
// int temp = arr[j];
// arr[j] = arr[j + 1];
// arr[j + 1] = temp;swap(arr,j,j+1);}}}}else if ("descend".equals(desc)){for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] < arr[j + 1]) {
// int temp = arr[j];
// arr[j] = arr[j + 1];
// arr[j + 1] = temp;swap(arr,j,j+1);}}}}else{System.out.println("您輸入的排序方式有誤!");}}private void swap(int[] arr,int i,int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public int getValue(int[] arr, int value) {//方法:線性查找for(int i = 0;i < arr.length;i++){if(value == arr[i]){return i;}}return - 1;}
}
注意:
開發(fā)中,一般成員實例變量都習(xí)慣使用private修飾,再提供相應(yīng)的public權(quán)限的get/set方法訪問。
對于final的實例變量,不提供set()方法。(后面final關(guān)鍵字的時候講)
對于static final的成員變量,習(xí)慣上使用public修飾。
10. 類的成員之三:構(gòu)造器(Constructor)
我們new完對象時,所有成員變量都是默認(rèn)值,如果我們需要賦別的值,需要挨個為它們再賦值,太麻煩了。我們能不能在new對象時,直接為當(dāng)前對象的某個或所有成員變量直接賦值呢?
可以,Java給我們提供了構(gòu)造器(Constructor)
,也稱為構(gòu)造方法
。
10.1 構(gòu)造器的作用
new對象,并在new對象的時候為實例變量賦值。
舉例:Person p = new Person(“Peter”,15)
;
解釋:如同我們規(guī)定每個“人”一出生就必須先洗澡,我們就可以在“人”的構(gòu)造器中加入完成“洗澡”的程序代碼,于是每個“人”一出生就會自動完成“洗澡”,程序就不必再在每個人剛出生時一個一個地告訴他們要“洗澡”了。
10.2 構(gòu)造器的語法格式
[修飾符] class 類名{[修飾符] 構(gòu)造器名(){// 實例初始化代碼}[修飾符] 構(gòu)造器名(參數(shù)列表){// 實例初始化代碼}
}
說明:
- 構(gòu)造器名必須與它所在的類名必須相同。
- 它沒有返回值,所以不需要返回值類型,也不需要void。
- 構(gòu)造器的修飾符只能是權(quán)限修飾符,不能被其他任何修飾。比如,不能被static、final、synchronized、abstract、native修飾,不能有return語句返回值。
代碼如下:
public class Student {private String name;private int age;// 無參構(gòu)造public Student() {}// 有參構(gòu)造public Student(String n,int a) {name = n;age = a;}public String getName() {return name;}public void setName(String n) {name = n;}public int getAge() {return age;}public void setAge(int a) {age = a;}public String getInfo(){return "姓名:" + name +",年齡:" + age;}
}
public class TestStudent {public static void main(String[] args) {//調(diào)用無參構(gòu)造創(chuàng)建學(xué)生對象Student s1 = new Student();//調(diào)用有參構(gòu)造創(chuàng)建學(xué)生對象Student s2 = new Student("張三",23);System.out.println(s1.getInfo());System.out.println(s2.getInfo());}
}
10.3 使用說明
- 當(dāng)我們沒有顯式的聲明類中的構(gòu)造器時,系統(tǒng)會默認(rèn)提供一個無參的構(gòu)造器并且該構(gòu)造器的修飾符默認(rèn)與類的修飾符相同
- 當(dāng)我們顯式的定義類的構(gòu)造器以后,系統(tǒng)就不再提供默認(rèn)的無參的構(gòu)造器了。
- 在類中,至少會存在一個構(gòu)造器。
- 構(gòu)造器是可以重載的。
11. 階段性知識補充
11.1 類中屬性賦值過程
1、在類的屬性中,可以有哪些位置給屬性賦值?
① 默認(rèn)初始化
② 顯式初始化
③ 構(gòu)造器中初始化
④ 通過"對象.屬性"或"對象.方法"的方式,給屬性賦值
2、這些位置執(zhí)行的先后順序是怎樣?
順序:① - ② - ③ - ④
3、說明:
- 上述中的①、②、③在對象創(chuàng)建過程中,只執(zhí)行一次。
- ④ 是在對象創(chuàng)建后執(zhí)行的,可以根據(jù)需求多次執(zhí)行。
11.2 JavaBean
-
JavaBean是一種Java語言寫成的可重用組件。
- 好比你做了一個扳手,這個扳手會在很多地方被拿去用。這個扳手也提供多種功能(你可以拿這個扳手扳、錘、撬等等),而這個扳手就是一個組件。
-
所謂JavaBean,是指符合如下標(biāo)準(zhǔn)的Java類:
- 類是公共的
- 有一個無參的公共的構(gòu)造器
- 有屬性,且有對應(yīng)的get、set方法
-
用戶可以使用JavaBean將功能、處理、值、數(shù)據(jù)庫訪問和其他任何可以用Java代碼創(chuàng)造的對象進(jìn)行打包,并且其他的開發(fā)者可以通過內(nèi)部的JSP頁面、Servlet、其他JavaBean、applet程序或者應(yīng)用來使用這些對象。用戶可以認(rèn)為JavaBean提供了一種隨時隨地的復(fù)制和粘貼的功能,而不用關(guān)心任何改變。
-
《Think in Java》中提到,JavaBean最初是為Java GUI的可視化編程實現(xiàn)的。你拖動IDE構(gòu)建工具創(chuàng)建一個GUI 組件(如多選框),其實是工具給你創(chuàng)建Java類,并提供將類的屬性暴露出來給你修改調(diào)整,將事件監(jiān)聽器暴露出來。
-
示例
public class JavaBean {private String name; // 屬性一般定義為privateprivate int age;public JavaBean() {}public int getAge() {return age;}public void setAge(int a) {age = a;}public String getName() {return name;}public void setName(String n) {name = n;} }
11.3 UML類圖
-
UML(Unified Modeling Language,統(tǒng)一建模語言),用來描述
軟件模型
和架構(gòu)
的圖形化語言。 -
常用的UML工具軟件有
PowerDesinger
、Rose
和Enterprise Architect
。 -
UML工具軟件不僅可以繪制軟件開發(fā)中所需的各種圖表,還可以生成對應(yīng)的源代碼。
-
在軟件開發(fā)中,使用
UML類圖
可以更加直觀地描述類內(nèi)部結(jié)構(gòu)(類的屬性和操作)以及類之間的關(guān)系(如關(guān)聯(lián)、依賴、聚合等)。- +表示 public 類型, - 表示 private 類型,#表示protected類型
- 方法的寫法:
方法的類型(+、-) 方法名(參數(shù)名: 參數(shù)類型):返回值類型 - 斜體表示抽象方法或類。