java Interface應該是爲了實現多繼承而添加的,提到Interface,那就不得不說abstract class,的確abstract class和Interface有很多地方很相似,例如都是抽象,都不能實例化,implements或者extends需要實現裏面全部的abstract函數。但是他們的區別也很多。
區別:
1 Interface中的函數默認屬性是public和abstract(無論是否在code中明確寫出來),函數不能有body;而abstract class的默認屬性應該是protected的,如果屬性不是abstract,那就需要body或者是abstract屬性。
2 Interface可以implements多個,而abstract只能extends一個。
3 abstract class還是class,從編譯後的class文件中,仍然能夠看到默認的構造函數,而interface沒有。
4 Interface中的成員變量默認是public static final的,也就是常量,所以不能定義可變量,但是abstract class除了定義常量還可以定義可變量。如果能夠選用interface,就最好用interface。另外如果文件中大量使用常量,並且不同文件中會使用同一個,那麼可以將使用到的所有常量定義在一個interface裏面,不需要定義函數,也不要使用一段時間後定義函數,之後各個使用的類直接implements interface就可以了,或者import interface也可以。
例如:
public interface IColor {
public static final int RED = 0;
public static final int BLUE = 1;
public static final int GREEN = 2;
}
public class Cloth implements IColor {
protected int computePrice(int color) {
if (color == RED) {
return 100;
} else {
return 50;
}
}
}
當然也可以用import interface的方式了,這樣就無論interface中是否有定義函數,都可以使用裏面的常量。
例如:
public interface IColor {
public static final int RED = 0;
public static final int BLUE = 1;
public static final int GREEN = 2;
void setColor();
}
import Icolor;
public class Cloth { //這裏如果寫成public class Cloth implements IColor ,那麼就必須實現setColor
protected int computePrice(int color) {
if (color == IColor.RED) {
return 100;
} else {
return 50;
}
}
}
interface還可以嵌入到類裏面,將常量根據類別進行分組,使得對外使用這些常量更好的組織在一起。
例如
public class Car {
int mClor;
int mSize;
public interface Size {
public static final int BIG = 0;
public static fianl int SMALL = 1;
}
public interface SkinColor {
public static final int RED = 0;
public static final int BLUE = 1;
}
public int setColor (int color) {
mClor = color;
}
public int setSize (int size) {
mSize = size;
}
}
import Car;
public class CarShop {
Car newCar = new Car();
newCar.setColor(Car.SkinColor.RED); // 這裏不能寫成newCar.setColor(newCar.SkinColor.RED)
newCar.setColor(Car.Size.BIG);
}
當然用interface當enum使用,不如enum安全了,例如setColor的參數是int,除非setColor裏面判斷傳入參數的有效性。
例如
public void setColor (int color) {
if (color < RED || color > BLUE) {
return -1;
}
mClor = color;
return mClor;
}
既然提到了enum,我們就研究一下,Java enum的功能比較強大,使用也比較方便。
package son;
public enum TestEnum {
ALWAYS_USE,
ASK_EVERY_TIME,
NEVER_USE;
public void set(TestEnum value) {
System.out.println(value);
}
}
import son.TestEnum;
public class Test {
public TestEnum returnEnumValue(TestEnum value) { // 這就比使用interface的static的value安全了,因爲參數現在只能傳入TestEnum
return value;
}
public static void main(String[] args) [
Test test = new Test();
System.out.println(test.returnEnumValue(TestEnum.ALWAYS_USE));
System.out.println(TestEnum.valueOf("ALWAYS_USE"));
}
}
我們可以通過Java document或者網上資源來了解獲取新知識,但是對於Java我們可以先看一下編譯出來的class文件,再結合網絡資源,可以說事半功倍。下面就是TestEnum編譯出來後的class文件。從class文件可以看出TestEnum繼承了java.lang.Enum,TestEnum的屬性是public final的class,也就是說TestEnum不能被繼承並且也是一個class,而其中的enum value值是public static final son.TestEnum的屬性,另外還有兩個static的函數values()和valueOf,values返回TestEnum中的所有值,而valueOf的參數是String,通過String返回TestEnum對應的值。關於valueOf需要注意傳入的String如果不是ENUM裏面能夠對應的string值,編譯的時候不會有問題,但是執行的時候會有異常。之後在文件的最後有static code,從語義上來說應該是將Enum對應的value值“ALWAYS_USE”,“ASK_EVERY_TIME”,“NEVER_USE”,放到一個地方存起來,這部分應該和父類Enum相關,具體的含義需要參考Enum的實現了。
從class文件我們就瞭解了Enum的知識,Enum是一個public final class,裏面有兩個static的函數values()和valueOf(),並且enum value值是public static final的屬性。
Compiled from "TestEnum.java"
public final class son.TestEnum extends java.lang.Enum<son.TestEnum>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
#1 = Fieldref #4.#39 // son/TestEnum.$VALUES:[Lson/TestEnum;
#2 = Methodref #40.#41 // "[Lson/TestEnum;".clone:()Ljava/lang/Object;
#3 = Class #22 // "[Lson/TestEnum;"
#4 = Class #42 // son/TestEnum
#5 = Methodref #16.#43 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#6 = Methodref #16.#44 // java/lang/Enum."<init>":(Ljava/lang/String;I)V
#7 = Fieldref #45.#46 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #47.#48 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#9 = String #17 // ALWAYS_USE
#10 = Methodref #4.#44 // son/TestEnum."<init>":(Ljava/lang/String;I)V
#11 = Fieldref #4.#49 // son/TestEnum.ALWAYS_USE:Lson/TestEnum;
#12 = String #19 // ASK_EVERY_TIME
#13 = Fieldref #4.#50 // son/TestEnum.ASK_EVERY_TIME:Lson/TestEnum;
#14 = String #20 // NEVER_USE
#15 = Fieldref #4.#51 // son/TestEnum.NEVER_USE:Lson/TestEnum;
#16 = Class #52 // java/lang/Enum
#17 = Utf8 ALWAYS_USE
#18 = Utf8 Lson/TestEnum;
#19 = Utf8 ASK_EVERY_TIME
#20 = Utf8 NEVER_USE
#21 = Utf8 $VALUES
#22 = Utf8 [Lson/TestEnum;
#23 = Utf8 values
#24 = Utf8 ()[Lson/TestEnum;
#25 = Utf8 Code
#26 = Utf8 LineNumberTable
#27 = Utf8 valueOf
#28 = Utf8 (Ljava/lang/String;)Lson/TestEnum;
#29 = Utf8 <init>
#30 = Utf8 (Ljava/lang/String;I)V
#31 = Utf8 Signature
#32 = Utf8 ()V
#33 = Utf8 set
#34 = Utf8 (Lson/TestEnum;)V
#35 = Utf8 <clinit>
#36 = Utf8 Ljava/lang/Enum<Lson/TestEnum;>;
#37 = Utf8 SourceFile
#38 = Utf8 TestEnum.java
#39 = NameAndType #21:#22 // $VALUES:[Lson/TestEnum;
#40 = Class #22 // "[Lson/TestEnum;"
#41 = NameAndType #53:#54 // clone:()Ljava/lang/Object;
#42 = Utf8 son/TestEnum
#43 = NameAndType #27:#55 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#44 = NameAndType #29:#30 // "<init>":(Ljava/lang/String;I)V
#45 = Class #56 // java/lang/System
#46 = NameAndType #57:#58 // out:Ljava/io/PrintStream;
#47 = Class #59 // java/io/PrintStream
#48 = NameAndType #60:#61 // println:(Ljava/lang/Object;)V
#49 = NameAndType #17:#18 // ALWAYS_USE:Lson/TestEnum;
#50 = NameAndType #19:#18 // ASK_EVERY_TIME:Lson/TestEnum;
#51 = NameAndType #20:#18 // NEVER_USE:Lson/TestEnum;
#52 = Utf8 java/lang/Enum
#53 = Utf8 clone
#54 = Utf8 ()Ljava/lang/Object;
#55 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#56 = Utf8 java/lang/System
#57 = Utf8 out
#58 = Utf8 Ljava/io/PrintStream;
#59 = Utf8 java/io/PrintStream
#60 = Utf8 println
#61 = Utf8 (Ljava/lang/Object;)V
{
public static final son.TestEnum ALWAYS_USE;
descriptor: Lson/TestEnum;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final son.TestEnum ASK_EVERY_TIME;
descriptor: Lson/TestEnum;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final son.TestEnum NEVER_USE;
descriptor: Lson/TestEnum;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static son.TestEnum[] values();
descriptor: ()[Lson/TestEnum;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field $VALUES:[Lson/TestEnum;
3: invokevirtual #2 // Method "[Lson/TestEnum;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lson/TestEnum;"
9: areturn
LineNumberTable:
line 3: 0
public static son.TestEnum valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lson/TestEnum;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #4 // class son/TestEnum
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class son/TestEnum
9: areturn
LineNumberTable:
line 3: 0
public void set(son.TestEnum);
descriptor: (Lson/TestEnum;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_1
4: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
7: return
LineNumberTable:
line 9: 0
line 10: 7
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new #4 // class son/TestEnum
3: dup
4: ldc #9 // String ALWAYS_USE
6: iconst_0
7: invokespecial #10 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #11 // Field ALWAYS_USE:Lson/TestEnum;
13: new #4 // class son/TestEnum
16: dup
17: ldc #12 // String ASK_EVERY_TIME
19: iconst_1
20: invokespecial #10 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #13 // Field ASK_EVERY_TIME:Lson/TestEnum;
26: new #4 // class son/TestEnum
29: dup
30: ldc #14 // String NEVER_USE
32: iconst_2
33: invokespecial #10 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #15 // Field NEVER_USE:Lson/TestEnum;
39: iconst_3
40: anewarray #4 // class son/TestEnum
43: dup
44: iconst_0
45: getstatic #11 // Field ALWAYS_USE:Lson/TestEnum;
48: aastore
49: dup
50: iconst_1
51: getstatic #13 // Field ASK_EVERY_TIME:Lson/TestEnum;
54: aastore
55: dup
56: iconst_2
57: getstatic #15 // Field NEVER_USE:Lson/TestEnum;
60: aastore
61: putstatic #1 // Field $VALUES:[Lson/TestEnum;
64: return
LineNumberTable:
line 4: 0
line 5: 13
line 6: 26
line 3: 39
}
Signature: #36 // Ljava/lang/Enum<Lson/TestEnum;>;
SourceFile: "TestEnum.java"
當然interface和enum進行結合使用,在interface中定義enum,可以將相關的常數變量group到一起。
例如
public interface IInnerTestEnum {
public enum ColorEnum {
RED,
BLUE,
GREEN
}
}
其他class如果想使用IInnerTestEnum.ColorEnum,可以import IInnerTestEnum,當然也可以通過implements IInnerTestEnum方式使用。
我個人的建議,優先使用interface,之後根據情況選用abstract class或者enum。另外期望大家通過Java的class文件進行學習,這樣不僅深刻而且深入,對Java的機制瞭解更清楚。