【问题标题】:Follow up: instance control in Java without enum [closed]跟进:没有枚举的Java中的实例控制[关闭]
【发布时间】:2009-05-22 18:42:04
【问题描述】:

这是对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];
    }
}

【问题讨论】:

  • 关于您认为可以正常工作的代码的问题与堆栈溢出无关,它们可能与Code Review 的主题有关

标签: java


【解决方案1】:

这个解决方案防水吗?

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

Item 77: For instance control, prefer enum types to 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];
    }

  }

}

不过,就像从互联网上复制任何与安全相关的东西一样,请注意购买者。我绝不是专家。

【讨论】:

  • 很好的警告。最好的解决方案是不要让类 Serializable。
【解决方案2】:

这段代码仍然缺少 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());
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    相关资源
    最近更新 更多