1. 程式人生 > >JavaSE面向物件(下)

JavaSE面向物件(下)

JavaSE面向物件(下)

初始化程式碼塊

普通初始化程式碼塊

又叫物件初始化程式碼塊
程式碼塊的執行是順序執行先定義先執行,語法如下

	{
		//可執行程式碼
	}

這個程式碼塊會出現在成員變數之前會在例項化物件時使用程式碼塊中的邏輯程式碼對對像進行初始化。

靜態初始化程式碼塊

靜態初始化程式碼塊又叫類初始化程式碼塊
語法如下

static{
		//可執行程式碼
}

靜態初始化程式碼塊是系統在初始化類時執行而不是建立物件來執行的。因此他比普通程式碼塊先執行。

final關鍵字

final 成員變數

成員變數是隨著物件的初始化而初始化的。也就是說當執行靜態程式碼塊
,普通初始化程式碼塊,構造器時可以給出例項變數初值,但是如果,用final修飾後在一開始的時候如果沒有給出預設值,那麼成員變數會一直是系統給出的預設值0,‘\u0000’,false,null。這些成員變數也就失去了存在的意義。因此Java與法規定:final修飾的成員變數必須由程式設計師顯示的指定初始值。

final修飾區域性變數

區域性變數中系統不會對其進行初始化,區域性變數必須由程式設計師顯示的初始化。因此在使用final修飾的區域性變數時,既可以在定義時指定預設初值,也可以不指定預設值,在後來的程式碼中對該final進行賦值,但是隻能賦值一次。

final修飾基本型別變數和引用型別變數的區別

當使用final修飾基本變數型別時,基本型別的值不能被改變,但引用型別只儲存了一個引用,final只能保證其儲存的地址不會被改變,即一直引用到同於一個物件。但這個物件不會被改變。

可執行“巨集替換”的final變數

在C/C++中大家都瞭解過巨集定義 #define他在程式碼編譯過程中會直接替換掉所定義的變數,Java中也會出現這個情況當
1.使用final修飾符修飾;
2.在定義時已經給出了初值;
3.該初值在編譯時已經確定。
符合上面三個條件時這個變數就相當於一個直接量。
比如:

final int a = 50;
System.out.println(a);

對於這個程式來說,a就根本不存在,實際是執行了
System.out.println(50);
這就相當於C/C++中的巨集定義。

final方法

用final修飾的方法不可以被重寫,如果出於某些原因不希望子類重寫父類的方法時可以用final修飾。(在Java中object類中的getClass()方法就是用final修飾的,Java不希望任何類重寫這個方法所以用final飾。),當你試圖重寫時,將會編譯錯誤。final定義的方法只是不能被重寫,但是可以被過載。

public class FinalOverload
{ public final void test(){} public final void test(String arg){} }

final類

final修飾的類不可以被繼承

public final class FinalClass {}
//下面的程式碼將出現編譯錯誤
class Sub extends FinalClass {}

抽象類

抽象類的引入

當我們編寫一個類時,我們通常會定義一些方法,但是當我們再寫父類時,我們只知道子類有這些方法,但是不知道這些子類的方法具體表現所以Java引入一個只有方法簽名沒有方法實現的抽象類
語法如下

修飾符 abstract class 類名{
}

抽象類和抽象方法的特點

1.抽象類中必須使用abstract修飾,抽象方法也必須使用abstract修飾,抽象方法也不能有方法體
2.抽象類不能例項化,無法使用new建立例項物件
3.含有抽象方法的類必須被定義為抽象類
4.抽象類可以包含成員變數、方法、初始化塊、構造器五種成分,主要適用於子類呼叫。

abstract使用注意事項

1.當abstract修飾類時,表明這個類只能被繼承;當使用abstract修飾方法時,表明這個方法必須由子類重寫。而final修飾的方法不能被重寫,final修飾的類不能被繼承,所以final和abstract不能同時出現。
2.abstract不能用於修飾成員變數和區域性變數。
3.static修飾方法時這個方法就屬於類,但是如果定義為抽象方法時,則會導致呼叫時出現錯誤,因此static和abstract不能同時修飾某個方法。(但是static和abstract並不絕對互斥,可以用來修飾內部類。)
4.abstract 修飾的方法必須被子類重寫才有意義所以,abstract和private不能同時修飾方法。

