JavaSE学习笔记(8.Java枚举类)

1.Java中类的分类:

Java中一共有4种类:普通类(class)、抽象类(Abstract Class)、接口(Interface)、枚举类(enum)

Java中的内部类还存在4种:静态内部类、非静态内部类、局部内部类、匿名内部类

相互组合应该可以组合成16个种类,但是由于匿名内部类需要具备实例化的能力,所以没有匿名抽象类和匿名接口;枚举类又可以转换为普通类(原因后面描述),所以总共存在10个中类的类类型!

特性分析原则:

上述这10种类的特性分析原则很简单就是特性组合原则,把其本身的特性和其作为内部的特性组合起来,就是其所具备的特性,同时还需要同步考虑public/private/protected等修饰符的修饰作用!

Ps:1.抽象类和接口特性查看《JavaSE学习笔记(5.抽象类与接口)》

        2.内部类特性查看《JavaSE学习笔记(6.Java的内部类)》

 

2.枚举类的本质:

其实枚举类就是“语法糖”,就是一个对继承于Enum的类的包装,通过Jad反编译可以清晰的看到枚举类的本质!注意Java语法规定Enum类不能被其他类继承,编译会报错(may not subclass Enum explicitly)!

枚举定义代码:

package Test;

public enum EnumWeek {
	
	Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday;
}

通过Jad反编译的对应Class文件(本节重点!):

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumWeek.java

package Test;


public final class EnumWeek extends Enum
{

    private EnumWeek(String s, int i)
    {
        super(s, i);
    }

    public static EnumWeek[] values()
    {
        EnumWeek aenumweek[];
        int i;
        EnumWeek aenumweek1[];
        System.arraycopy(aenumweek = ENUM$VALUES, 0, aenumweek1 = new EnumWeek[i = aenumweek.length], 0, i);
        return aenumweek1;
    }

    public static EnumWeek valueOf(String s)
    {
        return (EnumWeek)Enum.valueOf(Test/EnumWeek, s);
    }

    public static final EnumWeek Monday;
    public static final EnumWeek Tuesday;
    public static final EnumWeek Wednesday;
    public static final EnumWeek Thursday;
    public static final EnumWeek Friday;
    public static final EnumWeek Saturday;
    public static final EnumWeek Sunday;
    private static final EnumWeek ENUM$VALUES[];

    static 
    {
        Monday = new EnumWeek("Monday", 0);
        Tuesday = new EnumWeek("Tuesday", 1);
        Wednesday = new EnumWeek("Wednesday", 2);
        Thursday = new EnumWeek("Thursday", 3);
        Friday = new EnumWeek("Friday", 4);
        Saturday = new EnumWeek("Saturday", 5);
        Sunday = new EnumWeek("Sunday", 6);
        ENUM$VALUES = (new EnumWeek[] {
            Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
        });
    }
}

Ps:通过上面反编译代码可以看到,enum为枚举类提供了两个静态方法,并继承了Enum类中的部分方法!

values():生成一个新的ENUM$VALUES数组,返回枚举类中的所有枚举实例!

valueOf():将特定的字符串转为对应的枚举类型,如果该枚举类型不存在,上报异常!

 

3.Enum枚举类父类介绍:

Enum.class源码:

package java.lang;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Map;

