Java程式設計基礎-面向物件(中)
本章承接Java程式設計基礎-面向物件(上)一文。
一、static關鍵字
在java中,定義了一個static關鍵字,它用於修飾類的成員,如成員變數、成員方法以及程式碼塊等,被static修飾的成員具備一些特性。
1、static關鍵字的特點:
a)隨著類的載入而載入,也就是說,靜態會隨著類的消失而消失,說明它的生命週期最長。
b)優先於物件存在,明確一點,靜態是先存在的,物件是後存在的。
c)被類的所有物件共享。
d)可以通過類名呼叫。
2、靜態變數
在一個java類中,可以使用static關鍵字來修飾成員變數,該變數被稱作靜態變數。靜態變數被所有例項共享,可以使用”類名.變數名”的形式來訪問。
程式碼示例:
注意:static關鍵字只能用於修飾成員變數,不能用於修飾區域性變數,否則編譯會報錯。public class Student { static String schoolName;//定義靜態變數schoolName } public class TestStudent { public static void main(String[] args) { Student stu1=new Student();//建立學生物件 Student stu2=new Student(); Student.schoolName="清華大學";//為靜態變數賦值 System.out.println("我的學校是"+stu1.schoolName); System.out.println("我的學校是"+stu2.schoolName); } }
什麼時候定義靜態變數(類變數)呢?
當物件中出現共享資料時,該資料被靜態所修飾,物件中特有的資料要定義成非靜態儲存於堆記憶體中。
3、靜態方法
概念:不建立物件的情況下就可以呼叫某個方法,換句話說,也就是使該方法不必和物件綁在一起。只需在類中定義的方法前加上static關鍵字即可。同靜態變數一樣,靜態方法可以使用”類名.方法名”的方式來訪問,也可以通過類的例項物件來訪問。
注意:在一個靜態方法中只能訪問用static修飾的成員,原因在於沒有被static修飾的成員需要先建立物件才能訪問,而靜態方法在被呼叫時可以不建立任何物件。
什麼時候定義靜態函式?
當功能內部沒有訪問到非靜態資料(物件的特有資料),那麼該功能可以定義成靜態的。
靜態的使用注意事項:
a)靜態方法只能訪問靜態成員(包括方法和變數),非靜態方法既可以訪問靜態也可以訪問非靜態。
b)靜態方法中不可以定義this,super關鍵字。因為靜態優先於物件存在,所以靜態方法中不可以出現this。
c)主函式是靜態的。
靜態利弊:
利處:對物件的共享資料進行單獨空間的儲存,節省空間,沒有必要每一個物件中都儲存一份。可以直接被類名呼叫。
弊端:生命週期過長,訪問出現侷限性。
4、靜態程式碼塊
1、概念:在java類中,使用一對大括號包圍起來的若干行程式碼被稱為一個程式碼塊,用static關鍵字修飾的程式碼塊被稱為靜態程式碼塊。當類被載入時,靜態程式碼塊會被執行,由於類只加載一次,因此靜態程式碼塊只執行一次。在程式中,通常會使用靜態程式碼塊來對類的成員變數進行初始化。
格式:
static
{
靜態程式碼塊中的執行語句
}
特點:隨著類的載入而執行,只執行一次,並優於主函式。用於給類進行初始化的。
2、程式碼塊分類:
a)區域性程式碼塊:作用是控制變數的生命週期。
在程式中,當我們已經使用完 x 後,並且在接下來的程式碼中,不會再用到x,那麼就沒必要讓x 在記憶體中佔用空間了,這用情況下,可以使用 區域性程式碼塊,將x及其所涉及到的區域封裝為區域性程式碼塊,他們在程式執行中順序不變,只是在執行完成後消失。
b)構造程式碼塊:作用是可以給所有物件初始化,物件一建立就執行,而且優先於建構函式執行。存在於類中,當類中的構造方法以過載的形式存在時,並且有共同成員變數或共同方法時,可以通過構造程式碼塊進行初始化,這樣可以減少程式碼的重複。構造程式碼塊中定義的是不同物件共性的初始化內容。
c)靜態程式碼塊:作用是載入驅動器,當類中的方法都被靜態化,並且構造方法被private了,這時我們不能再將這個類例項化,然而又想讓這個類增加一些屬性,就可以使用靜態程式碼塊。靜態程式碼塊的特點是隨著類的載入而執行,而且只執行一次,且優於主函式執行。
d)同步程式碼塊,主要出現在多執行緒中。
程式碼示例:
class StaticCode {
int num = 9;
StaticCode() {
System.out.println("b");
}
static // 靜態程式碼塊
{
System.out.println("a");
}
// 構造程式碼塊
{
System.out.println("c" + this.num);
}
StaticCode(int x) {// 建構函式
System.out.println("d");
}
public static void show() {
System.out.println("show run");
}
}
public class StaticCodeDemo {
public static void main(String[] args) {
new StaticCode(4);
}
}
執行結果為:
a
c9
d
二、內部類
1、概念:內部類顧名思義是指在一個類的內部再定義一個類。
注意:內部類是個編譯時的概念,一旦編譯成功後,它就與外部類屬於兩個完全不同的類(當然他們之間還是有聯絡的)。對於一個名為OuterClass的外部類和一個名為InnerClass的內部類,在編譯成功後,會出現這樣兩個class檔案:OuterClass.class和OuterClass$InnerClass.class。
2、分類:
java內部類主要分為四種:成員內部類、區域性內部類、靜態內部類和匿名內部類。
a)成員內部類:成員內部類是最普通的內部類,在成員內部類中可以訪問外部類的所有成員,包括私有。之所以可以訪問,是因為內部類中持有了一個外部類的引用,外部類名.this.外部類要訪問內部類的所有成員,必須要建立內部類物件。
注意:成員內部類中不能存在任何用static修飾的變數和方法;成員內部類是依附於外部類的,所以只有先建立了外部類才能夠建立內部類。
建立內部類物件的具體語法格式:外部類型別.內部類型別 變數名=new 外部類名().new 內部類名();
public class OuterClass {// 外部類
private String str;// 外部類中的屬性
public void outerDisplay() {// 外部類中的方法
System.out.println("outerClass...");
}
public class InnerClass {// 內部類
public void innerDisplay() {// 內部類中的方法
// 使用外部類的屬性
str = "abcd";
System.out.println(str);
// 呼叫外部類中的方法
outerDisplay();
}
}
/* 推薦使用getxxx()來獲取成員內部類,尤其是該內部類的建構函式無引數時 */
public InnerClass getInnerClass() {
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
b)區域性內部類:它巢狀在方法和作用域內,它只能在該方法和屬性中被使用,出了方法和作用域就會失效。
定義在方法中:
public class Parcel5 {
public Destionation destionation(String str){
class PDestionation implements Destionation{
private String label;
private PDestionation(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestionation(str);
}
public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destionation d = parcel5.destionation("chenssy");
}
}
定義在作用域內:
public class Parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("chenssy");
String string = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}
c)靜態內部類:可以使用static關鍵字來修飾一個成員內部類,該內部類被稱作靜態內部類,它可以在不建立外部類物件的情況下被例項化。
注意:在靜態內部類中只能訪問外部類的靜態成員。在靜態內部類中可以定義靜態成員,而在非靜態的內部類中不允許定義靜態的成員。
d)匿名內部類:在做swing程式設計時,我們常用匿名內部類的方式來繫結事件。
注意幾點:
a)匿名內部類是沒有訪問修飾符的。
b)new 匿名內部類,這個類首先是要存在的。
c)當所在方法的形參需要被匿名內部類使用,那麼這個形參就必須為final。
d) 匿名內部類是沒有構造方法的。因為它連名字都沒有何來構造方法。
三、匿名物件
1、匿名物件:沒有名字的實體,也就是該實體沒有對應的變數名引用。
2、匿名物件的用途:
當物件對方法進行一次呼叫的時候,可以使用匿名物件對程式碼進行簡化。呼叫完之後,該物件不再使用,並被回收。為什麼只對方法,而不呼叫屬性呢?因為匿名物件呼叫屬性沒意義。如果物件要多成員進行多次呼叫,必須給物件起個名字。不能在使用匿名物件。匿名物件可以實際引數進行傳遞。
3、匿名物件的簡單演示
new 類名().方法名();
注意:
a)匿名物件設定的屬性永遠無法獲取? 沒有引用變數指向那個物件。
b)任何兩個匿名物件使用==比較,永遠返回false。
c)匿名物件主要應用於實參。
四、類的繼承
1、繼承概念:在程式中,繼承描述的是事物之間的所屬關係,通過繼承可以使多種事物之間形成一種關係體系。在java中,類的繼承是指在一個現有類的基礎上去構建一個新的類,構建出來的新類被稱作子類,現有的類被稱作父類,子類會自動擁有父類所有可繼承的屬性和方法。在程式中,如果想宣告一個類繼承另一個類,需要使用extends關鍵字。
格式: class 子類名 extends 父類名{}
繼承的好處:提高了程式碼的複用性,提高了程式碼的維護性,讓類與類之間產生了關係,是多型的前提。
在類的繼承中,需要注意的問題:
a)在java中,類只支援單繼承,不允許多重繼承,也就是說一個類只能有一個直接父類。因為多繼承容易帶來安全隱患:當多個父類中定義了相同功能,當功能內容不同時,子類物件不確定要執行哪一個。例如:C類不能同時繼承A類和B類。
b)多個類可以繼承一個父類。
c)在java中,多層繼承是可以的,即一個類的父類可以再去繼承另外的父類,例如:C類繼承自B類,而B類又可以去繼承自A類,這時,C類也可以稱作A類的子類。
程式碼示例:
class Animal {
String name;
public void shout() {
System.out.println("動物發出叫聲");
}
}
class Cat extends Animal {
public void catchFish() {
System.out.println("貓在抓魚");
}
}
public class Demo {
public static void main(String[] args) {
Cat c = new Cat();
c.shout();//呼叫父類的shout()方法
}
}
注意:子類只能繼承父類所有非私有的成員(成員方法與成員變數);不能繼承父類的構造方法,但可以通過super關鍵字來訪問父類的構造方法;不要為了部分功能而去繼承。2、重寫父類方法:
在繼承關係中,子類會自動繼承父類中定義的方法,但有時在子類中需要對繼承的方法進行一些修改,即對父類的方法進行重寫。需要注意的是,在子類中重寫的方法需要和父類被重寫的方法具有相同的方法名、引數列表和返回值型別。(也稱方法覆蓋,方法複寫)
程式碼示例:
class Animal {
String name;
public void shout() {
System.out.println("動物發出叫聲");
}
}
class Cat extends Animal {
public void shout() {//重寫父類的shout()方法
System.out.println("貓咪發出叫聲");
}
}
public class Demo {
public static void main(String[] args) {
Cat c = new Cat();
c.shout();
}
}
方法重寫時要遵循的規則:“三同一小一大”規則
“三同”即方法名相同,形參列表相同、返回值型別相同;
“一小”子類方法宣告丟擲的異常類應比父類方法宣告丟擲的異常類更小或相等;
“一大”指的子類方法的訪問許可權應比父類方法更大或相等;
覆蓋方法和被覆蓋方法要麼都是類方法,要麼都是例項方法,不能一個是類方法,一個是例項方法。
注意:
a)子類重寫父類方法時,不能使用比父類中被重寫的方法更嚴格的訪問許可權。
b)父類中的私有方法不能被重寫,父類私有,子類根本無法繼承。
c)父類靜態方法,子類也必須通過靜態方法重寫。
d)子類重寫父類方法的時候,最好宣告的一模一樣。
3、重寫和過載的區別:
a)方法的重寫:要有繼承關係,是在子父類方法中宣告相同(方法名和引數列表都相同)。與返回值型別有關,覆蓋時,子類方法的訪問許可權不能小於父類方法的訪問許可權,靜態只能覆蓋靜態。
b)方法的過載:在同一個類中,方法名相同,引數列表不同,與返回值型別無關。
五、super關鍵字
當子類重寫父類方法後,子類物件將無法訪問父類被重寫的方法,為了解決這個問題,在java中專門提供了一個super關鍵字用於訪問父類的成員。例如:訪問父類的成員變數、成員方法和構造方法。
1、使用super關鍵字呼叫父類的成員變數和成員方法。
具體格式:
super.成員變數
super.成員方法({引數1},{引數2..})
程式碼示例:
class Animal // 定義Animal類
{
String name = "動物";
void shout()// 定義動物叫的方法
{
System.out.println("動物發出叫聲");
}
}
class Dog extends Animal// 定義Dog類繼承Animal類
{
String name = "犬類";
void shout() {
super.shout();// 訪問父類的成員方法
}
void printName() {
System.out.println("name=" + super.name);// 訪問父類的成員變數
}
}
class Demo {
public static void main(String[] args) {
Dog dog = new Dog();// 建立Dog的例項物件
dog.shout();// 呼叫dog重寫shout()方法
dog.printName();// 呼叫dog物件的printName()方法
}
}
2、
使用super關鍵字呼叫父類的構造方法。super.([引數1],[引數2..])通過super呼叫父類的構造方法程式碼必須位於子類構造方法的第一行並且只能出現一次。
程式碼示例:
class Animal // 定義Animal類
{
String name;
public Animal(String name)// 定義Animal類的構造方法
{
this.name = name;
}
}
class Dog extends Animal// 定義Dog類繼承Animal類
{
public Dog() {
super("柴犬");// 呼叫父類有參的構造方法
}
public void show() {
System.out.println("我是一隻" + name);
}
}
class Demo {
public static void main(String[] args) {
Dog dog = new Dog();// 建立Dog的例項物件
dog.show();
}
}
3、this和super的區別:a)呼叫成員變數:this呼叫本類的成員變數,super呼叫父類的成員變數。
b)呼叫構造方法:this()呼叫本類的構造方法,super()呼叫父類的構造方法。
c)呼叫成員方法:this呼叫本類的成員方法,super呼叫父類的成員方法。
d)建構函式沒有覆蓋,this和super不能同時存在。
六、final關鍵字
final關鍵字可以用於修飾類、變數和方法。它有“這是無法改變的”或者“最終”的含義,因此被final修飾的類、變數和方法具有以下特性:
a)final修飾的類不能被繼承。也就是不能夠派生子類,為最終類。
b)final修飾的方法不能被子類重寫。被final修飾的方法為最終方法。
c)final修飾的變數(成員變數和區域性變數)是常量(自定義常量),只能賦值一次。當在描述事物時,一些資料的出現值是固定的,那麼這時為了增強閱讀性,都給這些值起個名字,方便閱讀。而這個值不需要改變,所以加上final修飾。作為常量:常量的書寫規範所有字母都大寫,如果由多個單片語成,單詞間通過下劃線連線。