抽象類的作用

抽象類不能建立例項物件,只能當成父類被繼承。從語義的角度來看,抽象類是從多個具體類中抽象出的父類,具有更高層次的抽象。以這個抽象類作為其子類的模板從而避免了子類的隨意性。這就體現了一個模板模式。模板模式在面向物件的軟體中很常用的。

介面

介面的引入

抽象類可以給我們提供一個類的模板,但是我們如果將這種抽象抽象的更徹底,則可以提煉出一個更加特殊的“抽象類”——介面。為了體現事物功能的擴充套件性,Java中就提供了介面來定義這些額外功能,並不給出具體實現。

介面的語法

[修飾符] interface 介面名 extends 父介面1,父介面2...{
	//常量定義、
	//抽象方法定義
	//內部類、介面、列舉定義、
	//預設方法或類方法定義
}

介面特點

1.介面支援多繼承
2.介面不能被直接例項化(可以通過多型來例項化)
3.介面的子類,可以是抽象類但是意義不大。可以是具體類,但是要重寫介面中的所有抽象方法。
4.介面成員變數只能是常量,並且是靜態的。預設修飾符:public static final。介面沒有構造方法,成員方法只能是抽象方法,預設修飾符:public abstract

介面和抽象類

相同點
1.介面和抽象類都不能被例項化,位於繼承樹的頂端,只能被用來其他類的實現和繼承。
2.抽象類和介面都可以包含抽象方法,子類必須都實現這些方法。
不同點
1.介面相當於時系統與外界互動的視窗,介面體現的是一種規範。抽象類是系統中大多數子類的父類,它體現的是模板的概念。
2.介面中只能包含抽象方法、靜態方法和預設方法,不呢為普通方法提供實現;而抽象類可以包含普通方法。
3.介面只能定義靜態常量,不能定義普通成員變數;抽象類既可以定義普通成員變數,也可以是靜態常量。
4.介面中不可以包含構造器;抽象類裡可以包含構造器,並不是用來建立物件而是讓子類呼叫構造器來實現抽象類的初始化。
5.抽象類可以包含初始化塊;介面不可以包含初始化塊
6.抽象類只能單繼承;但是介面支援多繼承。

類與介面之間的關係

1.類與類
繼承關係,只能是單繼承但是支援多層繼承
2.類與介面
實現關係,可以單實現,也可以多實現。並且還可以在繼承一個類的同時實現多個介面。
3.介面與介面
繼承關係,可以單繼承,也可以多繼承

內部類

定義:

定義在其他類內部的類稱為內部類,把弄含內部類的類被稱作外部類。

作用

1.內部類提供了更好的封裝,可以把內部類隱藏在外部類之中,不允許同一個包中其他類訪問該類。
2.內部類成員可以直接訪問外部類的私有資料,因為內部類被當作外部類的成員,但是外部類不可以訪問內部類的具體細節。
3.內部類適合用於建立那些僅需使用一次的類。

內部類與外部類的區別

除了位置的區別外還有
1.內部類可以使用private、protect、static
2.非靜態內部類不可以擁有靜態成員。

非靜態內部類

定義方法如下

public class OuterClass{
	修飾符 class InClass{
	}
}

非靜態內部類注意事項

非靜態內部類不允許出現靜態方法、成員變數、靜態初始化塊。

靜態內部類

如果用static修飾一個內部類,則這個內部類就屬於外部類本身,而不屬於外部類的某個物件因此也叫類的內部類。

靜態內部類的特點

1.靜態內部類可以包含靜態成員,也可以包含非靜態成員。
2.靜態內部類不可以訪問外部類的非靜態成員。
3.靜態內部類屬於外部類的一個靜態成員因此外部類的所有方法所有初始化塊中可以使用靜態內部類來定義變數建立物件。
4.外部類不能直接訪問靜態內部類的成員,但是可以通過內部類的類名做呼叫者來訪問靜態內部類的類成員,也可以使用內部類物件作為呼叫者來訪問靜態內部類的實力成員。
5.介面中定義內部類時預設使用public static 修飾,也就是介面中只能定義靜態內部類。、

區域性內部類