public abstract class Enum<E extends Enum<E>>
  implements Comparable<E>, Serializable
{
  private final String name;
  private final int ordinal;
  
  public final String name()
  {
    return name;
  }
  
  public final int ordinal()
  {
    return ordinal;
  }
  
  protected Enum(String paramString, int paramInt)
  {
    name = paramString;
    ordinal = paramInt;
  }
  
  public String toString()
  {
    return name;
  }
  
  public final boolean equals(Object paramObject)
  {
    return this == paramObject;
  }
  
  public final int hashCode()
  {
    return super.hashCode();
  }
  
  protected final Object clone()
    throws CloneNotSupportedException
  {
    throw new CloneNotSupportedException();
  }
  
  public final int compareTo(E paramE)
  {
    E ? = paramE;
    Enum localEnum = this;
    if ((localEnum.getClass() != ?.getClass()) && (localEnum.getDeclaringClass() != ?.getDeclaringClass())) {
      throw new ClassCastException();
    }
    return ordinal - ordinal;
  }
  
  public final Class<E> getDeclaringClass()
  {
    Class localClass1 = getClass();
    Class localClass2 = localClass1.getSuperclass();
    return localClass2 == Enum.class ? localClass1 : localClass2;
  }
  
  public static <T extends Enum<T>> T valueOf(Class<T> paramClass, String paramString)
  {
    Enum localEnum = (Enum)paramClass.enumConstantDirectory().get(paramString);
    if (localEnum != null) {
      return localEnum;
    }
    if (paramString == null) {
      throw new NullPointerException("Name is null");
    }
    throw new IllegalArgumentException("No enum constant " + paramClass.getCanonicalName() + "." + paramString);
  }
  
  protected final void finalize() {}
  
  private void readObject(ObjectInputStream paramObjectInputStream)
    throws IOException, ClassNotFoundException
  {
    throw new InvalidObjectException("can't deserialize enum");
  }
  
  private void readObjectNoData()
    throws ObjectStreamException
  {
    throw new InvalidObjectException("can't deserialize enum");
  }
}

/* Location:           C:\Program Files\Java\jre1.8.0_131\lib\rt.jar
 * Qualified Name:     java.lang.Enum
 * Java Class Version: 8 (52.0)
 * JD-Core Version:    0.7.1
 */

Enum接口手册:

JavaSE学习笔记(8.Java枚举类)

static valueOf():静态方法,通过枚举名和枚举类型获取枚举实例,已经被枚举类封装为其实例方法valueOf()

name():获取枚举实例的名字,详细见上反编译代码!

ordinal():获取枚举实例的标号,详细见上反编译代码!

compareTo():比较两个枚举实例的标号

toString():重写Ojbect类的toString()方法,返回枚举实例的名字!

equals():比较两个枚举实例是否相等,具体逻辑就是==

枚举类不允许序列化和反序列化、不允许克隆、不可以重写finalize()方法!

 

4.枚举类的构造器及实例初始代码块:

由于enum是一个特殊的语法结构,我们暂且把枚举实例中定义方法的这个区域叫做其初始代码块,与实例的初始化代码块不同!

a.枚举类默认提供一个无参构造器

b.当需要使用有参构造器的时候,示例如下:

public enum EnumWeek {
	Monday("星期一"),Tuesday("星期二");
	
	private String name;
	
	private EnumWeek(String name)
	{
		this.name = name;
	}	
}

jad反编译后的构造器:

public final class EnumWeek extends Enum
{
    private String name;

    private EnumWeek(String s, int i, String name)
    {
        super(s, i);
        this.name = name;
    }
}

c.当枚举类中存在抽象方法的时候,抽象方法来自者接口的时候(不可能来自抽象类,因为已经继承Enum类):

可以统一在枚举类中提供实现方法,也可以在每个枚举实例初始的代码块中分别实现

interface Week
{
	void fun();
}

public enum EnumWeek implements Week{
	
	Monday,Tuesday;
	
	public void fun()
	{
		
	}	
}

/*示例将两个EnumWeek放到了一起*/
public enum EnumWeek implements Week{
	
	Monday
	{
		public void fun()
		{
			
		}
	},
	Tuesday
	{
		public void fun()
		{
			
		}
	};	
}

d.当枚举类中存在抽象方法的时候,抽象方法来自本身的枚举抽象类的时候:

只能在每个枚举实例初始的代码块中分别提供实现方法

public enum EnumWeek {
	Monday()
	{
        @Override
		void fun()
		{
			
		}
	},
	Tuesday()
	{
        @Override
		void fun()
		{
			
		}
	};
	
	abstract void fun();
}

