这是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];
}
}
答案 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的一些有趣功能:
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());
}
}
}