如果一個類定義在方法中,則這個類就是區域性內部類,區域性內部類僅在該方法中有效。由於區域性內部類不能再方法以外的地方使用所以不能用static和訪問控制符修飾。
區域性內部類是一個很“雞肋”的語法,他只能在當前方法中使用。但是大部分時候,定義一個類之後,當然是希望多次複用這個類,因此在實際開發中很少用到

使用內部類

1.在外部類中使用內部類
外部類中使用內部類沒有太多的差別,但是不要再內部類的靜態成員中使用非靜態內部類。同時外部類內也可以繼承內部類的子類

public class SubClass extends Out.In{
	public SubClass(Out out){
		//通過傳入的out物件顯示呼叫In構造器
		out.super()
	}
 }

2.外部類外使用非靜態內部類
1)省略訪問控制符的內部類,只能被與外部類處於同一個包下的其他類訪問
2)使用protect修飾的內部類,可以被外部類處於同一個包中的其他類和外部類的子類訪問。
3)使用public修飾符的內部類,可以在任何地方訪問。
4)使用private修飾的內部類,外部類以外無法訪問
使用語法如下

	Out.In = new Out().new In();

5)非靜態內部類的子類不一定是一個內部類,它可以是一個外部類。但是非靜態內部類的子類例項一樣要保留一個指向其父類所在的外部類的物件的一個引用
3.在外部類以外使用靜態內部類
因為靜態內部類是外部類類相關的,因此建立靜態內部類物件時無需建立外部類物件。在外部類以外的地方建立靜態內部類例項的語法如下:

StaticOut.StaticiIn = new OuterClass.InnerConstructor();

不管是靜態內部類還是非靜態內部類,他們宣告變數的語法完全一樣。區別只在於靜態內部類只需要使用外部類呼叫構造器,而非靜態內部類必須使用外部類物件來呼叫構造器。
建立靜態內部類的子類也無需使用外部類的物件:

public class StaticSubClass extends StaticOut.StaticIn{ }

匿名內部類

定義:

匿名內部類適合那種只需要使用一次的類,建立匿名內部類的時候會立即建立一個這個類的例項,這個類的定義立即消失,匿名內部類不能重複使用。語法如下:

new 實現介面 | 父類構造器(實參列表){
	//匿名內部類的類體部分
}

從上面的定義可以看出,匿名內部類必須繼承一個父類或實現一個介面,但是最多隻能繼承一個父類,或者實現一個介面。

匿名內部類的使用注意事項:

1.匿名內部類不能是抽象類,因為系統在建立匿名內部類時,會立即建立匿名內部物件,因此不允許匿名內部類是抽象類。
2.匿名內部類不能有構造器。由於匿名內部類沒有類名,所以無法定義構造器,但是可以通過初始化程式碼塊來完成構造器完成的事情。

匿名內部類實現介面

interface Product{
	public double getPrice();
	public String getName();
}
public class AnonymousTest{
	public void test(Product p){
		System.out.println("Name:"+p.getName);
		System.out.println("Price:"+p.getPrice);
	}
	public static void main(String [] args){
		AnonymouseTest ta = new AnonymouseTest;
		//傳入匿名實現類的例項
		ta.test(new Prodect(){
			public double getPrice(){
				return 555.23;
			}
			public String getName(){
				return "鍵盤";
			}
		});
	}
}

由於test方法需要傳入一個Product物件作為引數,但是Product只是一個介面不能直接建立物件。我們就可以向上面一樣定義一個匿名內部類來實現。

匿名內部類實現抽象類

同樣的如果傳參需要一個抽象類的物件型別我們也可以使用匿名內部類的方法

package com.conpany.demo;
abstract  class Device{
    private String name;
    public abstract double getPrice();

    public Device() {
    }

    public Device(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class AnonymouseInner {
    public void test(Device d){
        System.out.println("Name:" + d.getName() + "Price:" + d.getPrice());
    }

    public static void main(String[] args) {
        AnonymouseInner ai = new AnonymouseInner();
        ai.test(new Device("鍵盤") {
            @Override
            public double getPrice() {
                
                return 78.9;
            }
        });
        Device d = new Device() {
            {
                System.out.println("我是初始化塊");
            }
            @Override
            public double getPrice() {
                return 82.2;
            }

            @Override
            public String getName() {
                return "滑鼠";
            }
        };
        ai.test(d);
    }
}