文章目录
- 反射是什么
- 反射的用途
- 反射的缺点
- 反射的基本运用
- 获取Class 类对象
- 类相关的反射
- 获取包名
- 获取supperClass
- 获取Public成员类
- 获取声明的类
- 获取所有Public构造方法
- 获取泛型参数
- 获取实现的接口
- 获取所有Public方法
- 获取所有Public字段
- 获取所有注释
- 获取权限修饰符
- 字段相关反射
- 获取字段
- 字段赋值
- 获取字段类型
- 获取字段声明类
- 方法相关的反射
- 获取方法
- 调用方法
- 构造方法的反射
- 获取构造方法
- 使用构造函数实例化对象
- 总结
反射是java高级特性之一,常用的框架,例如:spring、mybatis等在实现的过程中都使用到了反射,所以还是非常有必要了解反射的。
反射是什么
Java反射提供了检查和修改应用程序运行时行为的能力。反射允许我们在运行时检查和操作类、接口、构造函数、方法和字段,即使类在编译时不可访问。我们还可以使用反射来实例化一个对象,调用它的方法,更改字段值。这种动态获取的信息以及动态调⽤对象的⽅法的功能称为java语⾔的反射机制。
反射的用途
可能有些人认为反射在工作中用的并不多,但其实并不是这样的,工作中处处都能见到反射的影子,比如工作中经常会通过对象 . 的时候编译器会列出该对象可以调用的方法,其实这个原理使用的也是反射。还有像Spring、Mybatis、Tomcat等通用框架或者容器底层都会大量的使用到反射。所以如果你想实现一个通用的功能,反射或许是最好的选择之一。
反射的缺点
反射的好处是允许我们在运行时对类、接口等进行一系列操作。但是它也有缺点:
- 性能不佳: 由于 java 反射动态解析类型,它涉及扫描类路径以查找要加载的类等处理,导致性能下降。
- 安全限制:反射需要运行时权限,这对于在安全管理器下运行的系统可能不可用。由于安全管理器,这可能会导致应用程序在运行时失败。
- 安全问题: 使用反射我们可以访问我们不应该访问的部分代码,例如我们可以访问一个类的私有字段并更改它的值。这可能是一个严重的安全威胁,并导致应用程序行为异常。
- 高维护: 反射代码难以理解和调试,并且在编译时无法发现代码的任何问题,因为类可能不可用,使其灵活性降低且难以维护。
反射的基本运用
以下类用于测试
BaseInterface.java
package com.example.reflection;public interface BaseInterface {public int interfaceInt=0;void method1();int method2(String str);
}
BaseClass.java
package com.example.reflection;public class BaseClass {public int baseInt;private static void method3(){System.out.println("Method3");}public int method4(){System.out.println("Method4");return 0;}public static int method5(){System.out.println("Method5");return 0;}void method6(){System.out.println("Method6");}// inner public classpublic class BaseClassInnerClass{}//member public enumpublic enum BaseClassMemberEnum{}
}
ConcreteClass.java
package com.example.reflection;public class ConcreteClass extends BaseClass implements BaseInterface{public int publicInt;private String privateString="private string";public static String staticStr = "public static string";protected boolean protectedBoolean;Object defaultObject;public ConcreteClass(int i){this.publicInt=i;}public ConcreteClass(){}private ConcreteClass(String privateString){this.privateString = privateString;}@Overridepublic void method1() {System.out.println("Method1 impl.");}@Overridepublic int method2(String str) {System.out.println("Method2 impl.");return 0;}@Overridepublic int method4(){System.out.println("Method4 overriden.");return 0;}public int method5(int i){System.out.println("Method4 overriden.");return 0;}// inner classespublic class ConcreteClassPublicClass{}private class ConcreteClassPrivateClass{}protected class ConcreteClassProtectedClass{}class ConcreteClassDefaultClass{}//member enumenum ConcreteClassDefaultEnum{}public enum ConcreteClassPublicEnum{}//member interfacepublic interface ConcreteClassPublicInterface{}
}
获取Class 类对象
java.lang.Class是所有反射操作的入口点。Class 类只有java虚拟机才能new出来,任何⼀个类都是Class 类的实例对象。要使用反射首先要获取到Class对象,Class 对象有三种方式可以获取到:
- 通过
.class
Class<Integer> integerClass = Integer.class;
- 使用对象的
getClass
方法
Date date = new Date();
Class<? extends Date> dateClass = date.getClass();
- 通过
java.lang.Class.forName(String fullyClassifiedClassName)
方法
Class<Integer> forName = Class.forName("java.lang.Integer");
对于基本类型和数组,Class对象的获取可以通过.class
的方式获取。
Class<?> booleanClass = boolean.class;
System.out.println(booleanClass.getCanonicalName()); // prints booleanClass<?> cDouble = Double.TYPE;
System.out.println(cDouble.getCanonicalName()); // prints doubleClass<?> cDoubleArray = Class.forName("[D");
System.out.println(cDoubleArray.getCanonicalName()); //prints double[]Class<?> twoDStringArray = String[][].class;
System.out.println(twoDStringArray.getCanonicalName()); // prints java.lang.String[][]
对于包装类,也可以通过静态变量TYPE获取Class对象
Class<?> doubleClass = Double.TYPE;
类相关的反射
获取包名
getPackage()
方法返回这个类所在的包。该类的类加载器用于查找包。
final Package aPackage = ConcreteClass.class.getPackage();
System.out.println(aPackage);System.out.println(aPackage.getName());
结果:
package com.example.reflection
com.example.reflection
获取supperClass
getSuperclass()
如果是类对象调用的话会返回类的父类,如果此类为Object类、接口、基本类型或void,则返回null。如果此对象为数组类,则返回表示Object类的类对象。
final Class<? super Integer> superclass = Integer.class.getSuperclass();
System.out.println(superclass); // 结果:class java.lang.NumberSystem.out.println(Object.class.getSuperclass()); // 结果:nullSystem.out.println(String[][].class.getSuperclass()); // 结果:class java.lang.Object
获取Public成员类
getClasses()
返回一个Class类型数组, 通过getClasses()
可以获取到Class对象的所有Public类、接口和枚举类。这包括从父类继承的公共类和接口以及由类声明的公共类和接口。如果Class对象没有Public成员类、接口,又或者Class对象是个数组、基本类型、void, getClasses()
会返回一个空数组。
final Class<?>[] publicClasses = ConcreteClass.class.getClasses();
Arrays.stream(publicClasses).forEach(System.out::println);
结果:
获取声明的类
getDeclaredClasses()
方法返回一个 Class 对象数组,返回声明为该 Class 对象表示的类成员的所有类和接口。返回的数组不包括在继承的类和接口中声明的类。
final Class<?>[] declaredClasses = ConcreteClass.class.getDeclaredClasses();
Arrays.stream(declaredClasses).forEach(System.out::println);
结果:
获取所有Public构造方法
getConstructors()
方法返回Class对象的Public构造方法列表。
final Constructor<?>[] constructors = ConcreteClass.class.getConstructors();
Arrays.stream(constructors).forEach(System.out::println);
结果:
public com.example.reflection.ConcreteClass()
public com.example.reflection.ConcreteClass(int)
获取泛型参数
如果有与该类关联的任何泛型参数,getTypeParameters()
则返回 TypeVariable 数组。泛型参数的返回顺序与声明的顺序相同。
TypeVariable<?>[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters();
for(TypeVariable<?> t : typeParameters){System.out.print(t.getName()+",");
}
结果:
K,V,
获取实现的接口
getGenericInterfaces()
方法返回由具有泛型类型信息的类实现的接口数组。
getInterfaces()
方法返回类实现的所有接口
final Class<?> hashMapClass = Class.forName("java.util.HashMap");
System.out.println(Arrays.toString(hashMapClass.getGenericInterfaces()));
System.out.println(Arrays.toString(hashMapClass.getInterfaces()));
结果:
[java.util.Map<K, V>, interface java.lang.Cloneable, interface java.io.Serializable]
[interface java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable]
获取所有Public方法
getMethods()方法返回该类的公共方法数组,包括它的父类和实现的接口的公共方法。
final Method[] allMethods = ConcreteClass.class.getMethods();
Arrays.stream(allMethods).forEach(System.out::println);
结果:
获取所有Public字段
getFields()
方法返回该类的公共字段数组,包括它的父类和实现的接口的公共字段。
public int com.example.reflection.ConcreteClass.publicInt
public static final int com.example.reflection.BaseInterface.interfaceInt
public int com.example.reflection.BaseClass.baseInt
获取所有注释
getAnnotations()
方法返回元素的所有注释。
final Annotation[] annotations = ConcreteClass.class.getAnnotations();
System.out.println(Arrays.toString(annotations));
结果:
[@java.lang.Deprecated()]
获取权限修饰符
getModifiers()
方法返回类修饰符的int表示,可以使用java.lang.reflect.Modifier.toString()方法以源代码中使用的字符串格式来显示出来。
final int modifiers = ConcreteClass.class.getModifiers();
final String modifier = Modifier.toString(modifiers);
System.out.println("modifiers="+modifiers +",modifier="+modifier);
结果:
modifiers=1,modifier=public
java.lang.reflect.Modifier
权限修饰符表示的常量
public static final int PUBLIC = 0x00000001;
/*** The {@code int} value representing the {@code private}* modifier.*/
public static final int PRIVATE = 0x00000002;/*** The {@code int} value representing the {@code protected}* modifier.*/
public static final int PROTECTED = 0x00000004;
字段相关反射
获取字段
getField()
和 getDeclaredField()
方法可以获取到类的字段。不同的是,getField()
获取的必须是声明了public的字段,包括父类或者实现的接口中的public字段; getDeclaredField()
只能获取的本类中定义的字段。
final Class<ConcreteClass> concreteClassClass = ConcreteClass.class;
//获取public字段
final Field interfaceInt = concreteClassClass.getField("interfaceInt");
System.out.println(interfaceInt.getName());//获取private字段
final Field privateString = concreteClassClass.getDeclaredField("privateString");
// 这一步很重要
privateString.setAccessible(true);
System.out.println(privateString.getName());
对于private类型字段一定要用setAccessible(true)
关闭访问检查,表示让字段可以被访问到,如果不关闭会报错。
字段赋值
//public 字段设值
final Field publicInt = concreteClassClass.getField("publicInt");
ConcreteClass obj = new ConcreteClass(5);
System.out.println(publicInt.get(obj)); //prints 5
publicInt.setInt(obj, 10); //setting field value to 10 in object
System.out.println(publicInt.get(obj)); //prints 10//private字段设值
Field privateField = concreteClassClass.getDeclaredField("privateString");
privateField.setAccessible(true);
ConcreteClass objTest = new ConcreteClass(1);
System.out.println(privateField.get(objTest)); // prints "private string"
privateField.set(objTest, "private string updated");
System.out.println(privateField.get(objTest)); //prints "private string updated"//static字段
final Field staticStr = concreteClassClass.getField("staticStr");
staticStr.set(null , "public static string updated");
System.out.println(staticStr.get(null));
如果字段是静态的,可以在 get()
方法中将 第一个Object参数传NULL值来调用。
获取字段类型
getType() 方法返回声明的字段类型的 Class 对象,如果字段是原始类型,则返回包装类对象。
final Class<ConcreteClass> concreteClassClass = ConcreteClass.class;
final Field publicInt = concreteClassClass.getField("publicInt");
final Class<?> type = publicInt.getType();
System.out.println(type.getCanonicalName()); //prints int
获取字段声明类
可以使用getDeclaringClass()
字段对象来获取声明字段的类。
final Field interfaceInt = concreteClassClass.getField("interfaceInt");
final Class<?> declaringClass = interfaceInt.getDeclaringClass();
System.out.println(declaringClass);
方法相关的反射
获取方法
- public方法的获取:以HashMap 的
put()
方法为例。
可以使用getMethod()
来获取类的公共方法,我们需要传递该方法的方法名和参数类型。如果在类中找不到该方法,反射 API 会在超类中查找该方法。Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class); //get method parameter types, prints "[class java.lang.Object, class java.lang.Object]" System.out.println(Arrays.toString(method.getParameterTypes())); //get method return type, return "class java.lang.Object", class reference for void System.out.println(method.getReturnType()); //get method modifiers System.out.println(Modifier.toString(method.getModifiers())); //prints "public
- private 方法获取
可以使用getDeclaredMethod()
来获取私有方法,要使用setAccessible(true)
关闭访问检查final Class<BaseClass> baseClassClass = BaseClass.class; Method method = baseClassClass.getDeclaredMethod("method3", null); method.setAccessible(true);
调用方法
-
public方法调用
可以使用 Method 对象的 invoke() 方法来调用方法,以HashMap 的put()
方法为例Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class); Map<String, String> hm = new HashMap<>(); method.invoke(hm, "key", "value"); System.out.println(hm); // prints {key=value}
-
private方法调用
以调用静态且没有参数的 BaseClass 的 method3()为例。final Class<BaseClass> baseClassClass = BaseClass.class; Method method = baseClassClass.getDeclaredMethod("method3", null); method.setAccessible(true); method.invoke(null, null); //prints "Method3"
和静态字段一样,invoke() 方法的第一个参数object传NULL值则是调用静态方法。
构造方法的反射
获取构造方法
可以在对象的类表示上使用 getConstructor() 方法来获取特定的public构造函数。
可以在对象的类表示上使用 getDeclaredConstructor() 方法来获取特定的public构造函数。
//获取有参构造方法
Constructor<?> constructor = concreteClassClass.getConstructor(int.class);
//获取构造方法参数
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"//获取无参构造方法
final Constructor<ConcreteClass> classClassConstructor = concreteClassClass.getConstructor();
System.out.println(classClassConstructor); // 结果:public com.example.reflection.ConcreteClass()//获取私有构造方法
final Constructor<ConcreteClass> declaredConstructor =concreteClassClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor); //结果:private com.example.reflection.ConcreteClass(java.lang.String)
使用构造函数实例化对象
可以在构造函数对象上使用 newInstance() 方法来实例化该类的新实例。
//获取有参构造方法
Constructor<?> constructor = concreteClassClass.getConstructor(int.class);
final Object newInstance = constructor.newInstance(111);
//获取method1 方法
final Method method1 = newInstance.getClass().getMethod("method1", null);
//调用method1方法
method1.invoke(newInstance , null);
总结
从上面所有的测试中我们可以发现,在Class对象中的方法中只要是带有Declared字段的都是获取本类中声明的方法、字段或者构造方法等,反之则是调用public的方法;在调用私有方法时要注意一点要将访问检查关闭。
参考资料:
https://www.digitalocean.com/community/tutorials/java-reflection-example-tutorial