Java劉意第八天筆記
工具中使用靜態:
在同一個類中,main方法只能訪問靜態方法。
【錯誤:無法從靜態上下文中引用非靜態,這樣的錯誤一定是因為在main方法中呼叫了非靜態方法。】
對非靜態方法來說,只能通過物件(也就是其他類的物件)來呼叫非靜態方法。
靜態方法當然也可以,而且靜態方法可以直接呼叫類名來訪問。
下面考慮一個問題:
在工具類中,假如我不想被人創造物件,只想被人直接通過類名呼叫靜態方法,該怎麼辦?
把類中的方法設定為靜態之後,將構造方法私有,外界就不能創造物件了。
ArrayTool是工具的名字,printArray是其中的靜態方法。
幫助文件的製作:
如果在工程中,你給別人的,別人給你的,都是無法閱讀的class檔案,而不是java,比如上文中的ArrayTool,就相當於,我給了你一個筆記本,你連開機都開不開,這個時候怎麼辦?你需要一個說明書。
第一步:
寫一個工具類。
第二步:
對這個類加入文件註釋【文件註釋,就可以被解析為說明書】。
怎麼加呢?加點什麼呢?
文件註釋的格式:
/**
*blablabla
*blablabla
*blablabla
/
@author:作者
@param:引數
@return:返回值
示例:
/**
-
這是針對陣列進行操作的工具類
-
@author 劉意
-
@version V.10
*/
public class ArrayTool {
//把構造方法私有,外界就不能在建立物件了 /** * 這是私有構造 */ privateArrayTool(){} /** * 這是遍歷陣列的方法,遍歷後的格式是:[元素1, 元素2, 元素3, ...] * @param arr 這是要被遍歷的陣列 */ public staticvoid printArray(int[] arr) { System.out.print("["); for(intx=0; x<arr.length; x++) { if(x== arr.length-1) { System.out.println(arr[x]+"]"); }else{ System.out.print(arr[x]+","); } } } /** * 這是獲取陣列中最大值的方法 * @param arr 這是要獲取最大值的陣列 * @return 返回陣列中的最大值 */ public staticint getMax(int[] arr) { int max =arr[0]; for(intx=1; x<arr.length; x++) { if(arr[x]> max) { max= arr[x]; } } returnmax; } /** * 獲取指定元素在陣列中第一次出現的索引,如果元素不存在,就返回-1 * @param arr 被查詢的陣列 * @param value要查詢的元素 * @return 返回元素在陣列中的索引,如果不存在,返回-1 */ public staticint getIndex(int[] arr,int value) { int index= -1; for(intx=0; x<arr.length; x++) { if(arr[x]== value) { index= x; break; } } returnindex; }
}
第三步:用javadoc工具解釋文件註釋
格式:在cmd中輸入:
javadoc –d 目錄 –author –versionArrayTool.java
【目錄如果是一個點,表示當前目錄。目錄可以寫一個資料夾的路徑,如果沒有這個目錄它會自動建立。找不到可以文件化的公共或保護的類:說明類的許可權不夠。只需要把要編譯的類宣告行的類名前加public】
解析後,用瀏覽器開啟文件的index,就可以看到相當高階的解析後的class說明頁面。
如何使用JDK的幫助文件:
顯示(左上角)-索引-輸入框,搜尋自己需要的東西
舉例:通過API學習MATH類。
程式碼塊:
在java中,使用{}括起來的程式碼被稱為程式碼塊。但是根據其位置和宣告的不同,可以分為區域性程式碼塊、構造程式碼塊、靜態程式碼塊和同步程式碼塊(最後一個先不講。)
看下面的demo:
第二次輸出x會報錯,因為x的作用域已經結束了。
區域性程式碼塊:在方法中出現,限定變數生命週期,令其及早釋放,節省記憶體空間。區域性程式碼塊是順序執行的(塊裡塊外都是)
什麼意思呢?當我們建立了這樣一個類,上下輸出x和y的兩個程式碼塊,都會在建立code的物件時,先於構造方法而執行。
【如果沒有給這個構造程式碼塊,就會出現錯誤:需要識別符號。動作不能直接放到類裡】
(沒找到符號,假裝這裡有一個向上的箭頭)構造程式碼塊:在類中的成員位置,用{}括起來的程式碼。每次呼叫構造方法執行前,都會先執行。可以把多個構造方法中的共同程式碼放到一起。
靜態程式碼塊:加一個static修飾。無論物件建立多少次,靜態程式碼塊只加載一次,而且先於構造程式碼塊執行。一般用於對類進行初始化。
Quiz:靜態程式碼塊,構造程式碼塊,構造方法的執行順序?
靜態程式碼塊→構造程式碼塊→構造方法
靜態程式碼塊只執行一次,其他兩個每次都執行。
同樣型別的程式碼塊,按定義的先後執行。
一個應用:下面的程式碼,哪個先執行,哪個後執行?
class Student {
static {
System.out.println("Student靜態程式碼塊");
}
{
System.out.println("Student構造程式碼塊");
}
publicStudent() {
System.out.println("Student構造方法");
}
}
class StudentDemo {
static {
System.out.println("林青霞都60了,我很傷心");
}
public staticvoid main(String[] args) {
System.out.println("我是main方法");
Student s1= new Student();
Student s2= new Student();
}
}
執行結果:
繼承:多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那個類即可。
如果類A繼承於類B,那麼A中就包含有B中的功能。
通過這樣的方式,可以把一些不同的類中共有的功能集中到它們的父類上。有了繼承以後,子類還可以定義一些新成員。
父類又稱基類或超類,子類又稱子類或派生類。
繼承格式:
class 子類名 extends 父類名{}
繼承的作用:
繼承的特點:
1.只支援單繼承,不支援多繼承。即一個子類只能繼承一個父類,不過一個父類可以被多個子類繼承。(據說有些語言是支援多繼承的)
2.但是,java中支援多層繼承。
下面這樣是可以的。
繼承的注意事項:
- 子類只能繼承父類所有非私有的成員(成員方法和成員變數)
【設想這樣一種情況,父類中定義了一個公共方法,在這個方法中返回了私有的變數,那麼子類是可以通過這個公共方法來獲得私有變數的,但是不能直接繼承父類的私有成員,這個和set/get方法是一個道理】
-
子類不能繼承父類的構造方法,但是可以通過super關鍵字區訪問父類構造方法。
-
不要為了部分功能而去繼承。
上面這種寫法為什麼不好?因為我不要show1()啊。
什麼情況下應該採用繼承?假設法。
繼承體現的是一種“is a”的關係。比如:
Person
Student
Teacher
→
Student is a Person
Teacher is a Person
如果有兩個類A,B,只有它們符合A是B的一種(或者反過來),就可以用來繼承。
那麼如果子類和父類的變數/方法名重合了,會發生什麼事?根據就近原則,會打印出離使用這個變數的語句最近的那一個值。也就是說查詢範圍是從小到大。
如果我想要的是成員範圍類的變數呢?之前說過了,this關鍵字。
如果我想要父類成員範圍的num(父類區域性範圍的num不能直接使用,因為方法之間是平級的,方法不能相互呼叫),就是用super關鍵字。
呼叫變數還是比較好理解的。但是繼承中構造方法是什麼關係呢?
子類中所有的構造方法預設都會訪問父類的無參構造方法。
原因:子類可能要使用父類的資料。需要先使用父類的構造方法來初始化。子類的每一個構造方法的第一句語句預設都是:super()。
【針對上一句話補充一下:我自己做了一個測試,如果在子類的構造方法中沒有寫super()或者寫了,都會呼叫父類的構造方法。但是如果子類在構造方法的第一句中顯式地呼叫了父類地帶參構造方法(super(”xxx”)),則會執行這個構造方法,不執行無參構造方法。】
如果父類沒有無參構造方法(意思是顯式地指定了並只指定了一個帶參構造方法),直接用super()或者乾脆什麼都不寫是一定會報錯的(實際引數列表和形式引數列表長度不同),這個時候只能直接或間接地顯式地呼叫父類的帶參構造方法。
當然啦,我們都不希望出現這種情況,所以最好還是自己定義好所有無參構造方法。
Tips:this或者super在呼叫構造方法時,必須出現在第一條語句上。
一個類的初始化過程:
先對成員變數初始化(預設初始化/顯示初始化/構造方法初始化)
什麼意思呢?對上一個類來說,先初始化num(值為0),再賦值為10,最後執行構造方法的列印。
繼承類的初始化過程:
對於同一個類:靜態程式碼塊>構造程式碼塊>構造方法
對於繼承的類,子類初始化之前先會進行父類的初始化。
看題的時候從main方法開始。
這裡有個很精彩的例題,全文複製過來:
/*
看程式寫結果:
A:一個類的靜態程式碼塊,構造程式碼塊,構造方法的執行流程
靜態程式碼塊 > 構造程式碼塊 > 構造方法
B:靜態的內容是隨著類的載入而載入
靜態程式碼塊的內容會優先執行
C:子類初始化之前先會進行父類的初始化
結果是:
靜態程式碼塊Fu
靜態程式碼塊Zi
構造程式碼塊Fu
構造方法Fu
構造程式碼塊Zi
構造方法Zi
*/
class Fu {
static {
System.out.println("靜態程式碼塊Fu");
}
{
System.out.println("構造程式碼塊Fu");
}
public Fu() {
System.out.println("構造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("靜態程式碼塊Zi");
}
{
System.out.println("構造程式碼塊Zi");
}
public Zi() {
System.out.println("構造方法Zi");
}
}
class ExtendsTest2 {
public staticvoid main(String[] args) {
Zi z = newZi();
}
}
雖然子類中構造方法預設有一個super(),但是初始化的時候不是按照那個順序進行的,而是按照分層初始化進行的,它僅僅表示先初始化父類,再初始化子類。
經過試驗,這和構造方法有沒有引數、呼叫哪個、怎麼呼叫,都沒有關係,結果不變。
繼承中成員方法的關係:
子類中的方法和父類中的方法的宣告一樣時,會按照就近原則呼叫子類中的方法。
方法重寫:子類中出現了和父類中方法宣告一摸一樣(方法名、引數列表、返回值型別)的方法。
【注意區分重寫和過載:過載,是同一個類裡,引數列表不同,與返回值無關】
當子類需要父類的功能,而功能主體子類有自己特有的內容時,可以重寫父類中的方法。這樣,既沿襲了父類的功能,又定義了子類特有的功能。
方法重寫的注意事項:
-
父類的私有方法不能被重寫。【你連訪問都訪問不到,就別想著覆蓋了】
-
子類重寫父類方法時,訪問許可權不能更低。【如果父類的方法是public,那子類的方法不能是private或者不宣告】大於等於,最好一致。
-
父類的靜態方法,子類也必須通過靜態方法進行重寫。
(這一點的具體解釋到多型中再解釋)靜態和動態,父類和子類最好是一模一樣。
子類重寫父類方法時,最好宣告一模一樣,這樣肯定不會出問題。
下面是兩個面試題:
1:方法重寫和方法過載的區別?方法過載能改變返回值型別嗎?
方法重寫:
在子類中,出現和父類中一模一樣的方法宣告的現象。
方法過載:
同一個類中,出現的方法名相同,引數列表不同的現象。
方法過載能改變返回值型別,因為它和返回值型別無關。
而方法重寫肯定不可以。
Override:方法重寫
Overload:方法過載
2:this關鍵字和super關鍵字分別代表什麼?以及他們各自的使用場景和作用。
this:代表當前類的物件引用
super:代表父類儲存空間的標識。(可以理解為父類的引用,通過這個東西可以訪問父類的成員)
場景:
成員變數:
this.成員變數
super.成員變數
構造方法:
this(...)
super(...)
成員方法:
this.成員方法
super.成員方法