Java进阶-反射机制

一、概念

Java反射是Java被视为动态(或准动态)语言的一个关键性质。指在程序运行状态中,可以构造任意一个类,可以调用获取任意一个类的方法、变量、构造器等。这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制。反射被视为动态语言的关键,框架设计的灵魂。《百度百科》

以上的解释不理解没关系,下面来结合图分析程序执行的流程:
首先javac编译源代码之后,通过类加载器进内存生成字节码文件对象。一个类的字节码文件大概可以拆分为:包信息、成员变量、构造方法、成员方法等等,个个组成部分映射成一个个对象。图中就是类的正常加载过程,反射的原理就在于如何去操作这些对象

Java代码在计算机中大致分为三个阶段:Source、Class、Runtime

image.png

Person person = new Person(); 执行!
JVM 启动后,javac 会把 Person.java 编译成一个 Person.class 文件,通过 ClassLoader 加载进内存的方法区中,类对象是用来描述字节码文件对象的,每个类只有一个class对象且只会加载一次,作为方法区类的数据结构的接口。然后创建Person的类实例到堆(Heap)内存中。注意:Jvm 在创建对象前,会先检查类是否加载,寻找类对应的class对象,加载完成之后,会给对象分配内存。

了解大致过程之后,发现Person是new出来写死给Jvm执行的,假设 当我们的程序在运行时,需要动态的加载一些类,这些类可能运行之前不确定是否需要加载到Jvm中,而是在运行时根据需要才去加载 。这就需要用到反射机制。

举个栗子:目前主流开发所用的 spring 框架,在配置各种各样的 bean 时,是以配置文件的形式配置的,spring 容器会根据配置的信息去动态加载类。 Spring的工作原理就是让一个对象的创建不用new就可以自动的生产,在运行时与xml Spring的配置文件来动态的创建对象和调用对象,而不需要通过代码来关联。

相关类
类名 释义
Class 类对象,在运行时java类数据结构的接口
Field 类属性(成员变量)
Method 类方法
Constructor 类构造方法

二、Class类

Class对象的三种获取方式

public class Reflection {
 /**
 * 1.Source阶段,通过将class字节码文件加载进内存,返回 class 对象
 * 2.class对象阶段,通过类名的属性 class 来获取
 * 3.Runtime阶段,通过Object.getClass()方法来获取
 *
 * @param args anything
 * @throws ClassNotFoundException
 */
 public static void main(String[] args) throws ClassNotFoundException {
 // 1.多用于配置文件,将类名配置在文件中,读取文件加载类
 Class aClass = Class.forName("com.angst.student.Reflection");
 // 2\. 一般用于参数传递,当做参数使用
 Class aClass1 = Reflection.class;
 // 3\. 多用于对象获取字节码
 Class aClass2 = new Reflection().getClass();

 }
}
Class
方法 释义
getClassLoader() 获得类的加载器
getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
getName() 获得类的完整路径名字
getPackage() 获得类的包
getSimpleName() 获得类的名字
getSuperclass() 获得当前类继承的父类的名字
getInterfaces() 获得当前类实现的类或是接口
asSubclass() 把传递的类的对象转换成代表其子类的对象
newInstance() 创建一个空参的对象(无参构造使用)
/**
 *  通过类完整路径获取 Class 对象
 * @param packagePath 包路径
 * @return  class
 * @throws ClassNotFoundException
 */
 public static Class getClass(String packagePath) throws ClassNotFoundException {
 return Class.forName(packagePath);
 }

 /**
 *  通过包名+类名返回 class 对象
 * @param packagePath 包路径
 * @param obj  类名
 * @return class
 * @throws ClassNotFoundException
 */
 public static Class getClass(String packagePath, String obj) throws ClassNotFoundException {
 if (obj.indexOf(".") == 0) { obj = packagePath + obj; }
 return getClass(obj);
 }
Constructor
方法 释义
getConstructor() 获得该类中与参数类型匹配的public构造方法
getConstructors() 获得该类的所有public构造方法
getDeclaredConstructor() 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法
newInstance() 根据传递的参数创建类的对象(new Object)
/**
 * 获取构造器
 * @param clazz  类对象
 * @param parameterTypes  参数类型
 * @return Constructor
 * @throws NoSuchMethodException
 */
 public static Constructor getConstructor(Class clazz, Class... parameterTypes) throws NoSuchMethodException {
 return clazz.getDeclaredConstructor(parameterTypes);
 }
