关于反射
开发过程汇总偶尔会用到反射相关的内容。每次使用到时都是在网上找Ctrl+CV,没去梳理总结过,今天正好想起来了,就做一个简单的总结,也加深一下印象。
在我的日常开发工作中,反射主要是用来调用一些被系统隐藏掉的API,以便实现某些需求。
实现反射的三种方式
- 使用Class类的静态方法forName(),用类的名字获取一个Class实例 ;
- 利用对象调用getClass()方法获取该对象的Class实例;
- 运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例;
示例
Book.java
package com.example.reflect;
/**
* @author: SuHang
* @date: 2021/9/14
* @desc: 示例类,含两个属性name和price,及四个对应的get/set方法,均为private类型。
*/
public class Book {
private String name = "Hello";
private float price;
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private float getPrice() {
return price;
}
private void setPrice(float price) {
this.price = price;
}
}
MainActvity.java
package com.example.reflect;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
reflect1();
}
/**
* 使用Class类的静态方法forName(),用类的名字获取一个Class实例
* 获取其中的成员变量及成员方法,并调用。
*/
private void reflect1() {
String bookName;
try {
//通过全路径获取Book类
Class<?> bookClass = Class.forName("com.example.reflect.Book");
//实例出Book类实例
Object book = bookClass.newInstance();
//获取全部成员变量
Field[] fields = bookClass.getDeclaredFields();
Log.d(TAG, "book fields size : " + fields.length);
for (int i = 0; i < fields.length; i++) {
Log.d(TAG, "book fields name : " + fields[i].getName());
}
//获取某个成员变量
Field nameField = bookClass.getDeclaredField("name");
/*
调用方法前,设置访问标志。参数值为true,打开禁用访问控制检查。
即 true 为可以访问, false 为不可以访问。
注意:此处设为true,并非将该方法从 private 变成了 public
public 方法默认属性也是true
*/
nameField.setAccessible(true);
Log.d(TAG, "nameField: " + nameField.get(book));
//获取全部成员方法
Method[] methods = bookClass.getDeclaredMethods();
Log.d(TAG, "book methods size : " + methods.length);
for (int i = 0; i < methods.length; i++) {
Log.d(TAG, "book methods name : " + methods[i].getName());
}
//获取成员方法 getName();
Method getNameMethod = bookClass.getDeclaredMethod("getName");
getNameMethod.setAccessible(true);
//调用成员方法 getName();
bookName = (String) getNameMethod.invoke(book);
Log.d(TAG, "before bookName: " + bookName);
//获取成员方法 setName();
Method setNameMethod = bookClass.getDeclaredMethod("setName", String.class);//需加上参数类型,防止方法重载
//调用成员方法 setName();
setNameMethod.setAccessible(true);
setNameMethod.invoke(book, "World");
//调用成员方法 getName();
bookName = (String) getNameMethod.invoke(book);
Log.d(TAG, "after bookName: " + bookName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
/**
* 方法二
* 利用对象调用getClass()方法获取该对象的Class实例
*/
private void reflect2() {
Book book = new Book();
Class<? extends Book> bookClass = book.getClass();
//.....
}
/**
* 方法三
* 运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例
*/
private void reflect3() {
Class<Book> bookClass = Book.class;
// 基本数据类型
Class<Integer> integerClass = Integer.TYPE;
//.....
}
}
输出结果:
D/MainActivity: book fields size : 2
D/MainActivity: book fields name : name
D/MainActivity: book fields name : price
D/MainActivity: nameField: Hello
D/MainActivity: book methods size : 4
D/MainActivity: book methods name : getName
D/MainActivity: book methods name : getPrice
D/MainActivity: book methods name : setName
D/MainActivity: book methods name : setPrice
D/MainActivity: before bookName: Hello
D/MainActivity: after bookName: World
关于 setAccessible(boolean b) 方法
调用方法前,设置访问标志。
参数值为true,打开禁用访问控制检查。
即 true 为可以访问, false 为不可以访问。
注意:此处设为true,并非将该方法从 private 变成了 public。public 方法默认属性也是true。
反射的一些常用方法
public Annotation[] getAnnotations () //获取这个类中所有注解
getClassLoader() //获取加载这个类的类加载器
getDeclaredMethods() //获取这个类中的所有方法
getReturnType() //获取方法的返回类型
getParameterTypes() //获取方法的传入参数类型
isAnnotation() //测试这类是否是一个注解类
getDeclaredConstructors() //获取所有的构造方法
getDeclaredMethod(String name, Class… parameterTypes)// 获取指定的构造方法(参数:参数类型.class)
getSuperclass() //获取这个类的父类
getInterfaces()// 获取这个类实现的所有接口
getFields() //获取这个类中所有被public修饰的成员变量
getField(String name) //获取指定名字的被public修饰的成员变量
newInstance() //返回此Class所表示的类,通过调用默认的(即无参数)构造函数创建的一个新实例
其他示例
由于目前没有实际需求需要用到反射,所以找了一个网上的示例,并未验证。
需求: 蓝牙开发的时候,自动配对的一些方法在API 19以后才对外开放的,这边我们就可以使用反射来实现配对功能了
try
{
Class<BluetoothDevice> bluetoothDeviceClass = BluetoothDevice.class;
bluetoothDeviceClass.getMethod("setPin", byte[].class).invoke(device, "1234".getBytes());
bluetoothDeviceClass.getMethod("createBond").invoke(device);
bluetoothDeviceClass.getMethod("setPairingConfirmation", boolean.class).invoke(device, true);
bluetoothDeviceClass.getMethod("cancelPairingUserInput").invoke(device);
}
catch (Exception e)
{
e.printStackTrace();
}
未完待续
以上内容只是对反射的基础使用进行了一些总结。未来在实际开发过程中再遇到有相关内容时,再来补充。