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的机制了解更清楚。