Java基礎之介面與抽象類及多型、內部類
final關鍵字
- 被其修飾的類,不能被繼承。
- 被其修飾的方法,不能被覆蓋。
- 被其修飾的變數,是一個常量,不能被修改,所以定義時必須初始化(和C++的const類似)。
一般有final,會搭配static使用。如
final static double PI = 3.14;
- 常量的命名規則 --> 所有字母大寫,多個單詞,中間用下劃線連線。
抽象類
貓和狗有共性,將共性抽取出來,放入Animal中,Animal是抽象的(想象不出實體是什麼)。
public abstract class Animal {
// 抽象類也有建構函式,給子類初始化用
public Animal() {
System.out.println("我們都是動物");
}
public abstract void eat();
}
// 也可以存在非抽象函式
public void sleep() {
System.out.println("睡覺了zzz~");
}
}
class Cat extends Animal {
Cat() {
System.out.println("我是一隻小貓咪");
}
@Override
public void eat() {
System.out.println("小貓吃魚");
}
}
class Dog extends Animal {
Dog() {
System.out.println("我是一條小狗狗");
}
@Override
public void eat() {
System.out.println("狗狗啃骨頭");
}
}
public class Demo {
public static void main(String args[]) {
public static void main(String[] args) {
Animal a = new Cat();
a.eat();
a.sleep();
// 列印如下內容
// 我們都是動物
// 我是一隻小貓咪
// 小貓吃魚
// 睡覺了zzz~
}
}
因為類Cat、類Dog或者其他類要繼承類Animal,且類Animal的show()是抽象的未被定義的,子類必須覆蓋父類的方法,定義自己特有的eat()
方法(貓和狗吃的東西不全一樣),故在抽象類中的方法只是宣告。
抽象類的特點
- 方法只有宣告沒有實現,為抽象方法,修飾符abstract,抽象方法必須在抽象類中。
- 抽象類不可以例項化,即不能new。因為呼叫抽象方法沒有意義。
- 子類必須覆蓋抽象類的所有抽象方法後,該子類才能例項化,否則還是抽象類。
Q:抽象類有沒有建構函式?
A:有,用來給子類初始化,如上例,最先列印我們都是動物。
Q:可以沒有抽象方法嗎,或者說,可以不全是抽象方法嗎?
A;可以。但是介面interface必須全是抽象方法。
abstract關鍵字不能和哪些關鍵字共存
- private:因為子類要覆蓋抽象類的抽象方法,私有後不可訪問,怎麼覆蓋?
- static:抽象類中只是宣告瞭,子類需要重寫抽象類的方法。而靜態方法不能被重寫,所有矛盾了。
- final:final修飾的方法不能被覆蓋,子類要覆蓋啊,所以不能加。
介面-interface
當一個抽象類中全部是抽象方法時,可將其定義為interface。
- 變數規定為:public static final,即使寫成int a,也被認為是
public static final int a;
- 方法規定為:public abstract
- 介面中的成員,都是public的
介面和抽象類一樣,不可例項化,用來給其他類擴充套件功能,介面與介面之間為繼承關係,介面可以多繼承。Java不支援直接多繼承,改成了多實現,即一個子類多個介面,解決了單繼承的侷限性。
抽象類和介面
抽象類只能被單繼承,介面可以背多實現。
抽象類中也可以定義非抽象方法,子類可以直接用非抽象方法。介面中只能定義抽象方法。共同點是,其抽象方法都必須被重寫。
抽象類定義基本的共性內容,介面是定義額外的功能。
多型
對於Animal a = new Cat()
,new出子類,卻讓父類指向子類,就叫多型。
public void method(Animal a) {
a.eat(); // 執行時根據具體傳入的實參,來呼叫對應的方法
// 若Dog和Cat都繼承了Animal,引數傳入Dog就執行dog.eat(),傳入Cat就執行cat.eat()
// 這有點像C++中的動態繫結
}
// 和以下函式過載比較起來,是不是方便了
public void method(Dog a) {
a.eat()
}
public void method(Cat a) {
a.eat();
}
多型好處
從上例可以看出,多型的好處:前期定義的程式碼可以用於後期,比如再來一個Wolf類繼承Animal,就可以傳入wolf呼叫以上函式,傳入的就是wolf了。
多型缺點
前期定義的內容,不可呼叫後期子類的特有內容。舉個例子
public void method(Animal a) {
a.eat(); // 父類定義了該方法
a.catchMouse(); // Animal沒有定義“抓老鼠”的方法,若是傳入的引數不是Cat,則報錯
}
// 怎麼解決?強轉回來!如下
public void method(Animal a) {
a.eat();
// 先判斷傳入的實參,若是Cat,進行強轉回Cat後,再執行“抓老鼠”
if (a instanceof Cat){
Cat c = (Cat)a;,
c.catchMouse();
}
// 若傳入的是狗,就執行“叫”
if (a instanceof Dog) {
Dog d = (Dog)a;
d.bark();
}
}
Animal a = new Cat();
- 若子類沒有覆蓋父類的方法,就呼叫父類自己的方法。
- 若子類呼叫了自己特有的方法,父類並沒有定義該方法,則編譯不通過。Cat型別提升為Animal後,Cat特有的方法會被捨棄,與Animal同名的函式被覆蓋。
換個說法,a是Animal,它只能用Animal定義過的方法和變數,但執行函式時實際執行的是Cat的重寫方法。總的來說,如下
- 對於成員變數,和靜態函式:編譯和執行都是看父類是否具有該變數和靜態,若a呼叫了Cat獨有的變數,則報錯。
- 對於成員函式,編譯時候檢查Animal是否定義該方法,執行時候執行子類重寫的方法,若子類沒有重寫,自然執行父類的。
內部類-巢狀類
public class Out {
private String out;
private class In {
private String in;
}
}
- 內部類可以直接訪問外部類的成員(private的也行)
- 但是外部類要訪問內部類,必須建立內部類的物件。
- 內部類也可以是static的,這是,內部類隨著外部類的載入而載入,相當於是外部類。
Out.In in = new Out().new In(); // 非static內部類的例項化
Out.In in = new Out.In(); // static內部類可以這樣例項化
In in = new In(); // static內部類也可以這樣例項化,相當於外部類了,僅在本例有效
如果內部類用到了靜態成員,則內部類必須是static的。
同名變數的訪問。
public class Out {
private int num = 1;
private class In {
int num= 2;
public void show() {
int num = 3;
System.out.println(num); // 列印3
System.out.println(this.num); // 列印2
System.out.println(Out.this.num); // 列印1
}
}
}
方法內部的內部類
public class Out {
public void func() {
// num是func方法中的區域性變數,func中又定義了內部類,用到這個num,該num必須是final的
final int num = 1;
// java8中,會預設加上final,所以可以直接int num = 1;
class In {
public void show() {
System.out.println(num);
}
}
}
}
方法裡的內部類不能訪問外部類方法中的區域性變數,除非變數被宣告為final型別,因為內部類物件的生命週期超過區域性變數的生命週期。有可能出現成員方法已呼叫結束,區域性變數已死亡,但匿名內部類的物件仍然活著。
Java 8中:如果區域性變數被匿名內部類訪問,那麼該區域性變數相當於自動使用了final修飾。
匿名內部類
若一個程式的某個類只使用了一次,則可定義為匿名內部類,用完就消亡。條件是必須繼承或者實現一個外部類或者介面。
abstract class Person {
public abstract void eat();
}
// 不用內部類的情況,需要再寫一個類繼承Person覆蓋方法
class Child extends Person {
@Override
public abstract void eat() {
System.out.println("Child eat");
}
}
public class Out {
public static void main(String[] args) {
// 匿名內部類,這裡相當於繼承了抽象類Person,新寫了一個無名的子類,並覆蓋了eat方法
new Person() {
public void eat() {
System.out.println("eat something");
}
}.eat();
}
}
下面內容轉自Nerxious-部落格園,總得得不錯,特地搬過來。
// 不使用匿名內部類
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
//執行結果:eat something
// 可以看到,我們用Child繼承了Person類,然後實現了Child的一個例項,將其向上轉型為Person類的引用但是,如果此處的Child類只使用一次,那麼將其編寫為獨立的一個類豈不是很麻煩?這個時候就引入了匿名內部類
// 匿名內部類的基本實現
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
//執行結果:eat something
// 可以看到,我們直接將抽象類Person中的方法在大括號中實現了這樣便可以省略一個類的書寫並且,匿名內部類還能用於介面上
// 在介面上使用匿名內部類
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
//執行結果:eat something
// 由上面的例子可以看出,只要一個類是抽象的或是一個介面,那麼其子類中的方法都可以使用匿名內部類來實現。最常用的情況就是在多執行緒的實現上,因為要實現多執行緒必須繼承Thread類或是繼承Runnable介面
// Thread類的匿名內部類實現
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
// 執行結果:1 2 3 4 5
// Runnable介面的匿名內部類實現
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
// 執行結果:1 2 3 4 5
by @sunhiayu
2016.12.11
相關文章
- JAVA基礎之介面與內部類Java
- Java 的抽象類, 介面以及內部類Java抽象
- Java基礎-抽象類和介面Java抽象
- 重拾java基礎之內部類Java
- Java基礎--Java 內部類Java
- Java基礎10 介面的繼承與抽象類Java繼承抽象
- JAVA基礎之三-介面和抽象類Java抽象
- Java基礎內部類4-內部類進階Java
- Java基礎10---包、內部類、匿名內部類Java
- java內部類之成員內部類之匿名內部類Java
- Java基礎-內部類詳解Java
- Java抽象類、繼承及多型和介面卡的實現Java抽象繼承多型
- java多型抽象類例項Java多型抽象
- java內部類之成員內部類Java
- java內部類之成員內部類之區域性內部類Java
- 內部類,類修飾符,上轉型物件,多型,介面回撥物件多型
- Java 基礎鞏固:再談抽象類和介面Java抽象
- 物件導向基礎(2)--抽象類 介面 集合 泛型物件抽象泛型
- java基礎的內部類定時Java
- Java類與匿名內部類Java
- Java總結-抽象類與介面Java抽象
- java中的抽象類與介面Java抽象
- Java的抽象類與介面理解Java抽象
- 抽象類與介面抽象
- 介面與抽象類抽象
- 類與介面(二)java的四種內部類詳解Java
- 學習Java基礎知識,打通面試關~十二介面與抽象類Java面試抽象
- Java 基礎學習總結(一)抽象類和介面Java抽象
- java之內部類(InnerClass)----非靜態內部類、靜態內部類、區域性內部類、匿名內部類Java
- Java_介面回撥與匿名內部類Java
- java內部類之成員內部類例項Java
- 多型和抽象類多型抽象
- Java基礎8:深入理解內部類Java
- scala_繼承、型別判斷、抽象類、匿名內部類繼承型別抽象
- java抽象類與介面——設計模式Java抽象設計模式
- Java抽象類與介面的區別Java抽象
- Java的抽象類 & 介面Java抽象
- Kotlin基礎 — 巢狀類、內部類Kotlin巢狀