Field
方法 释义
getField() 获得某个public属性对象
getFields() 获得所有public属性对象
getDeclaredField() 获得某个属性对象(包括私有)
getDeclaredFields() 获得所有属性对象(包括私有)
setAccessible() 设置为true,忽略访问权限修复符,操作 private 类属性(暴力反射不安全)
/**
 * 设置某个字段的值
 * @param clazz 类对象
 * @param fieldName  属性字段名称
 * @param value 设置值
 * @throws Exception
 */
 public static void setField(Class clazz, String fieldName, String value) throws Exception {
 Object obj = clazz.newInstance();
 Field declaredField = clazz.getDeclaredField(fieldName);
 declaredField.setAccessible(true);
 declaredField.set(obj, value);
 }

 /**
 *   获取某个字段的值
 * @param clazz
 * @param fieldName
 * @return
 * @throws Exception
 */
 public static Object getField(Class clazz, String fieldName) throws Exception {
 Object obj = clazz.newInstance();
 Field declaredField = clazz.getDeclaredField(fieldName);
 declaredField.setAccessible(true);
 return declaredField.get(obj);
 }

 /**
 *  获取所有字段名称列表
 * @param clazz
 * @return
 */
 public static List getFields(Class clazz) throws IllegalAccessException, InstantiationException {
 List list = new ArrayList<>();
 Object obj = clazz.newInstance();
 Field[] fieldList = clazz.getDeclaredFields();
 for (Field fld : fieldList) {
 list.add(fld.get(obj));
 }
 return list;
 }

 /**
 * 获取所有字段名称+类型以 hashMap 返回
 * @param clazz
 * @return
 */
 public static HashMap> getFieldMap(Class clazz) {
 HashMap> hashMap = new HashMap>();
 Field[] fieldList = clazz.getDeclaredFields();
 for (Field fld : fieldList) {
 hashMap.put(fld.getName(), fld.getType());
 }
 return hashMap;
 }

 
 
Method
方法 释义
getMethod() 获得该类某个 Public 方法
getMethods() 获得该类所有 Public 方法
getDeclaredMethod() 获得该类某个方法(包括私有)
getDeclaredMethods() 获得该类所有方法(包括私有)
invoke() 根据传递的object对象及参数调用该对象的方法
 * 获取所有方法列表
 * @param clazz 类对象
 * @return
 */
 public static List getMethods(Class clazz) {
 List list = new ArrayList<>();
 for (Method declaredMethod : clazz.getDeclaredMethods()) {
 declaredMethod.setAccessible(true);
 list.add(declaredMethod);
 }
 return list;
 }

 /**
 * 指定方法方法调用
 * @param clazz 类对象
 * @param methodName 调用的方法名称
 * @param value 方法入参
 * @param type 入参类型
 * @throws Exception
 */
 public static void invokeMethod(Class clazz, String methodName, String value, Class... type) throws Exception {
 Object obj = clazz.newInstance();
 Method method = clazz.getDeclaredMethod(methodName, type);
 method.invoke(obj, value);
 }
 
 
Other
方法 释义
isAnnotation() 如果是注解类型则返回true
isAnnotationPresent() 如果是指定类型注解类型则返回true
isAnonymousClass() 如果是匿名类则返回true
isArray() 如果是一个数组类则返回true
isEnum() 如果是枚举类则返回true
isInstance() 如果obj是该类的实例则返回true
isInterface() 如果是接口类则返回true
isLocalClass() 如果是局部类则返回true
isMemberClass() 如果是内部类则返回true

案例

从上面的一些方法示例来看,反射的操作过程比较麻烦,还不如使用new对象来的简单直接。更别说反射是框架设计的灵魂这一说法,并没有体现出本身的任何价值。下面会通过简单的案例演示,可以初步了解到反射的魅力所在。

需求:在不改变该类任何代码前提下,任意创建类对象,并且执行其中任意方法。通过配置文件来实现反射机制。

步骤:
1.将需要创建的对象的类路径和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件,并通过反射机制将类加载进内存
3.创建对象,执行指定方法

配置文件定义
#pro.properties
className=org.example.basic.Person
methodName=eat
配置类
package org.example.basic;

public class Person {

 public void eat() {
 System.out.println("anything...");
 }
}
反射类
package org.example.basic;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;


public class Reflection {
 private static String className;
 private static String methodName;

 /**
 * 加载配置文件
 */
 static {
 Properties properties = new Properties();
 try {
 properties.load(Reflection.class.getClassLoader().getResourceAsStream("pro.properties"));
 className = properties.getProperty("className");
 methodName = properties.getProperty("methodName");
 } catch (IOException e) {
 e.printStackTrace();
 }
 }

 /**
 * 加载类进内存,创建对象,执行指定方法
 * @throws Exception
 */
 public static void invokeMethod() throws Exception {
 Class aClass = Class.forName(className);  // 类加载进内存,返回类对象
 Object obj = aClass.newInstance();  // 创建对象
 Method method = aClass.getMethod(methodName);  // 获取指定方法
 method.invoke(obj);  // 执行方法调用
 }


 public static void main(String[] args) throws Exception {
 Reflection.invokeMethod();  // 方法调用输出:anything...

 }
}

Epilogue

以上就是通过动态加载配置文件的类和方法,在不改变该类任何代码前提下,可以实现任意创建类对象,并且执行其中任意方法。可以达到一个解耦的目的,提升程序的扩展性。

反思?

通过以上的反射相关类及方法的介绍和案例的使用,已经初步了解的Java反射机制的强大之处,那么如何结合反射应用到实际工作中或者编写一个简单“测试框架”?

你可能感兴趣的:(Java进阶-反射机制)