反射就是把java类中的各种成分映射成一个个的 Java 对象
反射的主要目的是为了获取受保护的或者是隐藏的 类/字段/方法, 然后调用.
在反射中所有的功能都是基于字节码 ( class ). class 在内存中应该只有一份, 而且 class 其实也是一个对象 Class.
- 反射方式一, 构造函数的调用
已知TestBean
的构造函数为私有, 外界无法通过new TestBean
来创建对象
public class TestBean {
private String name = "zhangsan";
private TestBean(String name) {
this.name = name;
}
private void sysName() {
Log.e("--TestBean--", name);
}
}
调用方式
Constructor<TestBean> constructor = TestBean.class.getDeclaredConstructor(String.class);
//如果调用了无参构造, newInstance()不需要传递参数
TestBean testBean = constructor.newInstance("王二");
getConstructors()
所有公有的构造方法
getDeclaredConstructors()
获取所有的构造方法(包括私有、受保护、默认、公有)
getConstructor(Class... parameterTypes)
获取单个的公有的构造方法
getDeclaredConstructor(Class... parameterTypes)
获取某个构造方法可以是私有的, 或受保护, 默认, 公有
- 反射方式二, 私有方法的调用
在方式一的基础上想要调用调用TestBean.sysName()
方法
Method method = TestBean.class.getDeclaredMethod("sysName");
//忽略访问修饰符
method.setAccessible(true);
//参数 1, 就是要调用的对象, 可以理解为, 调用发生在那个对象内,
//参数 2..... 可为多个参数, 如果要调用的方法有参数, 在参数 2 处可输入多个实参.
method.invoke(testBean);
getMethods()
获取所有公有方法 (包含了父类的方法也包含 Object 类)
getDeclaredMethods()
获取所有的成员方法, 包括私有的(不包括继承的)
getMethod(String name,Class<?>... parameterTypes)
获取单个的.
- 反射方式三, 获取私有字段
Field field = TestBean.class.getDeclaredField("name");
field.setAccessible(true);
//参数 1, 要获取字段所在的对象
String name = (String) field.get(testBean);
getFields()
获取所有共有字段
getDeclaredFields()
获取所有字段, 包括: 私有, 受保护, 默认, 公有
getField(String fieldName)
获取某个公有的字段
getDeclaredField(String fieldName)
获取某个字段(可以是私有的)
这里获取到的 name
值, 就为 TestBean
中的 name
值.
- 反射方式四, Class.forName
有时候, 我们是无法拿到xxx.class
的, 比如 Android 中的ActivityThread
. 这时候就需要Class.forName
来帮我们达到这一目的了. 例如我们需要获取到ActivityThread
中的私有静态变量sCurrentActivityThread
Class clazz = Class.forName("android.app.ActivityThread");
Field currentActivityThread = clazz.getDeclaredField("sCurrentActivityThread");
currentActivityThread.setAccessible(true);
// 如果是静态的, 可以直接传入 Null
Object o = currentActivityThread.get(null);
使用注解和反射实现一个简单的 findViewById
- 先声明一个注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FindViewById {
int value();
String name() default "zhangsan";
}
- 接着在工具类中, 创建一个方法来反射获取传入的
Activity
中所有的字段, 接着遍历所有字段, 判断字段是否包含我们声明的注解. 如果包含, 表示是我们需要的, 然后开始注入findViewById
public class ViewUtils {
public static void inject(Activity activity){
//通过反射拿到所有的字段
Field[] fields = activity.getClass().getDeclaredFields();
for (Field field : fields) {
//只要加了我们这个注解的属性
FindViewById findViewById = field.getAnnotation(FindViewById.class);
if(findViewById != null){
View view = activity.findViewById(findViewById.value());
try {
//开始注入
field.setAccessible(true);
//第一个参数表示 属性所在类, 第二个表示属性的值
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
- 在
MainActivity
中使用
public class MainActivity extends AppCompatActivity {
@FindViewById(R.id.tv)
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtils.inject(this);
tv.setText("2222");
}
}
网友评论