java.lang.reflect java.lang.reflect 包提供用于获取类和对象反射信息的类和接口。反射是指允许通过编程访问有关类的字段、方法和构造函数的信息,并允许使用反射的字段、方法和构造函数在封装和安全限制范围内对其基础对应项进行操作。
该包下的类有:
AccessibleObject
是Filed,Method,Constructor类的父类,提供与权限相关的一些方法
Array
提供了动态创建和访问Java数据的静态方法
Constructor
提供类的单个构造函数的信息和访问
Executable
Method和Constructor的父类
Field
提供类的单个字段的信息和访问
Method
提供类的单个方法的信息和访问
Modifier
提供静态方法和常量来解码类和成员的访问修饰符
Parameter
提供方法参数的信息
Proxy
提供静态方法创建充当接口实例但允许自定义方法调用的对象( Jdk 动态代理)
ReflectPermission
反射操作的权限类
通常使用到的类是 Constructor,Field,Method 与 Proxy 类。通常使用到的接口是 InvocationHandler 接口。其中 Proxy 类与 InvocationHandler 用在 jdk 动态代理上。
反射获得类信息 通过反射可以获得类的信息,定义 User 类如下,包含了一个私有构造函数,一个私有方法与三个私有字段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class User { private String name; private int id; private String password; public User () { this .name = "公共无参数构造函数" ; } public User (String name, int id, String password) { this .name = name; this .id = id; this .password = password; } protected User (String name, int id) { this .name = name; this .id = id; this .password = "protected Constructor" ; } private User (String name) { this .name = name; this .id = 9999 ; this .password = "private Constructor" ; } private String testPrivateMethod (String s) { System.out.println("打印传入字符串" + s); return s; } }
反射获得与调用构造函数 接下来,可以通过反射调用这些私有字段与方法,反射需要先获得类对象,获得类对象有三种方法 ,会体现在接下来的代码中,首先是调用构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void main (String[] s) throws Exception { User user = new User(); Class clazz = user.getClass(); Constructor[] constructors = clazz.getConstructors(); for (Constructor c : constructors) System.out.println(c); System.out.println("--------------------" ); constructors = clazz.getDeclaredConstructors(); for (Constructor c : constructors) System.out.println(c); System.out.println("--------------------" ); Constructor constructor = clazz.getConstructor(); System.out.println(constructor.newInstance()); constructor = clazz.getDeclaredConstructor(String.class , int .class ) ; System.out.println(constructor.newInstance("protected Constructor" , 111 )); constructor = clazz.getDeclaredConstructor(String.class ) ; constructor.setAccessible(true ); System.out.println(constructor.newInstance("private constructor" )); }
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 public fanshedemo.User(java.lang.String,int,java.lang.String) public fanshedemo.User() -------------------- private fanshedemo.User(java.lang.String) protected fanshedemo.User(java.lang.String,int) public fanshedemo.User(java.lang.String,int,java.lang.String) public fanshedemo.User() -------------------- User{name='公共无参数构造函数' , id=0 , password='null' } User{name='protected Constructor' , id=111 , password='protected Constructor' } User{name='private constructor' , id=9999 , password='private Constructor' }
反射获得与调用类方法 接下来,是反射获得类的方法,运行如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void main (String[] s) throws Exception { Class clazz = User.class ; Method[] methods = clazz.getMethods(); for (Method m : methods) System.out.println(m); System.out.println("---------以上方法包含非私有方法与父类Object方法-----------" ); methods = clazz.getDeclaredMethods(); for (Method m : methods) System.out.println(m); System.out.println("--------------------" ); User u = new User("name" , 88 , "password" ); System.out.println(u); Method method = clazz.getMethod("setName" , String.class ) ; method.invoke(u,"new Name" ); System.out.println(u); method = clazz.getDeclaredMethod("testPrivateMethod" , String.class ) ; method.setAccessible(true ); Object o = method.invoke(u,"invoke private method" ); System.out.println("返回值是" + o); }
以上代码运行后结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public java.lang.String fanshedemo.User.toString() public java.lang.String fanshedemo.User.getName() public void fanshedemo.User.setName(java.lang.String) public int fanshedemo.User.getId() public void fanshedemo.User.setPassword(java.lang.String) public java.lang.String fanshedemo.User.getPassword() public void fanshedemo.User.setId(int) public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ---------以上方法包含非私有方法与父类Object方法----------- public java.lang.String fanshedemo.User.toString() public java.lang.String fanshedemo.User.getName() public void fanshedemo.User.setName(java.lang.String) public int fanshedemo.User.getId() private java.lang.String fanshedemo.User.testPrivateMethod(java.lang.String) public void fanshedemo.User.setPassword(java.lang.String) public java.lang.String fanshedemo.User.getPassword() public void fanshedemo.User.setId(int) -------------------- User{name='name' , id=88 , password='password' } User{name='new Name' , id=88 , password='password' } 打印传入字符串invoke private method 返回值是invoke private method
反射获得与访问类的字段 接下来,是反射获得类的字段,运行如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void main (String[] s) throws Exception { Class clazz = Class.forName("fanshedemo.User" ); Field[] fields = clazz.getFields(); for (Field field : fields) System.out.println(field); System.out.println("--------------------" ); fields = clazz.getDeclaredFields(); for (Field field : fields) System.out.println(field); System.out.println("--------------------" ); User u = new User("name" , 111 , "password" ); System.out.println(u); Field field = clazz.getDeclaredField("name" ); field.setAccessible(true ); field.set(u, "new name" ); System.out.println(u); }
以上代码运行后结果如下:
1 2 3 4 5 6 7 -------------------- private java.lang.String fanshedemo.User.name private int fanshedemo.User.id private java.lang.String fanshedemo.User.password -------------------- User{name='name' , id=111 , password='password' } User{name='new name' , id=111 , password='password' }
通过反射,可以在运行时获取对象的构造函数,方法,字段信息并访问它们,除此之外,Class 类还有 getInterfaces() 方法可以获得类所有实现的接口,getSuperClass() 可以获得类的父类等方法。
利用反射运行指定方法 利用反射读取配置文件信息,根据配置文件信息运行指定方法。之前的 User 类不必改动,创建配置文件,填上如下内容:
1 2 className=fanshedemo.User methodName=testPrivateMethod
之后修改 main 方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void main (String[] s) throws Exception { FileInputStream in = new FileInputStream("src/main/java/fanshedemo/config.properties" ); Properties properties = new Properties(); properties.load(in); in.close(); String className = properties.getProperty("className" ); String methodName = properties.getProperty("methodName" ); Class clazz = Class.forName(className); Method[] methods = clazz.getDeclaredMethods(); Object o = clazz.getConstructor().newInstance(); for (Method method : methods){ if (method.getName().equals(methodName)){ Parameter[] parameters = method.getParameters(); Object[] objects = new Object[parameters.length]; for (int i = 0 ; i < parameters.length; i++) objects[i] = Class.forName(parameters[i].getType().getName()).getConstructor().newInstance(); method.setAccessible(true ); method.invoke(o, objects); break ; } } }
调用结果为:打印传入字符串
,可知已经成功的在读取配置文件后通过反射调用一个方法,当然,实际情况可能不会这么简单,会更加复杂,这只是一个基础的小 demo 。
利用反射绕过泛型检查 利用反射,还可以绕过泛型检查,例子如下:
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) throws Exception { List<String> list = new ArrayList<>(); list.add("aaa" ); list.add("bbb" ); Class clazz = list.getClass(); Method method = clazz.getMethod("add" , Object.class ) ; method.invoke(list, 123 ); method.invoke(list, false ); System.out.println(list); }
上述代码运行结果:[aaa, bbb, 123, false]
,由此,通过反射便绕过了 List 的泛型检查。
通过反射获得泛型实际参数 通过反射可以获得泛型实际参数,需要先了解 reflect 包中几个接口:
Type 接口 :Type是Java编程语言中所有类型的通用超接口。这些类型包括原始类型、参数化类型 、数组类型、类型变量和基本类型。 这个接口中只有一个方法 getTypeName() ,调用该方法可以获得类型名。
ParameterizedType 接口 :ParameterizedType表示参数化类型 ,其中的方法 getActualTypeArguments() 可以返回表示此类型的实际类型参数的类型对象数组,即 Type 数组。
WildcardType 接口 :表示一个通配符类型表达式 ,其中有 getLowerBounds() 与 getUpperBounds() 方法,分别返回表示此类型变量的下界与上界的类型对象数组。其中 getUpperBounds() 如果没有显式声明上限,则上限为Object。
通过反射获得类的泛型实际类型 通过反射获得类实际泛型实际类型,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Base <T ,E > {} public class GenericDemo extends Base <String ,Integer > { public static void main (String[] args) { GenericDemo genericDemo = new GenericDemo(); Class clazz = genericDemo.getClass(); Type genericClass = clazz.getGenericSuperclass(); if (genericClass instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType)genericClass; Type[] types = parameterizedType.getActualTypeArguments(); for (Type type: types){ System.out.println(type.getTypeName()); } } } }
代码运行结果如下:
1 2 java.lang.String java.lang.Integer
通过反射获得方法参数的泛型实际类型 通过反射获得方法参数的泛型实际类型,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class GenericDemo extends Base <String ,Integer > { public void genericMethodDemo (Map<String, Integer> map, List<String> list, String string) { } public static void main (String[] args) throws Exception { GenericDemo genericDemo = new GenericDemo(); Class clazz = genericDemo.getClass(); Method method = clazz.getMethod("genericMethodDemo" , Map.class , List .class ,String .class ) ; Type[] types = method.getGenericParameterTypes(); for (Type type: types){ if (type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type[] genericTypes = parameterizedType.getActualTypeArguments(); for (Type genericType : genericTypes) System.out.println(genericType.getTypeName()); } } }
代码运行结果如下:
1 2 3 java.lang.String java.lang.Integer java.lang.String
通过反射获得方法返回值的泛型实际类型 通过反射获得方法返回值的泛型实际类型,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class GenericDemo extends Base <String ,Integer > { public Map<String, Integer> genericMethodDemo2 () { return new HashMap<String, Integer>(); } public static void main (String[] args) throws Exception { GenericDemo genericDemo = new GenericDemo(); Class clazz = genericDemo.getClass(); Method method = clazz.getMethod("genericMethodDemo2" ); Type type = method.getGenericReturnType(); if (type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type[] genericTypes = parameterizedType.getActualTypeArguments(); for (Type genericType : genericTypes) System.out.println(genericType.getTypeName()); } }
代码运行结果如下:
1 2 java.lang.String java.lang.Integer
通过反射获得泛型上下界 通过反射获得泛型的上下界,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class GenericDemo extends Base <String ,Integer > { public void genericMethodDemo3 (List<? extends Number> l1, List<? super Integer> l2) { } public static void main (String[] args) throws Exception { GenericDemo genericDemo = new GenericDemo(); Class clazz = genericDemo.getClass(); Method method = clazz.getMethod("genericMethodDemo3" , List.class , List .class ) ; Type[] types = method.getGenericParameterTypes(); for (Type type: types){ if (type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type[] genericTypes = parameterizedType.getActualTypeArguments(); for (Type genericType : genericTypes){ System.out.println(genericType.getTypeName()); if (genericType instanceof WildcardType){ WildcardType wildcardType = (WildcardType) genericType; System.out.println("下界" ); for (Type t : wildcardType.getLowerBounds()) System.out.println(t.getTypeName()); System.out.println("上界" ); for (Type t : wildcardType.getUpperBounds()) System.out.println(t.getTypeName()); } } } } }
代码运行结果如下:结果也说明了,没有显示声明下界,下界为null,没有显示声明上界,上界为 Object 。
1 2 3 4 5 6 7 8 9 ? extends java.lang.Number 下界 上界 java.lang.Number ? super java.lang.Integer 下界 java.lang.Integer 上界 java.lang.Object