书成

再这样堕落下去就给我去死啊你这混蛋!!!

0%

Java反射机制

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;
}
// ......省略了getter,setter方法与toString()方法
}

反射获得与调用构造函数

 接下来,可以通过反射调用这些私有字段与方法,反射需要先获得类对象,获得类对象有三种方法,会体现在接下来的代码中,首先是调用构造函数:

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对象的方法,调用对象的getClass()方法,该方法继承自Object
Class clazz = user.getClass();

Constructor[] constructors = clazz.getConstructors(); // 获得public修饰的构造函数
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的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; // 第二种获得类Class对象的方法,访问它的静态字段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"); // 调用时需要传入调用该方法的对象u
System.out.println(u);

method = clazz.getDeclaredMethod("testPrivateMethod", String.class); // getDeclaredMethod方法可以获得private方法
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对象的方法,通过Class的静态方法forName,需传入类的全限定名
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"); // 调用Field的set方法设置新值
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();
// Class对象中的方法,可以返回表示该类所表示的实体的直接超类的类型
// 还有getGenericInterfaces方法返回接口中的类型
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