今天,我发现使用Collections.synchronizedXXX并不适合反射.
这是一个简单的例子:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Weird{
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Hello World");
List<String> wrappedList = Collections.synchronizedList(list);
printSizeUsingReflection(list);
printSizeUsingReflection(wrappedList);
}
private static void printSizeUsingReflection(List<String> list) {
try {
System.out.println(
"size = " + list.getClass().getMethod("size").invoke(list));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
第一次调用printSizeUsingReflection打印大小(即"1"),第二次调用导致:
java.lang.IllegalAccessException: Class Weird can not access a member of class
java.util.Collections$SynchronizedCollection with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Method.invoke(Method.java:588)
at Weird.printSizeUsingReflection(Weird.java:18)
at Weird.main(Weird.java:13)
Run Code Online (Sandbox Code Playgroud)
这有点令人惊讶和恼人.有一个很好的解决方法吗?我知道java.util.concurrent中有一个线程安全的List实现,但该实现似乎比使用Collections.synchronizedList()慢.
抛出异常的原因是通过调用获得的类
list.getClass()
Run Code Online (Sandbox Code Playgroud)
在线
System.out.println("size = " + list.getClass().getMethod("size").invoke(list));
Run Code Online (Sandbox Code Playgroud)
返回实际类型 - java.util.Collections $ SynchronizedCollection.SynchronizedCollection类恰好是Collections类的内部类,您无法访问它,因此异常.
绕过这个异常的最好方法是在更合适的类/接口上调用size方法 - 通常这恰好是实现类(因为它肯定包含方法声明或定义),但在我们的例子中我们需要调用给定obj.getClass()返回非公共类型的公共类型的size().具体来说,我们需要在java.util.Collection(超级)接口或java.util.List接口上调用size().
以下语句将在控制台中打印出"size = 1":
System.out.println("size = " + Collection.class.getMethod("size").invoke(list));
System.out.println("size = " + List.class.getMethod("size").invoke(list));
Run Code Online (Sandbox Code Playgroud)
更新
如果你想知道通过反射调用的size()方法是否同步,答案是肯定的,它是同步的.尽管在超类型 - Collection/List上调用了size()方法,但同步调用了SynchronizedCollection内部类中的size()方法,这恰好是同步的.这是一个实现细节,但保证在包装集合后工作.
此外,当超类型 - Collection接口包含size()方法时,使用反射不是一个好主意.
归档时间: |
|
查看次数: |
1784 次 |
最近记录: |