以下是《瘋狂Java講義》中的一些知識述吸,如有錯誤喷橙,煩請指正癞志。
類和對象
定義類
[修飾符] class 類名 {….類體…..}
- Java類名通常以大寫字母開頭往枷,如果類名稱由多個單詞組成,則每個單詞的首字母均應為大寫凄杯。
- 類的修飾符可以是public错洁、final、abstract; 或省略這三個戒突。
- 一個類里可以包含三種最常見的成員:構(gòu)造器屯碴、屬性、方法膊存。注意如果沒有為一個類編寫構(gòu)造器导而,該系統(tǒng)會自動為該類提供一個構(gòu)造器;一旦程序員提供了一個構(gòu)造器隔崎,系統(tǒng)將不再為該類提供構(gòu)造器今艺。
定義成員變量
[修飾符] 類型 成員變量名[=默認值];
- 修飾符: 可以是public、protected爵卒、private虚缎、static、final钓株,其中前三個只能出現(xiàn)其一遥巴,并可與后兩個組合修飾。
- 可以是基本類型或者引用類型
- 成員變量名:采用匈牙利標記法:在以Pascal標記法的變量前附加小寫序列說明該變量的類型享幽。在Java我們一般使用匈牙利標記法,基本結(jié)構(gòu)為scope_typeVariableName拾弃,它 使用1-3字符前綴來表示數(shù)據(jù)類型值桩,3個字符的前綴必須小寫,前綴后面是由表意性強的一個單詞或多個單詞組成的名字豪椿,而且每個單詞的首寫字母大寫奔坟,其它字 母小寫,這樣保證了對變量名能夠進行正確的斷句搭盾。例如咳秉,定義一個整形變量,用來記錄文檔數(shù)量:intDocCount鸯隅,其中int表明數(shù)據(jù)類型澜建,后面為表 意的英文名向挖,每個單詞首字母大寫。這樣炕舵,在一個變量名就可以反映出變量類型和變量所存儲的值的意義兩方面內(nèi)容何之,這使得代碼語句可讀性強、更加容易理解咽筋。 byte溶推、int、char奸攻、long蒜危、float、 double睹耐、boolean和short辐赞。
定義方法
[修飾符] 方法返回值類型 方法名(形參列表) {….方法體….}
- 修飾符:同成員變量定義
- 返回值類型:可以是基本類型或者引用類型,需要配合return疏橄。無返回值使用void
- 方法名:方法的名字的第一個單詞應以小寫字母作為開頭占拍,后面的單詞則用大寫字母開頭。 同變量命名規(guī)則捎迫。
- static修飾符修飾的成員屬于類本身晃酒。類變量、類方法窄绒、靜態(tài)變量贝次、靜態(tài)方法。靜態(tài)成員不能直接訪問非靜態(tài)成員彰导。如果靜態(tài)方法中需要訪問普通方法蛔翅,只能重新創(chuàng)建一個對象。作為調(diào)用者調(diào)用改方法位谋。
定義構(gòu)造器
[修飾符] 構(gòu)造器名(形參列表) {……}
- 修飾符:可以是public protected private
- 構(gòu)造器名必須和類名相同
- 類的構(gòu)造器如果定義了返回值類型或者加void山析,這個構(gòu)造器將被作為方法處理。
- 實際上掏父,類的構(gòu)造器是有返回值的笋轨,當使用new關(guān)鍵字來調(diào)用構(gòu)造器時,構(gòu)造器返回該類的實例赊淑。構(gòu)造器的返回值是隱式的
創(chuàng)建對象
// 定義p變量的同時爵政,為p變量賦值
// 引用型變量p里存放的僅僅是一個引用,它指向?qū)嶋H的對象
// 引用變量存放在棧內(nèi)存中,實際對象存放在堆內(nèi)存中
Person p = new Person();
// 多個引用變量指向同一個對象
Person p2=p;
如果堆內(nèi)存里的對象沒有任何變量指向該對象陶缺,這個對象就變成了垃圾钾挟,Java的垃圾回收機制將回收該對象,釋放該對象所占的內(nèi)存饱岸。即切斷引用變量和對象之間的關(guān)系掺出,將引用變量賦值為null徽千。
調(diào)用實例/方法
類.類變量|方法
實例.實例變量|方法
對象的this引用
this 關(guān)鍵字總是指向調(diào)用該方法的對象.
- 構(gòu)造器中引用該構(gòu)造器執(zhí)行初始化的對象
- 在方法中引用調(diào)用該方法的對象
特別的,Java允許允許一個成員直接調(diào)用另一個成員蛛砰,此時可以省略this前綴罐栈。一般來說,如果調(diào)用static修飾的成員時省略了主調(diào)(調(diào)用成員泥畅、方法的對象)荠诬,默認使用該類作為主調(diào);如果調(diào)用沒有用static修飾的成員時省略了主調(diào)位仁,默認使用this作為主調(diào).
如果普通方法中有個局部變量和成員變量同名柑贞,程序又需要在該方法中訪問被覆蓋的成員變量,則必須使用this前綴聂抢。
方法
Java中方法不能獨立存在钧嘶,必須屬于一個類或?qū)ο蟆?/p>
方法的參數(shù)傳遞機制
值傳遞:將實際參數(shù)值的副本傳入方法內(nèi),參數(shù)本身不會受到任何影響琳疏。
特別注意:引用類型的參數(shù)傳遞仍然是值傳遞有决。
class DataWrap
{
int a;
int b;
}
public class ReferenceTransferTest
{
public static void swap(DataWrap dw)
{
// 下面三行代碼實現(xiàn)dw的a、b兩個成員變量的值交換空盼。
// 定義一個臨時變量來保存dw對象的a成員變量的值
int tmp = dw.a;
// 把dw對象的b成員變量值賦給a成員變量
dw.a = dw.b;
// 把臨時變量tmp的值賦給dw對象的b成員變量
dw.b = tmp;
System.out.println("swap方法里书幕,a成員變量的值是"
+ dw.a + ";b成員變量的值是" + dw.b);
// 把dw直接賦為null揽趾,讓它不再指向任何有效地址台汇。
dw = null;
}
public static void main(String[] args)
{
DataWrap dw = new DataWrap();
dw.a = 6;
dw.b = 9;
swap(dw);
System.out.println("交換結(jié)束后,a成員變量的值是"
+ dw.a + "篱瞎;b成員變量的值是" + dw.b);//9,6;9,6
}
}
上面的程序中dw僅僅是一個引用變量苟呐,在main棧區(qū)會創(chuàng)建dw引用變量,之后再swap棧區(qū)會創(chuàng)建其副本俐筋,完成對堆內(nèi)存中的對象的引用牵素。在swap方法中操作堆內(nèi)存中的DataWrap對象,因此最后在main方法中dw引用的DataWrap成員變量的值交換了澄者。
形參個數(shù)可變的方法
如果在定義方法時,在最后一個參數(shù)的類型后增加三點…,則表明該形參接受多個參數(shù)值,多個參數(shù)值被當成數(shù)組傳入笆呆。長度可變的形參只能位于最后一個參數(shù),并一個方法里只能有一個可變長度的參數(shù)。
public class Varargs
{
// 定義了形參個數(shù)可變的方法
public static void test(int a , String... books)
{
// books被當成數(shù)組處理
for (String tmp : books)
{
System.out.println(tmp);
}
// 輸出整數(shù)變量a的值
System.out.println(a);
}
public static void main(String[] args)
{
// 調(diào)用test方法
test(5 , "瘋狂Java講義" , "輕量級Java EE企業(yè)應用實戰(zhàn)");
}
}
也可以使用數(shù)組形參來定義方法
public static void test(int a , String[] books)
但是調(diào)用時必須傳入數(shù)組:
test(5 , new String[] {"瘋狂Java講義" , "輕量級Java EE企業(yè)應用實戰(zhàn)"});
遞歸方法
遞歸就是在方法中再次調(diào)用自己闷哆。遞歸一定要向已知方向遞歸.
方法重載
Java 允許在一個類里定義多個同名方法,只要形參列表不同即可.
成員變量和局部變量
- 成員變量指的是在類范圍里定義的變量;局部變量指的是在一個方法內(nèi)定義的變量单起。
- 不管是成員變量還是局部變量都遵守相同的命名規(guī)則抱怔。
- 局部變量可分為三種:形參、方法局部變量嘀倒、代碼塊局部變量屈留,除了形參外,其他局部變量都必須顯式地初始化局冰。
- 成員變量不用顯式初始化,只要定義了一個類屬性或?qū)嵗龑傩?系統(tǒng)會在類的準備階段或者創(chuàng)建該類實例時,進行默認初始化灌危,成員變量默認初始化是復制規(guī)則與數(shù)組動態(tài)初始化時數(shù)組元素賦值規(guī)則完全相同康二。
- Java允許局部變量和成員變量重名。這樣局部變量會覆蓋成員變量,可以通過this來調(diào)用實例的屬性.
成員變量初始化
- 當類被加載時,類成員就在內(nèi)存中分配了一塊空間勇蝙,并指定默認值沫勿。
- 當對象被創(chuàng)建時,實例成員就在內(nèi)存中分配了內(nèi)存空間味混,并指定初始值产雹。
- 為實例變量賦值。
實例變量與實例共存亡翁锡;類變量與類本身共存亡蔓挖。
Person p1 = new Person();
Person p2 = new Person();
// 為實例變量賦值
p1.name = "張三";
p2.name = "孫悟空";
//為類變量賦值
p1.eyeNum = 2;
p2.eyeNum = 3;
局部變量的初始化
局部變量僅在方法內(nèi)有效。它總是保存在所在方法的棧內(nèi)存中馆衔。如果局部變量是基本類型的變量瘟判,直接把這個變量的值保存在該變量對應的內(nèi)存中;如果是引用變量角溃,則局部變量保存的是地址拷获,通過該地址引用到該變量實際引用的對象或數(shù)組。棧內(nèi)存中的變量無須系統(tǒng)垃圾回收开镣,當方法執(zhí)行完成時刀诬,局部變量便會自動銷毀。
變量使用
局部變量的作用范圍越小邪财,它在內(nèi)存中停留的時間就越短陕壹,程序運行性能就越好。
隱藏和封裝
訪問控制符
- private 私有的树埠。在同一個類里能被訪問糠馆。
- default 默認的,不加任何訪問控制符怎憋。包訪問權(quán)限又碌。
- protected 受保護的。子類中也能訪問
- public 公共的绊袋。在任何地方都可以訪問
基本原則:
- 絕大部分成員變量應該使用private修飾毕匀,只有static修飾的、類似全局變量的成員變量才能使用public修飾癌别。工具方法也應該使用private修飾皂岔。
- 父類的大部分方法可能僅希望被其子類重寫,而不希望被外界直接調(diào)用展姐,應該使用protected修飾躁垛。
- 希望暴露出來給其他類自由調(diào)用的方法應該使用public修飾剖毯。因此類的構(gòu)造器通過使用public修飾,從而允許在其他地方創(chuàng)建該類的實例教馆。
package
Java允許將一組功能相關(guān)的類放在一個package下逊谋。
包名應該全部是小寫字母,可以使用公司域名倒寫來作為包名土铺。
javac -d . Hello.java
以上是在當前路徑下編譯改文件胶滋,結(jié)果保存在當前路徑新建的包文件夾中。-d用于設(shè)置編譯生成class文件的保存位置舒憾。
java lee.Hello
以上是執(zhí)行時的命令镀钓。注意優(yōu)先搜索CLASSPATH下的子路徑,然后按照與包層次對應的目錄結(jié)構(gòu)查找class文件镀迂。
當需要導入兩個包包含同一個名稱的類時丁溅,不能使用import,需要使用類的全名
java.sql.Date d = new java.sql.Date();
import 引入包格式探遵。分為兩種:
- 非靜態(tài)導入窟赏,導入的是包下所有的類。如:
import package.subpackage.*
; - 靜態(tài)導入箱季,導入的是類的靜態(tài)屬性涯穷。如:
import static package.className.*
;
java常用包
- java.lang.*,系統(tǒng)自動導入該包下所有類
- java.util.*, 工具類、集合框架
- java.net.* ,
- java.io.*,
- java.text.*,
- java.sql.*,
- java.awt.*,
- java.swing.*.
構(gòu)造器
構(gòu)造器是一個特殊的方法藏雏,用于在創(chuàng)建對象時執(zhí)行初始化拷况。注意:當系統(tǒng)執(zhí)行構(gòu)造器的執(zhí)行體之前,系統(tǒng)已經(jīng)創(chuàng)建了一個對象掘殴,只是這個對象還不能被外部程序訪問赚瘦。
構(gòu)造器重載
構(gòu)造器的重載和方法的重載一樣,都是方法名相同奏寨,形參列表不相同起意。在構(gòu)造器中可通過this來調(diào)用另外一個重載的構(gòu)造器。
類的繼承
Java的繼承是單繼承病瞳,每個子類最多只有一個直接父類揽咕。
子類繼承父類的語法格式如下:修飾符 class subclass extends superclass {}
子類擴展了父類,將可以獲得父類的全部屬性和方法套菜,但不能獲得父類構(gòu)造器.
重寫父類方法
方法的重寫要遵循“兩同兩小一大”亲善,指的是:方法名相同,形參列表相同逗柴。返回值類型更小或相同蛹头,拋出的異常更小或相同,訪問控制權(quán)限要更大。
如果父類方法是private的權(quán)限掘而,子類無法重寫該方法。
重載與重寫于购?
重載是overload袍睡,重寫是override。前者是同一個類的多個同名方法之間肋僧,后者是發(fā)生在子類與父類同名方法之間斑胜。當然父類與子類也可能發(fā)生重載:子類進程父類方法,并定義了一個與父類方法函數(shù)名相同嫌吠,參數(shù)列表不同的方法止潘。
super
通過關(guān)鍵字super
來調(diào)用父類的被覆蓋的實例方法或隱藏屬性。沒有被覆蓋的屬性可以直接調(diào)用辫诅。
調(diào)用父類構(gòu)造器
子類構(gòu)造器總會調(diào)用父類構(gòu)造器凭戴。
如果子類構(gòu)造器沒有顯式使用super調(diào)用父類構(gòu)造器,子類構(gòu)造器默認會調(diào)用父類無參數(shù)的構(gòu)造器炕矮。
創(chuàng)建一個子類實例時么夫,總會先調(diào)用最頂層父類的構(gòu)造器。
多態(tài)
Java 引用變量有兩個類型:一個是編譯時的類型肤视,一個是運行時的類型档痪,編譯時的類型由聲明該變量時使用的類型決定,運行時的類型由實際賦給該變量的對象決定邢滑。如果編譯時類型和運行時的類型不一致腐螟,這就有可能出現(xiàn)所謂的多態(tài)。
兩個相同類型的引用變量困后,由于它們實際引用的對象的類型不同乐纸,當它們調(diào)用同名方式時,可能呈現(xiàn)出多種行為特征操灿,這就是多態(tài)锯仪。
class BaseClass
{
public int book = 6;
public void base()
{
System.out.println("父類的普通方法");
}
public void test()
{
System.out.println("父類的被覆蓋的方法");
}
}
public class SubClass extends BaseClass
{
//重新定義一個book實例變量隱藏父類的book實例變量
public String book = "輕量級Java EE企業(yè)應用實戰(zhàn)";
public void test()
{
System.out.println("子類的覆蓋父類的方法");
}
public void sub()
{
System.out.println("子類的普通方法");
}
public static void main(String[] args)
{
// 下面編譯時類型和運行時類型完全一樣,因此不存在多態(tài)
BaseClass bc = new BaseClass();
// 輸出 6
System.out.println(bc.book);
// 下面兩次調(diào)用將執(zhí)行BaseClass的方法
bc.base();
bc.test();
// 下面編譯時類型和運行時類型完全一樣趾盐,因此不存在多態(tài)
SubClass sc = new SubClass();
// 輸出"輕量級Java EE企業(yè)應用實戰(zhàn)"
System.out.println(sc.book);
// 下面調(diào)用將執(zhí)行從父類繼承到的base()方法
sc.base();
// 下面調(diào)用將執(zhí)行從當前類的test()方法
sc.test();
// 下面編譯時類型和運行時類型不一樣庶喜,多態(tài)發(fā)生
BaseClass ploymophicBc = new SubClass();
// 輸出6 —— 表明訪問的是父類對象的實例變量
System.out.println(ploymophicBc.book);
// 下面調(diào)用將執(zhí)行從父類繼承到的base()方法
ploymophicBc.base();
// 下面調(diào)用將執(zhí)行從當前類的test()方法
ploymophicBc.test();//子類覆蓋的父類方法
// 因為ploymophicBc的編譯類型是BaseClass,
// BaseClass類沒有提供sub方法,所以下面代碼編譯時會出現(xiàn)錯誤救鲤。
// ploymophicBc.sub();
}
}
第三個引用變量比較特殊久窟,編譯時類型是BaseClass,運行時類型是SubClass本缠。當調(diào)用引用變量的Test方法時斥扛,實際執(zhí)行的是SubClass中覆蓋后的Test方法。
子類對象賦給父類引用變量,是向上轉(zhuǎn)型稀颁,系統(tǒng)自動完成芬失。當調(diào)用引用變量的方法時總是表現(xiàn)出子類方法的行為特征。而不是父類匾灶。對于子類中新的方法因為編譯時為BaseClass就無法調(diào)用了棱烂,所以運行時無法調(diào)用。
實例變量不具備多態(tài)阶女,輸出BaseClass類的實例變量颊糜。官方的話:引用變量訪問實例變量時,總是試圖訪問編譯時類型所定義的成員變量秃踩。
引用變量的類型轉(zhuǎn)換
強制類型轉(zhuǎn)換: 類型轉(zhuǎn)換運算符是小括號衬鱼,語法如下(type)variable
;
注意:
- 基本類型之間的轉(zhuǎn)換只能在數(shù)值類型之間進行憔杨。數(shù)值類型和布爾類型之間不能進行類型轉(zhuǎn)換
- 引用類型之間的轉(zhuǎn)換只能在具有繼承關(guān)系的兩個類型之間進行鸟赫。如果試圖將一個父類實例轉(zhuǎn)換成子類類型,則這個對象必須實際上是子類實例才行消别。否則會引起ClassCastException惯疙。可以使用
instanceof
判斷是否可以成功轉(zhuǎn)換妖啥。
instanceof
前一個操作通常是一個引用類型的變量霉颠,后一個操作通常是一個類(也可以是接口)。如果是返回true 否返回false荆虱。
public class InstanceofTest
{
public static void main(String[] args)
{
// 聲明hello時使用Object類蒿偎,則hello的編譯類型是Object,
// Object是所有類的父類, 但hello變量的實際類型是String
Object hello = "Hello";
// String與Object類存在繼承關(guān)系怀读,可以進行instanceof運算诉位。返回true。
System.out.println("字符串是否是Object類的實例:"
+ (hello instanceof Object));
System.out.println("字符串是否是String類的實例:"
+ (hello instanceof String)); // 返回true菜枷。
// Math與Object類存在繼承關(guān)系苍糠,可以進行instanceof運算。返回false啤誊。
System.out.println("字符串是否是Math類的實例:"
+ (hello instanceof Math));
// String實現(xiàn)了Comparable接口岳瞭,所以返回true。
System.out.println("字符串是否是Comparable接口的實例:"
+ (hello instanceof Comparable));
String a = "Hello";
// // String類與Math類沒有繼承關(guān)系蚊锹,所以下面代碼編譯無法通過
// System.out.println("字符串是否是Math類的實例:"
// + (a instanceof Math));
}
}
初始化塊
格式:[修飾符]{//可執(zhí)行代碼}
修飾符只能是static瞳筏。static修飾的初始化塊稱為靜態(tài)初始化塊。
系統(tǒng)總是先調(diào)用初始化塊牡昆,然后是構(gòu)造器姚炕。初始化塊只在創(chuàng)建Java對象時隱式執(zhí)行。
初始化塊是構(gòu)造器的補充,是一段固定執(zhí)行的代碼柱宦,不能接受參數(shù)些椒。實際上初始化塊在使用javac命令編譯類之后,初始化塊會被還原到每個構(gòu)造器中掸刊,且位于構(gòu)造器所有代碼的前面摊沉。
順序:以此執(zhí)行父類的初始化塊、構(gòu)造器痒给、初始化塊、構(gòu)造器...
靜態(tài)初始化塊
靜態(tài)初始化塊將在類初始化階段執(zhí)行靜態(tài)初始化塊骏全,比普通初始化塊優(yōu)先級更高苍柏。同樣也需要上溯父類的靜態(tài)初始化塊。
class Root
{
static{
System.out.println("Root的靜態(tài)初始化塊");
}
{
System.out.println("Root的普通初始化塊");
}
public Root()
{
System.out.println("Root的無參數(shù)的構(gòu)造器");
}
}
class Mid extends Root
{
static{
System.out.println("Mid的靜態(tài)初始化塊");
}
{
System.out.println("Mid的普通初始化塊");
}
public Mid()
{
System.out.println("Mid的無參數(shù)的構(gòu)造器");
}
public Mid(String msg)
{
// 通過this調(diào)用同一類中重載的構(gòu)造器
this();
System.out.println("Mid的帶參數(shù)構(gòu)造器姜贡,其參數(shù)值:"
+ msg);
}
}
class Leaf extends Mid
{
static{
System.out.println("Leaf的靜態(tài)初始化塊");
}
{
System.out.println("Leaf的普通初始化塊");
}
public Leaf()
{
// 通過super調(diào)用父類中有一個字符串參數(shù)的構(gòu)造器
super("瘋狂Java講義");
System.out.println("執(zhí)行Leaf的構(gòu)造器");
}
}
public class Test
{
public static void main(String[] args)
{
new Leaf();
new Leaf();
}
}
靜態(tài)初始化塊與靜態(tài)成員變量定義的執(zhí)行順序與源程序中排列順序相同试吁。