e.枚举实例可以在初始的代码块中重写枚举类中的方法;与上面那点不同的是,被重写的方法可以不是抽象方法!

public enum EnumWeek {
	Monday()
	{
        @Override
		void fun()
		{
			
		}
	},
	Tuesday()
	{
        @Override
		void fun()
		{
			
		}
	};
	
    void fun();
}

ps:通过反编译可以看到,存在初始代码块的枚举实例,其实是一个继承于枚举类的内部类的实例化对象!

反编译代码如下:

EnumWeek.jad文件:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumWeek.java

package Test;


// Referenced classes of package Test:
//            Week

public abstract class EnumWeek extends Enum
    implements Week
{

    private EnumWeek(String s, int i)
    {
        super(s, i);
    }

    public static EnumWeek[] values()
    {
        EnumWeek aenumweek[];
        int i;
        EnumWeek aenumweek1[];
        System.arraycopy(aenumweek = ENUM$VALUES, 0, aenumweek1 = new EnumWeek[i = aenumweek.length], 0, i);
        return aenumweek1;
    }

    public static EnumWeek valueOf(String s)
    {
        return (EnumWeek)Enum.valueOf(Test/EnumWeek, s);
    }

    EnumWeek(String s, int i, EnumWeek enumweek)
    {
        this(s, i);
    }

    public static final EnumWeek Monday;
    public static final EnumWeek Tuesday;
    private static final EnumWeek ENUM$VALUES[];

    static 
    {
        Monday = new EnumWeek("Monday", 0) {

            public void fun()
            {
            }

        }
;
        Tuesday = new EnumWeek("Tuesday", 1) {

            public void fun()
            {
            }

        }
;
        ENUM$VALUES = (new EnumWeek[] {
            Monday, Tuesday
        });
    }
}

EnumWeek$1.Jad文件:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumWeek.java

package Test;


// Referenced classes of package Test:
//            EnumWeek

class EnumWeek$1 extends EnumWeek
{

    public void fun()
    {
    }

    EnumWeek$1(String s, int i)
    {
        super(s, i, null);
    }
}

5.枚举类与普通类的使用差异:

  • 枚举类的实例赋值必须放在枚举类的第一行!
  • switch-case语法支持枚举类,switch中已经明确枚举类,case中可以不通过枚举类访问其枚举实例!
  • 当枚举类包含抽象方法的时候,系统会自动将枚举类标记为abstract类型,无需手动添加abstartct修饰符(手动添加编译会报错Illegal modifier for the enum EnumWeek; only public is permitted);默认没有抽象方法的枚举类,系统会自动将枚举类标记为final类型!
  • 当枚举类中存在抽象方法的时候,需要在枚举实例初始的代码块中增加相应逻辑(详细见第4节)!
  • 带参枚举类需要在枚举实例初始的代码块中明确指示!

6.getDeclaringClass()方法的使用:

枚举实例建议通过使用getDeclaringClass()方法获取枚举类而不要使用getClass()方法,因为当枚举实例存在初始的代码块的时候,枚举实例的实例化其实并不来自于这个枚举类,而是来自这个枚举类中的一个内部类(详细示例见第4节)!调用getClass()方法的时候,会返回这个内部类,而不是对应的枚举类,getDeclaringClass()方法则不会存在这个问题!

public enum FruitEnum{
        BANANA{
            String getName() {
                return "香蕉";
            }
        },APPLE{
            String getName() {
                return "苹果";
            }
        };

        abstract String getName();

        public static void main(String[] args) {
            System.out.println(BANANA.getDeclaringClass());
            System.out.println(BANANA.getClass());
        }
    }

    # 运行结果
    class FruitEnum
    class FruitEnum$1

7.总结:

       枚举类其实就是针对enum关键字和枚举实例初始的代码块的“语法糖”,通过反编译可以看出,最终枚举类都会被编译成为不同的普通类。所以针对枚举类的相关分析,都可以当做普通类来分析,枚举类与普通类仅仅是语法规则不同而已!