方法/函數
爲了實現某段代碼的重複利用
格式
修飾符 返回值類型 方法名(參數列表){
方法體;
return 返回值;
}
// 明確返回值類型---方法執行完成之後,返回值的數據類型是什麼方法的返回值類型就是對應的類型---求整型數組的元素之和---確定返回值類型是int---如果方法執行完成之後沒有返回值,那麼返回值類型就是void // 明確是否需要參數---如果方法執行過程中需要一些未知量參與運算,而未知量子啊方法中不能自動產生,那麼這個時候需要以參數的形式體現---需要一個int類型的數組參與運算 // 聲明一個參數實際上就是聲明瞭變量---形式參數---形參 // 方法簽名---add(int[]) public static int add(int[] arr){ int sum = 0; for(int i : arr){ sum += i; } return sum; } // 在調用函數傳入的實際值---實際參數---實參 add(arr);
注意:
1.如果函數沒有返回值,那麼返回值類型定義爲void
2. return之後的代碼就執行不到了---一個函數一旦return,那麼後續代碼就不再執行了
3. 任何一個實體方法內都可以寫return
練習:
1. 判斷質數
2. 1000以內的親密數--- A的所有因子(包含了1而不包含本身)之和等於B,並且B的所有因子(包含1而不包含本身)之和等於A,那麼A和B就是一對親密數
3. 任何一個大於等於6的偶數都可以分解爲兩個質數之和。
6= 3 + 3;
14 = 3 + 11 = 7 + 7
18 = 5 + 13 = 7 + 11
方法的重載
當同一個類內出現了方法名一致而參數列表不同的方法的時候,構成了方法的重載。---方法名一致而參數列表不同---方法名+參數列表---方法簽名
方法在調用的時候會進行最優匹配---找參數類型最符合的進行匹配。---如果進行方法的重載,儘量重載所有的情況,以防出現調用混亂的情況
方法的遞歸
函數調用自己本身就形成了遞歸。
public static int sum(int number){ if(number <= 0){ return 0; } return number + sum(number - 1); }
練習:用遞歸求階乘
n! = n* (n - 1)(n-2)...1 5 = 5 * 4 * 3 * 2 * 1 = 120
StackOverflowError---棧溢出錯誤---反應函數在棧中執行---函數在執行完成之前不會釋放棧內存---遞歸的次數太多,就會出現這個錯誤
注意:在傳遞參數的時候,對於基本類型而言,傳遞的實際值;對於引用類型而言,傳遞的是地址。---只要地址不發生改變,函數中一切變化會作用於原數組
開發軟件:Eclipse---日食
IDE---智能開發工具
Luna---Mars---Neon
綠色的,基於插件的,免費的,開源的
IDEA--Intelli J
MyEclipse
Alt+/ 快速提示鍵
面向對象
面向對象是相對面向過程而言的。面向過程注重的是過程,強調的是動作;面向對象注重的是對象,只要找到了對應的對象,那麼就自然擁有了對象所具有的一切功能
面向對象是基於面向過程的。
面向對象一定優於面向過程嗎?當場景比較複雜的時候,建議使用面向對象;當事物比較簡單的時候,建議使用面向過程
類和對象的關係
根據一類事物進行抽取,將這一類事物用一個類表示,同時將這一類事物的共有特徵抽取成屬性,將這一類事物的共有行爲抽取成了方法---將這些屬性和方法放到了一個類中,用這個類表示這一類的事物---類是對象的抽取
/概括
根據類利用new關鍵字去創建該類對象的對象(實例),並且可以爲對象的對應屬性賦值,也可以去調用對應的方法---對象是類的實例化/具體化
對象在內存中的存儲
對象在傳值的時候傳遞的是地址
成員變量和局部變量之間有什麼不同?
1. 定義位置:成員變量是定義在類內方法外;局部變量是定義在方法或者語句內部
2. 作用域:成員變量作用在整個類中;局部變量只能在定義的方法或者語句內部使用
3.內存存儲位置不同:成員變量是隨着對象的創建存儲到了堆內存中,並且在堆內存中自動的賦予默認值;局部變量在方法或者語句執行的時候存儲到了棧內存在中,不會自動給值
4. 生命週期不同:成員變量是隨着對象的創建而加載到堆內存中,隨着對象的回收而釋放;局部變量在方法或者語句執行的時候纔會創建,隨着方法或者語句的結束而立即移除出棧內存
構造方法/函數
在類中與類同名而沒有返回值類型的方法---主要是用於創建對象,在創建對象的時候實際上是在調用對應形式的構造函數
構造方法可以重載
如果一個類沒有手動提供構造函數,那麼JVM在編譯的時候會自動添加一個無參的構造方法。
---構造函數可以重載
構造方法沒有返回值類型,也沒有返回值,能寫return語句嗎?---可以---規避風險
this關鍵字
注意:在Java中所有的非靜態屬性和非靜態方法都是通過對象來調用的。
this代表當前在使用的對象---由於在類內沒有實際對象的存在,所以通過this作爲一個虛擬對象來調用本類中的其他方法和屬性
可以通過this語句來調用本類中的其他對應形式的構造方法,--this必須放在構造函數的第一行
構造代碼塊
也叫初始化代碼塊。在構造函數執行之前先執行一次
在類內方法外用{}定義的代碼塊------在創建對象的時候先於構造函數執行---無論調用哪個構造函數都可以去執行某些必要的代碼
局部代碼塊
在方法內或者語句內用{}定義的代碼塊---局部代碼塊---限制變量的生命週期從而提高內存的利用率
int i = 0; while(i < 10); { System.out.println(i); i++; }
匿名對象
在代碼中聲明的沒有名字的對象---在創建之後只能使用一次---可以作爲參數進行傳遞
面向對象的特徵
封裝 繼承 多態(抽象)
封裝
封裝的體現形式:
函數,屬性的私有化---爲了防止在類外直接操作屬性的時候給屬性賦值一些不符合常量的值,因此將屬性私有化,提供了對外的訪問方法來間接的操作屬性---屬性私有化保護了數據的安全性
優勢:提高了代碼的複用性,提高了代碼的安全性
權限修飾符
用於限定變量或者方法的使用範圍的
| 本類 | 子類 | 同包類 | 其他類 |
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默認 | 可以 | 同包子類可以 | 可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
public的使用範圍最大
繼承
如果一些類中有一些共有的方法和屬性,將這些方法和屬性提取出來放到一個新的類中,然後利用extends關鍵字讓原來的類和新的類產生聯繫--這個關係稱之爲繼承。新的類稱之爲父類(超類,基類),原來的類稱之爲子類(派生類)
在Java中支持的是單繼承---一個子類只能有一個父類,而一個父類可以有多個子類
子類通過繼承父類可以使用父類中的一些方法和屬性
Java中支持多層繼承
單繼承和多繼承的優劣性比較:
多繼承在代碼的複用性上要優於單繼承
優點:提高了代碼的複用性,可以避免方法調用的混亂--使方法的調用更加安全
class A{ public int m(){ return 5; } } class B { public int m(){ return 10; } } public class C extends A, B{} // C子類中可以從A,B父類中繼承過來m方法 C c = new C(); int i = c.m(); // 多繼承會導致方法調用的混亂
方法的重寫
當父子類中產生了方法簽名完全一致的方法的時候,構成了方法的重寫/覆蓋
注意:方法的重寫遵循 兩等兩小一大 這五個要求
1. 方法簽名要求完全一致
2. 如果父類中方法的返回值類型是基本類型/void/最終類,子類中重寫的方法的返回值類型必須一致
class A { public void m(){} } class B extends A { public void m(){} }
3.如果父類方法的返回值類型是引用數據類型,那麼子類重寫的方法的返回值類型是父類方法返回值類型的子類或者是本身
class A {} class B extends A{} class C{ public A m(){ return null; } } class D extends C { public B m(){ return null; } }
4. 子類方法的權限修飾符的範圍要大於等於父類方法的權限修飾符的範圍
class A{ protected void m(){} } class B extends A{ public void m(){} }
注意:八種基本類型之間沒有繼承關係的
super關鍵字
super表示在子類中代表了父類的對象---通過super來調用父類中的方法或者屬性---可以將super看成一個虛擬對象
super語句---在子類構造函數中通過調用super語句來創建一個父類對象---如果父類中沒有提供無參構造,那麼這個時候子類的構造函數中手動添加一個super語句---super語句必須放在子類構造函數的首行
子類的構造函數中必然直接或者簡介的有一個super語句---在創建子類對象的時候必然先創建一個父類對象
多態
編譯時多態:函數的重載
運行時多態:函數的重寫 向上造型---基於繼承,繼承是運行時多態的前提
行爲多態:函數的重載和重寫
對象多態:向上造型
Animal a = new Lion();---在編譯時期並不會真正的關心是哪個具體的子類,在運行的時候需要確定是哪個具體的子類以分配空間
注意:向上造型用父類聲明用子類初始化---由於這個對象是父類聲明的,所以對象能夠幹什麼需要看父類;由於是用子類初始化,具體怎麼幹要看子類
重寫的理解
1. 子類中重寫的方法的權限修飾符要大於等於父類
class A { public void m(){} } class B extends A { void m(){} } A a = new B(); // 聲明a對象的時候用的A類,那麼A類告訴a對象可以使用一個m方法,並且告訴a對象這個m方法是一個public修飾的方法在任何地方都可使用 a.m(); // 具體的實現用的是B這個子類,當執行方法的時候需要看具體子類,發現這個方法使用默認權限修飾的
2. 如果父類方法的返回值類型是引用類型,那麼子類重寫的方法的返回值類型是父類方法返回值類型本身或者子類
class A { public void ma(){} } class B extends A { public void mb(){} } class C { public B m(){/*more code here*/} } class D extends C { public A m(){/*more code here*/} } C c = new D(); // 聲明c對象用的是C類,C類告訴c對象有一個m方法可以使用,並且這個m的方法的返回值類型是一個B類型 B b = c.m(); // 方法的具體執行要看子類,子類中實際上這個方法的返回值類型是A---子類對象接了一個父類對象 b.mb(); // 子類接了一個父類對象,所以實際上是一個父類對象,而父類對象中不一定含有子類中獨有的方法
3. 如果父類方法的返回值類型是基本類型/void/最終類的時候,子類的方法的返回值類型要一致----基本類型之間沒有繼承關係
能夠用子類來接住一個父類對象麼?---不行
B -> A
B b = new A();
static
修飾變量、方法、代碼塊、內部類
靜態變量
static修飾變量---靜態變量,也叫類變量---隨着類的加載而加載到了方法區中的靜態區,並且在靜態區中自動賦予了一個默認值。靜態變量先於對象而存在,所以靜態變量可以不通過對象而通過類來調用,也可以通過對象來調用。該類產生的所有的對象實際上存的是該靜態變量在靜態區中的地址,靜態變量是被所有對象所共享的。
靜態變量是先於對象而存在,所以靜態變量可以不通過對象而通過類名來調用
System.out System.in
靜態變量可以定義在構造代碼塊中嗎?---不可以
靜態變量可以在構造代碼塊中初始化嗎?---可以
方法區
存儲類信息。存儲類的基本信息的區域稱之爲靜態常量池。存儲靜態屬性和靜態方法的地方稱之爲靜態區。
一個類在第一次真正使用的時候纔會向方法區中加載---所以類只加載一次,加載完成之後就不再從方法區中移除。
靜態方法
static修飾方法---靜態方法,也叫類方法---在類加載的時候加載到了方法區中靜態區,只是存儲在靜態區,並不執行,而是在方法被調用的時候到棧內存中執行。靜態方法先於對象而存在的,所以靜態方法可以通過類名來調用,也可以通過對象來調用。
System.arraycopy()
Arrays.toString()
Arrays.copyOf()
Math.sqrt()
System.out.println();---out是一個靜態對象
靜態變量可以定義到靜態方法中嗎?---不可以---靜態方法在靜態區中只存儲不執行
靜態方法中可以直接調用本類中的非靜態方法嗎?---不可以
能在主函數中使用this或者super嗎?---不可以
靜態方法可以重載嗎?---可以
Arrays.sort()
靜態方法可以重寫(覆蓋)嗎?---不可以
父子類中可以存在方法簽名完全一致的靜態方法嗎?---可以
靜態看的是聲明類,非靜態看的是實現類---可以---如果父子類中存在了方法簽名完全一致的方法,要麼都是靜態的,要麼都是非靜態的。
靜態代碼塊
用static{}包括起來的代碼
在這個類第一次被真正使用(第一次創建對象/調用方法)的時候執行一次。
執行順序:
先父後子,先靜後動-------父類靜態->子類靜態->父類非靜態->子類非靜態