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接口手册:
static valueOf():静态方法,通过枚举名和枚举类型获取枚举实例,已经被枚举类封装为其实例方法valueOf()
name():获取枚举实例的名字,详细见上反编译代码!
ordinal():获取枚举实例的标号,详细见上反编译代码!
compareTo():比较两个枚举实例的标号
toString():重写Ojbect类的toString()方法,返回枚举实例的名字!
equals():比较两个枚举实例是否相等,具体逻辑就是==
Ps:枚举类不可以重写clone()方法和finalize()方法,也不允许自定义序列化和反序列化(因为JVM规范中禁掉了枚举类自定义序列化的相关方法,例如writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,有着自己序列化规则)!
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 member enum e; only public, protected, private & static are 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关键字和枚举实例初始的代码块的“语法糖”,通过反编译可以看出,最终枚举类都会被编译成为不同的普通类。所以针对枚举类的相关分析,都可以当做普通类来分析,枚举类与普通类仅仅是语法规则不同而已!