class Impossible {
public Impossible() {
throw new AssertionError("you cannot instantiate me");
}
public void foo() {
System.out.println("The impossible is possible!");
}
}The solution is in the comments...
class Impossible {
public Impossible() {
throw new AssertionError("you cannot instantiate me");
}
public void foo() {
System.out.println("The impossible is possible!");
}
}
9 comments:
Unfortunately you can! It takes a gross combination of subclasses and a badly behaved finalizer:
class Defy extends Impossible {
static Defy STATIC_INSTANCE = null;
@Override protected void finalize() {
STATIC_INSTANCE = this;
}
public static void main(String... args) {
try {
new Defy();
} catch (Error e) {
}
for (int i = 0; i < 10; i++) {
System.gc();
if (Defy.STATIC_INSTANCE != null) {
Defy.STATIC_INSTANCE.foo();
break;
}
}
}
}
Even dead-on-arrival objects whose constructors fail will still be finalized. You should be careful about using constructors to enforce security!
indeed, that's why I always strongly advise against registering objects to other objects in their constructor like:
class MyObject {
public MyObject(Registry registry) {
registry.register(this);
...
throw SomethingHere;
}
}
this works too:
ChuckNorris.newInstance(Defy.class).foo();
Update: this was fixed in Java 6!
For embarrassing reasons, I'm usually running a Java 5 VM, but hopefully that will change Real Soon Now.
Actually, the code you posted still works. What was "fixed" was that if Object's constructor is never run, the finalizer won't be called. But in your example Object's constructor is run.
This object won't be finalized:
class A {
public A (int value) {}
}
class B extends A {
public B () {
super(fail());
}
static int fail () {
throw new RuntimeException();
}
protected void finalize () {
System.out.println("Woe is me. I'm never called.");
}
}
A sometimes-possible workaround would be to declare the constructor private & use factory creation methods. (Only possible if you can disable subclassing, which, if you're using exceptions in the constructor for security, is a reasonable assumption.)
You can also bypass the constructor completely, like shown here:
http://www.javaspecialists.eu/archive/Issue175.html
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
Constructor objDef = parent.getDeclaredConstructor();
Constructor intConstr = rf.newConstructorForSerialization(clazz, objDef);
return clazz.cast(intConstr.newInstance());
sun.misc.Unsafe can be used to by-pass constructor (it may not work in all JDKs):
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe u = (Unsafe) unsafeField.get(null);
Impossible i = (Impossible) u.allocateInstance(Impossible.class);
i.foo();
Post a Comment