public class DefaultConstructor {
private static class B {
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
B.class.newInstance();
}
}
When I use reflection to create an object from B, it give me the following exception:
java.lang.IllegalAccessException: Class DefaultConstructor can not access a member of class DefaultConstructor$B with modifiers “private”
Let us see which member the DefaultConstructor is trying to access by viewing the source.
Source Code
The following is part of source code of Class#newInstance()
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers); // <--- here throws exception
newInstanceCallerCache = caller;
}
}
As it turns out, our code fails the constructor accessibility check, so it proves that the constructor the compiler provided is private!
Synthetic Default Constructor
We all know compiler will add a default constructor for us, but how this constructor is like?
Let’s look at the code from above example:
// original code:
private static clsss B {
}
//code de-compiled from byte code:
private static class B {
private B() {
}
}
The compiler is clever enough to detect that this is a private class, so it generate a private default constructor. In the most cases, we have a public class, so compiler will just generate a public constructor:
public class DefaultConstructor {
}
// de-compiled from byte code
public class DefaultConstructor {
public DefaultConstructor() {
}
}
What about protected
and default visibility?
static class C {
}
protected static class D {
}
// de-compiled from byte code
protected static class D {
protected D() {
}
}
static class C {
C() {
}
}
So we can come to conclusion: compiler will generate constructor according to class’s visibility.
More Synthetic Constructor
Now, we move to a more complex example:
public class DefaultConstructor {
private static class A {
}
public static void main(String[] args) {
new A();
}
}
Is this example will have different constructor? Let’s see the byte code:
$ javap -c -p DefaultConstructor\$A.class
Compiled from "DefaultConstructor.java"
class reflect.DefaultConstructor$A {
private reflect.DefaultConstructor$A();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: return
reflect.DefaultConstructor$A(
reflect.DefaultConstructor$1);
Code:
0: aload_0
1: invokespecial #1 // Method "<init>":()V
4: return
}
We can clearly see two constructors! One is private, as we have seen before. Another is a default, with a parameter.
What this constructor for and what is DefaultConstructor$1
? (Try to give your answer before moving on)
Nested Class Synthetic Constructor/Class
In order to answer this question, we should understand what’s the relationship between class A
and DefaultConstructor
: A
is the nested class of DefaultConstructor, A
has the privilege to access the private field/method of DefaultConstructor
. So how is this implemented? The answer is adding a new constructor with default visibility for DefaultConstructor
to use.
// this synthetic constructor is only for DefaultConstructor to use
reflect.DefaultConstructor$A(
reflect.DefaultConstructor$1);
Code:
0: aload_0
1: invokespecial #1 // Method "<init>":()V
4: return
Because we already have a private constructor which has no parameter, we have to add a parameter to distinguish them (Otherwise, if we invoke new A()
in class A
, which constructor will be invoked?). So compiler synthesize an empty class called DefaultConstructor$1
to work as parameter.
$ javap -c -p DefaultConstructor\$1.class
Compiled from "DefaultConstructor.java"
class reflect.DefaultConstructor$1 {
}
So the result code is like this:
public class DefaultConstructor {
public DefaultConstructor() {
}
public static void main(String[] args) {
new DefaultConstructor.A(null);
}
private static class A {
private int a;
private A() {
}
A(SomeClass o) {
this();
}
}
}
Ref
Written with StackEdit.
评论
发表评论