ClassCastException时常见,只要两个不同类强转换就会有这种问题,不过下面这种错误不知道见过没
Caused by: java.lang.ClassCastException: Socker cannot be cast to Socker at Socker.setInstance(Socker.java:10) … 6 more
说明一下,前后两个Socker是同一个类的不同对象,来看看是怎么弄出这个错误的,我们用的还是昨天自定义ClassLoader的代码
public class Ming {
public static void main(String[] args) {
System.out.println("Ming looking for socker");
checkClassCast();
}
private static void checkClassCast() {
try {
DiskClassLoader loader1 = new DiskClassLoader("../WangHouse");
DiskClassLoader loader2 = new DiskClassLoader("../WangHouse");
Class class1 = loader1.findClass("Socker");
Class class2 = loader2.findClass("Socker");
Object socker1 = class1.newInstance();
Object socker2 = class2.newInstance();
Method method = class1.getDeclaredMethod("setInstance", Object.class);
method.invoke(socker1, socker2);
} catch(Exception e){
e.printStackTrace();
}
}
}
public class Socker {
private Socker mInstance;
public void setInstance(Object socker) {
System.out.println("Socker set");
this.mInstance = (Socker)socker;
}
}
我们构造了两个DiskClassLoader对象,分别加载了同一个Socker类实例化两个对象,然后用反射把其中一个通过set方法注入给另外一个Socker对象。
然后异常就发生了。
是不是没道理?明明都是Socker类的对象,这种转换都不行的吗?
其实原因很简单,在JAVA中,判断两个类是否相同,不仅要判断类名,还要判断是否由同一个ClassLoader加载的。上面的代码中因为我们实例化了两个ClassLoader,两个Socker对象在不同的ClassLoader中,因此不满足类相同的条件,就不能转换。
如果把代码调整一下变成用同一个ClassLoader加载的话
private static void checkClassCast() {
try {
DiskClassLoader loader1 = new DiskClassLoader("../WangHouse");
// DiskClassLoader loader2 = new DiskClassLoader("../WangHouse");
Class class1 = loader1.findClass("Socker");
// Class class2 = loader2.findClass("Socker");
Object socker1 = class1.newInstance();
Object socker2 = class1.newInstance();
Method method = class1.getDeclaredMethod("setInstance", Object.class);
method.invoke(socker1, socker2);
} catch(Exception e){
e.printStackTrace();
}
}
输出结果如下
Ming looking for socker Socker set
这样就可以了。
通常在开发中我们不会遇到这种奇葩问题。不过如果在Android开发涉及到热修复的时候,就有可能遇到这种问题了。 一般场景是自定义了一个ClassLoader用来加载插件dex,而主Loader中又加载了相同的类,那么就有可能发生这种问题。