Java反射API
反射定义:
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射最常用到的操作:
取得属性的修饰符,属性名,属性值,设置属性值,取得所有的属性。
操作方法,取得方法的修饰符,方法名,方法参数,方法参数类型,方法返回值,取得一个类的所有方法。
反射可以动态的调用、修改一个对象的任何属性、方法(包括私有属性和方法)。
Field
访问字段:
Field getField(name) : 根据字段名获取某个 public 的field(包括父类);
Field getDeclaredField(name) : 根据字段名获取当前类的某个 Field(不包括父类,可以获取private属性)
Field[] getFields() : 获取所有 public 的 field(含父类);
Field[] getDeclaredFields() : 获取当前类的所有 filed (不包括父类);
Field对象包含的信息:
Object get(Object obj)
getName() :返回字段名称;
getType() : 返回字段类型, 一个 Class 实例;
getAnnotation(xxx.class) : 获取 xxx 注解;
Type getGenericType() # 字段类型(能正确表示出泛型) java.util.List<java.lang.String> ,ParameterizedType的实例;
field.set(Object,Object) # 设置字段值:(指定的实例,待修改的值);
getModifiers() : 字段修饰符,是一个 int;
Method
通过 Class 获取 Method:
Method getMethod(name,Class…) # 获取某个 public 的 Method(包括父类);
Method getDeclaredMethod(name,Class…) # 获取当前类的某个 Method(不包括父类);
Method[] getMethods() # 获取所有 public 的Method(包括父类);
Method[] getDeclaredMethods() # 获取当前类的所有Method(不包括父类);
Method 对象包含一个方法的所有信息
getName() : 返回方法名称;
getReturnType() : 返回方法的返回值类型,一个 Class 实例;
getParameterTypes() : 返回方法的参数类型,是一个 Class 数组
getParameterAnnotations() : 返回方法参数的所有注解,是一个Annotation[][]
1、取得一个类的Class对象的三种方式
//第一种 Class<?> clazz1 = Class.forName("com.wp.reflect.entity.Child"); //第二种 Class<? extends Child> clazz2 = new Child().getClass(); //第三种 Class<?> clazz3 = Child.class;
2、获取一个类的父类以及所有实现的接口
Class<?> superclass = clazz1.getSuperclass();
Class<?>[] interfaces = clazz1.getInterfaces();
//取得一个类的全类名(包名 + 类名) clazz1.getName() //取得类的简单名称 clazz1.getSimpleName();
3、构造函数
1、取得一个类的所有构造函数
Class<?> clazz1 = Class.forName("xxxx"); Constructor<?>[] constructors = clazz1.getConstructors(); Constructor<?>[] constructors1 = clazz1.getDeclaredConstructors();
2、根据参数类型取得构造函数
//取得只有一个参数且参数类型为String的构造方法 Constructor<?> constructor1 = clazz1.getDeclaredConstructor(String.class); System.out.println(constructor1);
4、通过反射实例化对象
1、通过Class对象的newInstance()方法
Object instance = clazz1.newInstance();
2、通过构造方法实例化对象
//取得无参构造 Constructor<?> constructor2 = clazz1.getConstructor(); //通过无参构造创建一个对象 Object child1 = constructor2.newInstance(); //取得指定参数的构造方法对象 Constructor<?> constructor3 = clazz1.getConstructor(String.class, int.class); //通过构造方法对象创建一个对象 constructor3.newInstance("wenpan",21);
5、反射操作属性
1、取得本类的所有属性(不包含父类和接口)
Field[] declaredFields = clazz1.getDeclaredFields(); for (int i = 0; i < declaredFields.length; i++) { //属性名称 String fieldName = declaredFields[i].getName(); //属性的类型 String fieldType = declaredFields[i].getType().getName(); //属性修饰符 String fieldModifier = Modifier.toString(declaredFields[i].getModifiers()); System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";"); }
2、取得实现的接口里的所有属性
Field[] fields = clazz1.getFields(); for (int i = 0; i < fields.length; i++) { //属性名称 String fieldName = fields[i].getName(); //属性的类型 String fieldType = fields[i].getType().getName(); //属性修饰符 String fieldModifier = Modifier.toString(fields[i].getModifiers()); System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";"); }
3、通过指定属性名取得属性
//通过属性名取得属性
Field field = clazz1.getDeclaredField("name");
//访问私有属性需要设置Accessible为true才可以更改或读取值(get 或 set)
field.setAccessible(true);
//取得instance对象里面的属性值
System.out.println("更改前的name值:" + field.get(instance));
//更改instance对象里的name属性值
field.set(instance,"文攀啊");
System.out.println("更改后的name属性值:" + field.get(instance));
通过属性名获取属性: Field field = clazz1.getDeclaredField("name");
返回的是属性对象: Field
6、反射操作方法
1、取得一个类的所有方法(包括父类和实现的接口)
Method[] methods = clazz1.getMethods();
Method[] methods = clazz1.getMethods(); //输出取得的方法 for (int i = 0; i < methods.length; i++) { StringBuffer buffer = new StringBuffer(); //方法修饰符 String modifier = Modifier.toString(methods[i].getModifiers()); //返回值类型 String returnType = methods[i].getReturnType().getSimpleName(); //方法名 String name = methods[i].getName(); //方法参数类型 Class<?>[] parameterTypes = methods[i].getParameterTypes(); buffer.append(modifier).append(" ").append(returnType).append(" "). append(name).append("("); for (int j = 0; j < parameterTypes.length; j++) { buffer.append(parameterTypes[j].getSimpleName()).append(" arg").append(j); if(j < parameterTypes.length - 1){ buffer.append(","); } } buffer.append(")"); //方法抛出的异常信息 Class<?>[] exceptionTypes = methods[i].getExceptionTypes(); if(exceptionTypes.length > 0){ buffer.append(" throws"); } for (int k = 0; k < exceptionTypes.length; k++) { buffer.append(exceptionTypes[k].getSimpleName()); if(k < exceptionTypes.length -1){ buffer.append(","); } } buffer.append("{ }"); System.out.println(buffer); }
2、取得一个类的所有方法(不包括父类和实现的接口)
Method[] declaredMethods = clazz1.getDeclaredMethods();
3、通过反射调用某个对象的方法
要调用某个对象的方法,首先这个对象得存在,所以可以通过反射创建一个对象
public static void main(String[] args) throws Exception { //使用反射将Child类的Class对象加载进来 Class<?> clazz1 = Class.forName("com.wp.reflect.entity.Child"); //创建一个实例对象(使用反射调用方法必须要有实例对象) Object instance = clazz1.newInstance(); //通过方法名称和指定的参数类型获取指定方法 Method method = clazz1.getDeclaredMethod("setName", String.class); //调用Method对象的invoke方法执行instance对象的方法 method.invoke(instance,"文攀啊"); //通过方法名称和指定的参数类型获取指定方法 Method getNameMethod = clazz1.getMethod("getName"); //调用Method对象的invoke方法执行方法 String name = (String)getNameMethod.invoke(instance); //输出执行方法返回的结果 System.out.println(name); }
通过方法名和方法参数类型取得对象的方法:Method method = clazz1.getDeclaredMethod(“setName”, String.class);
7、反射操作注解
1、通过反射获取类上的注解,类属性上的注解
public static void main(String[] args) throws Exception{ User user = new User(); // 取得类上的所有注解 Annotation[] annotations = user.getClass().getAnnotations(); // 获取类上指定类型的注解 MyAnnotation annotation = user.getClass().getAnnotation(MyAnnotation.class); // 获取类的某个属性上的所有注解 Annotation[] allAnnotations = user.getClass().getDeclaredField("name").getAnnotations(); // 获取类的某个属性上指定类型的注解 MyAnnotation annotation1 = user.getClass().getDeclaredField("name").getAnnotation(MyAnnotation.class); // 获取注解的属性 String value = annotation1.value(); String pattern = annotation1.pattern(); }
8、反射实例应用
1、使用反射向一个int集合添加一个String元素
**原理:**首先通过反射取得该List集合的add()方法,然后使用反射调用该方法向list集合里面添加一个String类型元素
//创建一个int类型的集合 ArrayList<Integer> list = new ArrayList<Integer>(); //取得集合的添加方法 Method addMethod = list.getClass().getMethod("add", Object.class); //执行集合的add方法,向集合中添加一个String类型元素 addMethod.invoke(list, "wenpan"); System.out.println("取得元素=========================>" + list.get(0));
2、通过反射修改数组元素的值
**原理:**其实就是通过反射中的 Array.get()
和 Array.set()
来读取和修改数组中的元素值。
int temp[] = {1,2,3,4,5}; System.out.println("数组第一个元素:" + Array.get(temp,0)); Array.set(temp,0,100); System.out.println("修改之后数组第一个元素为:" + Array.get(temp,0));
扩展方法:
int temp[] = {1,2,3,4,5}; //取得数组的类型,该方法为动态扩展数组大小做铺垫 Class<?> componentType = temp.getClass().getComponentType(); System.out.println("数组类型:" + componentType.getName()); System.out.println("数组长度:" + Array.getLength(temp));
3、通过反射修改数组的大小
**原理:**其本质就是通过反射得到原数组的类型,然后通过Array.newInstance()方法根据数组类型创造出一个指定长度的新数组,最后使用System.arraycopy()方法将原数组的值拷贝到新数组中
public static Object arrayInc(Object obj, int length) { //取得传入数组的类型,以便于创造出同类型的数组 Class<?> componentType = obj.getClass().getComponentType(); //根据传入的数组类型创建出新的指定长度的数组实例 Object newArr = Array.newInstance(componentType, length); //原有的数组长度 int originArrLen = Array.getLength(obj); //将原有的数组数据拷贝到新的数组中去 System.arraycopy(obj,0,newArr,0,originArrLen); //返回修改大小后的数组 return newArr; }
4、通过反射实现通用工厂模式
class Factory { /** * 通过传入的全类名返回该类的对象 * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。 * java读取配置文件可实现完全解耦 * @param className * @return */ public static Object getInstance(String className){ Object instance = null; try { //通过反射创建对象实例 instance = Class.forName(className).newInstance(); }catch (Exception e) { e.printStackTrace(); } return instance; } }
public static void main(String[] args) { //反射工厂 Apple apple = (Apple)Factory.getInstance("com.wp.reflect.Apple"); apple.method(); Banana banana = (Banana)Factory.getInstance("com.wp.reflect.Banana"); banana.method(); }
反射还可以实现动态代理
9 安全问题
如下形式enum 枚举类实现单例模式
public enum EnumSingleton { INSTANCE; private EnumSingleton(){ } public static EnumSingleton getInstance() { return INSTANCE; } }
在反射的newInstance()方法里面,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败,枚举类实现单例模式不用怕反射破坏
public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //其他代码已删除 if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); //其他代码已删除 }
总结: 只有枚举实现单例模式可以防止反射攻击,其它单例模式无法防止
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix