Java Interface,abstract class和enum的比对和组合使用方法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/diangangqin/article/details/101470818

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

猜你喜欢

转载自blog.csdn.net/diangangqin/article/details/101470818