阿里巴巴Java成神之路-笔记(7)枚举类型和泛型
Java语言基础(7)
1. 枚举
从Java1.5开始,枚举类型( enum type) 是指由一组固定的常量组成合法的类型。 Java 中由关键字enum
(小写!)来定义一个枚举类型。 下面就是 java 枚举类型的定义。
枚举可以在编译期间实现自动的类型检查。
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
enum既然是类,就可以定义属性和方法,如下:
RED,GREEN,BALNK,YELLOW 都是enum类型的 实例对象。
下面的 name, index,Color(),toString()
是它们4个都有的属性或者方法。
所以类似RED("红色", 1)
就是调用下面的Color()
构造函数 初始化Color对象RED.
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}
1.1 枚举的实现
那么枚举类型到底是什么类呢? 是 enum 吗?
答案很明显不是, enum 就和 class 一样, 只是一个关键字, 他并不是一个类, 那么枚举是由什么类维护的呢, 我们简单的写一个枚举:
public enum t {
SPRING,SUMMER;
}
然后我们使用反编译, 看看这段代码到底是怎么实现的, 反编译后代码内容如下:
public final class T extends Enum
{
private T(String s, int i)
{
super(s, i);
}
public static T[] values()
{
T at[];
int i;
T at1[];
System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0,
i);
return at1;
}
public static T valueOf(String s)
{
return (T)Enum.valueOf(demo/T, s);
}
public static final T SPRING;
public static final T SUMMER;
private static final T ENUM$VALUES[];
static
{
SPRING = new T("SPRING", 0);
SUMMER = new T("SUMMER", 1);
ENUM$VALUES = (new T[] {
SPRING, SUMMER
});
}
}
通过反编译后代码我们可以看到, public final class T extends Enum, 说明, 该类是继承了 Enum 类的, 同时 final 关键字告诉我们, 这个类也是不能被继承的。
当我们使用 enmu 来定义一个枚举类型的时候, 编译器会自动帮我们创建一个 final类型的类继承 Enum 类, 所以枚举类型不能被继承。
java 枚举值比较用 ==
和 equals
方法没啥区别, 两个随便用都是一样的效果。
因为枚举 Enum 类的 equals 方法默认实现就是通过 == 来比较的; 类似的Enum 的 compareTo 方法比较的是 Enum 的 ordinal 顺序大小; 类似的还有 Enum的 name 方法和 toString 方法一样都返回的是 Enum 的 name 值。
1.2 枚举与单例模式
双重锁校验实现的单例模式的代码之所以很臃肿, 是因为大部分代码都是在保证线程安全。 为了在保证线程安全和锁粒度之间做权衡, 代码难免会写的复杂些。 但是, 这段代码还是有问题的, 因为他无法解决反序列化会破坏单例的问题。
枚举可解决线程安全问题
下面是enum实现单例模式 返回 User 对象
public class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
INSTANCE;//创建一个枚举对象INSTANCE,该对象天生为单例
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
public class Test {
public static void main(String [] args){
System.out.println(User.getInstance());
System.out.println(User.getInstance());
System.out.println(User.getInstance()==User.getInstance());
}
}
结果为true
2. 泛型
Java 泛型( generics) 是 JDK 5 中引⼊的⼀个新特性, 允许在定义类和接口时候使⽤类型参数。泛型最⼤的好处是可以提高代码的复用性。
2.1 类型擦除
类型擦除指的是通过类型参数合并, 将泛型类型实例关联到同一份字节码上。 编译器只为泛型类型生成一份字节码, 并将其实例关联到这份字节码上。 类型擦除的关键在于从泛型类型中清除类型参数的相关信息, 并且再必要的时候添加类型检查和类型转换的方法。 类型擦除可以简单的理解为将泛型 java 代码转换为普通 java 代码, 只不过编译器更直接点,将泛型 java 代码直接转换成普通 java 字节码。 类型擦除的主要过程如下: 1.将所有的泛型参数用其最左边界( 最*的父类型) 类型替换。 ( 这部分内容可以看: Java 泛型中 extends 和 super 的理解) 2.移除所有的类型参数。
2.2 泛型带来的问题
- 泛型遇到重载
public class GenericTypes {
public static void method(List<String> list) {
System.out.println("invoke method(List<String> list)");
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
}
}
上面这段代码, 有两个重载的函数, 因为他们的参数类型不同, 一个是 List<String>
另一个是 List<Integer>
, 但是, 这段代码是编译通不过的。 因为我们前面讲过, 参数List<Integer>
和 List<String>
编译之后都被擦除了, 变成了一样的原生类型 List, 擦除动作导致这两个方法的特征签名变得一模一样。
- 泛型内包含静态变量
public class StaticTest {
public static void main(String[] args){
GT<Integer> gti = new GT<Integer>();
gti.var=1;
GT<String> gts = new GT<String>();
gts.var=2;
System.out.println(gti.var);
}
}
class GT<T>{
public static int var=0;
public void nothing(T x){}
}
答案是: 2! 由于经过类型擦除, 所有的泛型类实例都关联到同一份字节码上, 泛型类的所有静态变量是共享的。
总结:
2.3 List<Object>
和原始类型 List
之间的区别
上一篇: Python中字典的基本操作
下一篇: String-面试常考问题剖析