反射2--Field,简单的实现和部分源码分析
### 反射2–Field
“`
public class Person {
private int age;
public String name;
{
age = 11;
name = “yoyoyo”;
}
static {
System.out.println(“Hello”);
}
public Person() {}
public Person(String name) {
this.name = name;
System.out.println(this.name);
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
“`
### 反射获取参数
“`
public class reflectTest {
public static void main(String[] args) throws Exception {
//1.加载class,实例化对象
Person person = (Person)Class.forName(“POJO.Person”).newInstance();
//2.获取参数
//getDeclaredField可以获取类(不包括超类)中所有作用域(修饰符),因此可以获得private int age
Field field = person.getClass().getDeclaredField(“age”);
//getField可以获取类或超类的公有域(public),所以无法获取private int age,但可以获得public String name;
//Field field = person.getClass().getField(“name”);
//3.当访问private作用域的参数时,需要setAccessible(true),反射的对象在使用时取消Java语言访问检查
//public的参数可以不写setAccessible(true)
field.setAccessible(true);
//field.getModifiers()获得参数作用域
//field.getType()获取参数类型
//field.getName()获取参数名字
//field.get(obj)获取值,返回为object
System.out.println(“first field:”+Modifier.toString(field.getModifiers())+” “+field.getType()+” “+field.getName()+” = “+field.get(person));
//为field设置新值,如果是基本类型,会自动解包
field.set(person,10010);
System.out.println(“last field:”+Modifier.toString(field.getModifiers())+” “+field.getType()+” “+field.getName()+” = “+field.get(person));
}
}
“`
###注意:并不是说private的Accessible是false,而public的Accessible就是true(事实是false),实际上field.isAccessible()是获取此对象的可访问标志的值,说人话就是“是否允许get,set”。
“`
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
//checkMemberAccess进行java的安全验证和访问权限检查,
//Reflection.getCallerClass()可以获取到调用这个方法的类
//PUBLIC允许反射
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
“`
“`
private void checkMemberAccess(int which, Class caller, boolean checkProxyInterfaces) {
//java安全管理器,进行访问控制和权限控制,防止运行未知java程序被恶意代码对系统产生影响
//https://docs.oracle.com/javase/8/docs/api/java/lang/SecurityManager.html
final SecurityManager s = System.getSecurityManager();
//异常则返回null
if (s != null) {
//获取调用类的加载器
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
//调用native本地方法,返回classLoader
final ClassLoader cl = getClassLoader0();
//PUBLIC=0,DECLARED=1,Member.DECLARED可以禁止反射
if (which != Member.PUBLIC) {
//如果classLoader和本地获取的不一致
if (ccl != cl) {
//验证安全管理器是否有权限
//SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION指运行时权限 “可以访问以声明的类成员”
//RuntimePermission(“accessDeclaredMembers”);
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
//验证类是否有包权限
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
}
“`
“`
private Field getField0(String name) throws NoSuchFieldException {
Field res;
// 搜索声明的公共字段
if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {
return res;
}
// 递归搜索接口
Class[] interfaces = getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class c = interfaces[i];
if ((res = c.getField0(name)) != null) {
return res;
}
}
// 递归搜索父类
if (!isInterface()) {
Class c = getSuperclass();
if (c != null) {
if ((res = c.getField0(name)) != null) {
return res;
}
}
}
return null;
}
“`
#####题外话,关于反射的速度区别
先说结论,“(在常规条件下,也就是我的例子的情况下)setAccessible(true)将关闭java的安全检查,大幅度提升反射速度”。
“`
public class reflectSpeed {
public static void main(String[] args) throws Exception{
SecurityManager sm = new SecurityManager();
sm.checkMemberAccess(Person.class, Member.DECLARED);
Person person = (Person)Class.forName(“main.java.Person”).newInstance();
person.setName(“firstName”);
Method method = person.getClass().getMethod(“getName”);
//getName是public,所以不需要setAccessible(true)
long start = System.currentTimeMillis();
for(int i = 0;i<10000000;i++){
method.invoke(person);
}
System.out.println(“simple:”+(System.currentTimeMillis()-start));
//看一下setAccessible(true)之后的耗时
method.setAccessible(true);
long start1 = System.currentTimeMillis();
for(int i=0;i<10000000;i++){
method.invoke(person);
}
System.out.println(“setAccessible(true):”+(System.currentTimeMillis()-start1));
//当然不用反射的耗时也作为对比
long start2 = System.currentTimeMillis();
for(int i=0;i<10000000;i++){
person.getName();
}
System.out.println(“getName:”+(System.currentTimeMillis()-start2));
}
}
“`