时间记录:2019-11-19
问题描述: 今天在使用 quartz 开始定时任务的时候,我当时考虑到实现 job 不会给外部使用的情况下,使用了内部的私有类,然后发现其不能够进行实例化操作,然后看了下源码,发现其实通过反射的方式将类进行实例化的,然后我当时写的任务类的问题,导致了其通过反射的方式不能够实例化。
下面探索下反射的方式进行的实例化操作,为什么不行
第一部分: 私有类且没用进行无参构造函数显示创建
package com.huo;
/**
* 反射的方式初始化测试
* @author huoruilin
*/
public class TestReflection
{
public static void main(String[] args)
{
try
{
Item item = (Item)Item.class.newInstance();
}catch (IllegalAccessException | InstantiationException exception)
{
exception.printStackTrace();
}
}
private class Item
{
}
}
然后很明显的是就会报找不到其无参的构造函数,如下报错内容
java.lang.InstantiationException: com.huo.TestReflection$Item
at java.lang.Class.newInstance(Class.java:418)
at com.huo.TestReflection.main(TestReflection.java:13)
Caused by: java.lang.NoSuchMethodException: com.huo.TestReflection$Item.<init>()
at java.lang.Class.getConstructor0(Class.java:3069)
at java.lang.Class.newInstance(Class.java:403)
... 1 more
然后我们把其的无参构造函数显示的表示
private class Item
{
public Item()
{
}
}
然后依然是会报错,如下的报错
java.lang.InstantiationException: com.huo.TestReflection$Item
at java.lang.Class.newInstance(Class.java:418)
at com.huo.TestReflection.main(TestReflection.java:13)
Caused by: java.lang.NoSuchMethodException: com.huo.TestReflection$Item.<init>()
at java.lang.Class.getConstructor0(Class.java:3069)
at java.lang.Class.newInstance(Class.java:403)
... 1 more
这个时候我想为什么会没有找到其的构造函数,难道内部类的构造函数有什么特殊的地方,还是说反射的方式查找的无参构造函数有什么特殊的地方,我们调试发现,其寻找的构造函数,确实有点特殊的地方。以及内部类中的构造函数的特殊地方。我们不免思考,这样的设计方式的目的在什么地方。
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
//这个地方就是查找当前的类的构造函数的地方
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
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);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
从getConstructor0 中我们看到了 Member.DECLARED 那么这个表示什么意思呢?
我们从源码中可以看到
Member is an interface that reflects identifying information about
a single member (a field or a method) or a constructor.
是一个反映有关单个成员(字段或方法)或构造函数的标识信息的接口。
DECLARED
标识类或接口的已声明成员的集合,不包括继承的成员。
我们来看具体的方法信息
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
//这一步有找到其的构造函数,且数量为1,然后再下面的判断中被舍弃,然后报找不到方法
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
我们来看下 arrayContentsEq 的意思,比较的意思是查看当前的构造函数是不是无参构造函数
private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
if (a1 == null) {
return a2 == null || a2.length == 0;
}
if (a2 == null) {
return a1.length == 0;
}
//这里直接返回了
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}
return true;
}
这里导致的其,没有无参构造函数,也就是指返回的构造函数不是无参构造函数,那么这个构造函数代表的是什么含义,我们来自己来获取下这个构造函数,看看为什么不是无参构造函数
package com.huo;
import java.lang.reflect.Constructor;
/**
* 反射的方式初始化测试
* @author huoruilin
*/
public class TestReflection
{
public static void main(String[] args)
{
Constructor[] constructors = Item.class.getDeclaredConstructors();
for(Constructor c:constructors){
System.out.println(c);
}
}
private class Item
{
public Item()
{
}
}
}
结果如下,我们发现了其会带上 TestReflection 这个外部类
public com.huo.TestReflection$Item(com.huo.TestReflection)
我们来看下真正执行的class文件中的内容一探究竟,我们看下TestReflection$Item.class这个内部类的实际执行内容
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.huo;
class TestReflection$Item {
public TestReflection$Item(TestReflection var1) {
this.this$0 = var1;
}
}
很明显其没有显示的无参构造函数,而且java文件中的无参构造直接被忽略了,从而导致了反射的方式实例化的方式没有成功。从这里我们可以和上面的查找其构造函数对应上了,其找到一个构造函数的原因。然后内部类是和外部的类进行关联的,想要创建内部的类,就需要先创建外部的类,然后才能创建内部的类,所以这里的构造函数会传一个外部的类。如果是一个静态的内部类我们会发现其是有无参构造函数的,然后这个内部类是独立的无需与外部类进行关联。
总结:
1 反射的方式进行实例化的操作是要查找其无参构造函数的,先从缓存中查找,缓存中没有查找到,再去虚拟机中查找
2 内部类的编译会生成一个带其外部类的一个构造函数,而且会直接忽视了其java中的无参构造,不参与 了编译【还是需要了解下编译的规则】
时间记录:2019-11-19