单例与枚举类型

单例与枚举

在《Effective Java》中,提到创建单例的三种方法以及枚举类型的使用。

单例实现

作者首先说明Java 1.5之前的两种单例实现方法:

方法一:Singleton with public final field

// Singleton with public final field
   public class Elvis {
       public static final Elvis INSTANCE = new Elvis();
       private Elvis() { ... }
       public void leaveTheBuilding() { ... }
   }

这种方法通过私有构造方法,并提供public static final属性实现单例。但其缺点是,通过反射,及AccessibleObject.setAccessible(true)方法,可以调用私有构造方法。

方法二:Singleton with static factory

// Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis(); 
    private Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }
    public void leaveTheBuilding() { ... }
}

这种方法也是通过私有构造方法,并提供public static方法实现单例。但其缺点时反序列化后,会创建一个新的实例。为了解决这个问题,要增加readResolve方法:

// readResolve method to preserve singleton property
   private Object readResolve() {
        // Return the one true Elvis and let the garbage collector
        // take care of the Elvis impersonator.
       return INSTANCE;
   }

readResolve方法用于替换从Stream流中读出的对象,确保不会反序列化出来另一个实例。

方法三:Enum实现单例

Java 1.5以后,可以使用枚举类型实现单例:

// Enum singleton - the preferred approach
   public enum Elvis {
       INSTANCE;
       public void leaveTheBuilding() { ... }
   }

只有一个元素的enum类型是实现单例最好的方法。不存在序列化、反射等影响。

上面的简单例子似乎不太清晰,到底怎么用呢?就如下面这样:

public enum MySingleton {
    INSTANCE;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void otherMethod() {
        ...
    }
}


//调用示例
MySingleton.INSTANCE.setName("ABCD");

枚举类型究竟还有哪些功能呢?

枚举类型

枚举类型的基本用法

枚举类型最基本的用法就像下面的例子:

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

public class EnumTest {
    Day day;

    public EnumTest(Day day) {
        this.day = day;
    }

    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;

            case FRIDAY:
                System.out.println("Fridays are better.");
                break;

            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break;

            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }

    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();
    }
}

//The output is:

Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best.

还可以对枚举类型遍历:

for (Day p : Day.values()) {
    ...
}

其它大家熟知的用法还包括:

public enum Coin {
    PENNY(1), NICKEL(5), DIME(10), QUARTER(25);

    Coin(int value) {
        this.value = value;
    }

    private final int value;

    public int value() {
        return value;
    }
}

枚举类型的高级用法

枚举常量的自引用(Self-Reference)限制
//有错误代码
enum Color {
    RED, GREEN, BLUE;
    Color() { colorMap.put(toString(), this); }

    static final Map<String,Color> colorMap =
        new HashMap<String,Color>();
}

上面的代码中,colorMap并未初始化,所以构造方法会出错!可以改成:

enum Color {
    RED, GREEN, BLUE;

    static final Map<String,Color> colorMap =
        new HashMap<String,Color>();
    static {
        for (Color c : Color.values())
            colorMap.put(c.toString(), c);
    }
}
Enum Constants with Class Bodies

枚举类型还能这么玩:

enum Operation {
    PLUS {
        double eval(double x, double y) { return x + y; }
    },
    MINUS {
        double eval(double x, double y) { return x - y; }
    },
    TIMES {
        double eval(double x, double y) { return x * y; }
    },
    DIVIDED_BY {
        double eval(double x, double y) { return x / y; }
    };

    // Each constant supports an arithmetic operation
    abstract double eval(double x, double y);

    public static void main(String args[]) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        for (Operation op : Operation.values())
            System.out.println(x + " " + op + " " + y +
                               " = " + op.eval(x, y));
    }
}


java Operation 2.0 4.0
2.0 PLUS 4.0 = 6.0
2.0 MINUS 4.0 = -2.0
2.0 TIMES 4.0 = 8.0
2.0 DIVIDED_BY 4.0 = 0.5
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值