跟进:Java中没有枚举的实例控件

时间:2009-05-22 08:50:48

标签: java

这是this question的后续行动。这个解决方案是否不漏水?

public final class Thing implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final Thing[] instances = new Thing[2];
    private static int nextOrdinal = 0;

    public static final Thing instance0 = new Thing("whatever0");
    public static final Thing instance1 = new Thing("whatever1");

    private transient final String someState;

    public String someMethod() {return someState;}

    private final int ordinal;

    private Thing(String someState) {
        this.someState = someState;
        this.ordinal = nextOrdinal++;
        instances[this.ordinal] = this;
    }

    private Object readResolve() throws ObjectStreamException {
        return instances[this.ordinal];
    }
}

2 个答案:

答案 0 :(得分:8)

  

此解决方案是否具有防水功能?

没有。 (虽然它可能是足够的,取决于代码的使用方式和位置。)

项目77中:对于实例控制,首选枚举类型为readResolve Effective Java, 2nd ed),Bloch演示了攻击者如何拥有像您这样的类返回任何值。攻击依赖于手工制作的字节输入,并且能够在目标上运行代码(因此,如果在沙盒环境中使用您的代码可能存在安全风险,某些RMI情况等)。我不知道这是否是唯一的攻击 - 它是唯一提到的攻击。解决方案是声明所有字段是瞬态的,但是你有如何存储值的问题。

您可以使用序列化代理模式来解决这些问题(书中的第78项 - 每个阅读它的Java程序员都推荐它的原因。)

public final class Thing implements Serializable {
  private static final long serialVersionUID = 1L;
  private static final Thing[] INSTANCES = new Thing[2];
  private static int NEXT_ORDINAL = 0;

  public static final Thing INSTANCE0 = new Thing(
      "whatever0");
  public static final Thing INSTANCE1 = new Thing(
      "whatever1");

  private transient final String someState;

  public String someMethod() {
    return someState;
  }

  private final int ordinal;

  private Thing(String someState) {
    this.someState = someState;
    ordinal = NEXT_ORDINAL++;
    INSTANCES[ordinal] = this;
  }

  private Object writeReplace() {
    return new ThingProxy(this);
  }

  private void readObject(ObjectInputStream stream)
      throws InvalidObjectException {
    throw new InvalidObjectException("Proxy required");
  }

  private static class ThingProxy implements Serializable {
    private static final long serialVersionUID = 1L;
    private final int ordinal;

    private ThingProxy(Thing t) {
      ordinal = t.ordinal;
    }

    private Object readResolve()
        throws ObjectStreamException {
      return INSTANCES[ordinal];
    }

  }

}

虽然,与复制任何与互联网相关的安全性一样,但需要注意。我绝不是专家。

答案 1 :(得分:1)

此代码仍然缺少Enum的一些有趣功能:

  • 在所有枚举值
  • 上缺少“values()”作为迭代器
  • 仍然缺少远程迭代器
  • 不能用于开关

实施例

public final class Day implements Serializable {

    private static final long serialVersionUID = 1L;
    private static Day[] instances = new Day[7];
    public static final Day MONDAY = new Day("Monday");
    public static final Day TUESDAY = new Day("Tuesday");
    public static final Day WEDNESDAY = new Day("Wednesday");
    public static final Day THURSDAY = new Day("Thursday");
    public static final Day FRIDAY = new Day("Friday");
    public static final Day SATURDAY = new Day("Saturday");
    public static final Day SUNDAY = new Day("Sunday");
    private static int nextOrdinal = 0;
    private transient final String name;
    private final int ordinal;

    private Day(String aName) {
        this.name = aName;
        this.ordinal = nextOrdinal++;
        instances[this.ordinal] = this;
    }

    private Object readResolve() throws ObjectStreamException {
        return instances[this.ordinal];
    }

    public String toString() {
        return name;
    }

    public boolean equals(Object obj) {
        return obj instanceof Day && ((Day) obj).ordinal == ordinal;
    }

    public int hashCode() {
        return name.hashCode();
    }

    public static Iterator values() {
        return new Iterator() {

            private int i = 0;

            public boolean hasNext() {
                return i < instances.length;
            }

            public Object next() {
                return instances[i++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Not supported.");
            }
        };
    }

    public static Iterator range(final Day from, final Day to) {
        return new Iterator() {

            private int i = from.ordinal;

            public boolean hasNext() {
                return i <= to.ordinal;
            }

            public Object next() {
                return instances[i++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Not supported.");
            }
        };
    }

    public static void main(String[] args) {
        Iterator week = Day.values();

        while (week.hasNext()) {
            System.out.println(week.next());
        }

        Iterator weekEnd = Day.range(SATURDAY, SUNDAY);

        while (weekEnd.hasNext()) {
            System.out.println(weekEnd.next());
        }
    }
}