Java的反射(Reflection)
1.什么是反射
反射(Reflection)是Java被称为“准动态语言”的关键技术。反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
正常方式是通过完整的“包类”名称通过new来实例化对象。反射方式可以通过实例化对象调用getClass()方法来获得完整的“包类”名称。
2.Class类
在Object类中定义了getClass()方法,该方法的返回值是Class类,这个类是Java反射的源头。针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
//获取Class对象的方法
public static void main(String[] args) throws ClassNotFoundException {
//方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1);
//方式二:forName获得
Class c2 = Class.forName("包名+类名");
System.out.println(c2);
//方式三:通过类名.class获得
Class c3 = Person.class;
System.out.println(c3);
}
3.类加载器
-
类的加载过程:
(1)加载(Load):将class文件加载到内存中,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
(2)链接(Link):将类的二进制数据合并到JVM中的过程。
(3)初始化(Initialize):执行类构造器()方法的过程。由JVM负责对类进行初始化。
-
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父加载器-->根加载器(C/C++)
ClassLoader parentParent = parent.getParent();
System.out.println(parentParent);
}
4.从Class对象获取信息
(1)访问 Class 对应的类所包含的属性
- 批量的属性:
public Field[] getFields():获取所有public属性
public Field[] getDeclaredFields():获取所有的属性 - 获取单个属性:
public Field getField(String name): 获取单个的public属性
public Field getDeclaredField(String name):获取任何属性
(2)访问 Class 对应的类所包含的方法
- 批量的方法:
public Method[] getMethods():获取所有public构造器
public Method[] getDeclaredMethods():获取所有的构造器 - 获取单个的方法:
public Method getMethod(String name, Class… parameterTypes): 获取单个的public方法
public Method getDeclaredMethod(String name, Class… parameterTypes):获取任何方法
(3)访问 Class 对应的类所包含的构造方法
- 批量的方法:
public Constructor[] getConstructors():获取所有public构造器
public Constructor[] getDeclaredConstructors():获取所有的构造器 - 获取单个的方法:
public Constructor getConstructor(Class… parameterTypes): 获取单个的public构造器
public Constructor getDeclaredConstructor(Class…parameterTypes):获取任何构造器
测试代码如下:
- 首先创建一个User实体类
class User{
private int id;
private int age;
private String name;
public User() {}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 通过Class对象获取该实体类的方法、属性以及构造器
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.carry.reflection.User");
//获得类的名字
System.out.println(c1.getName());//包名+类名
System.out.println(c1.getSuperclass());//类名
//获得类的属性
System.out.println("=======================");
Field[] fields = c1.getFields();//只能找到public的属性
fields = c1.getDeclaredFields();//找到全部属性
for(Field field : fields){
System.out.println(field);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods();//获得本类及其父类的所有方法
for (Method method : methods) {
System.out.println("正常的"+method);
}
methods = c1.getDeclaredMethods();//获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods"+method);
}
//获得指定方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得类的构造器
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("Declared"+constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class,int.class,String.class);
System.out.println("指定"+declaredConstructor);
}
5.Class动态创建对象
- 使用 Class 对象的 newInstance() 方法创建对象
- 使用 Constructor 对象创建对象
测试代码如下:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("com.carry.reflection.User");
//构造一个对象
User user = (User) c1.newInstance();//本质是调用了类的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(int.class, int.class, String.class);
User user2 = (User)constructor.newInstance(1, 18, "hello");
System.out.println(user2);
//通过反射调用普通方法
User user3 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活的意思(对象,“方法的值”)
setName.invoke(user3,"hi");
System.out.println(user3.getName());
//通过反射操作属性
System.out.println("======================");
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
name.setAccessible(true);//取消安全检测
name.set(user4,"hi2");
System.out.println(user4.getName());
}
6.通过反射获取泛型
通过反射的方法可以获得方法中的泛型。
- 首先创建两个测试方法,这两个方法的返回值和参数分别为泛型。
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
- 分别通过Class获得两个方法的泛型。.
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("#"+genericParameterType);
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
method = Test.class.getMethod("test02",null);
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
7.通过反射获取注解
- 定义两个注解
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableHello{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldHello{
String columnName();
String type();
int length();
}
- 定义一个实体类,在实体类中使用这两个注解
@TableHello("db_student")
class Student{
@FieldHello(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldHello(columnName = "db_age",type = "int",length = 10)
private int age;
@FieldHello(columnName = "db_name",type = "varchar",length = 5)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 在主方法中获取实体类中的注解
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.carry.reflection.Student");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableHello tableHello = (TableHello) c1.getAnnotation(TableHello.class);
String value = tableHello.value();
System.out.println(value);
//获得类指定的注解
Field field = c1.getDeclaredField("name");
FieldHello annotation = field.getAnnotation(FieldHello.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}