前言:

本文内容:动态创建对象执行方法、性能对比分析、获取泛型信息

推荐免费Java注解和反射讲解视频:【狂神说Java】注解和反射_哔哩哔哩_bilibili

动态创建对象执行方法

有了Class对象,能做什么

  • 创建类的对象:调用Class对象的newInstance()方法:
    • 类必须有一个无参数的构造器。
    • 类的构造器的访问权限需要足够。

思考:难道没有无参的构造器就不能创建对象了吗?只要操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

  • 步骤如下:
    • 通过Class类的getDeclaredConstructor(Class...parameterTypes取得本类的指定形参类型的构造器。
    • 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    • 通过Constructor实例化对象。

调用指定的方法

​ 通过反射,调用类中的方法,通过Method类完成。

  • 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

  • 之后使用Object invoke(Object obj,Object[]args)进行调用,并向方法中传递要设置的obj对象的参数信息。

    hvVNtO.png

调用指定的方法:

​ Object invoke(Object obj,Object… args)

  • Object对应原方法的返回值,若原方法无返回值,此时返回null
  • 若原方法为静态方法,此时形参Object obj可为null
  • 若原方法形参列表为空,则Object[]args为null
  • 若原方法声明为private,则需要在调用此invoke()方法前,显示调用方法对象的setAccessible(true)方法,将可访问private的方法

setAccessible

  • Method和Field、Constructor对象都有setAccessible()方法。

  • setAccessible作用是启动和禁用访问安全检查的开关。

  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

    • 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true
    • 使原本无法访问的私有乘员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package com.jokerdig.reflection;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    /**
    * @author Joker大雄
    * @data 2021/9/10 - 19:06
    **/
    //动态的创建对象,通过反射
    public class Demo07 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    Class c1 = Class.forName("com.jokerdig.reflection.User");

    //构造一个对象
    User user =(User) c1.newInstance();//调用无参构造器(过时)
    System.out.println(user);

    //通过构造器创建对象
    Constructor constructor = c1.getDeclaredConstructor(String.class, String.class, int.class);
    User user1 =(User) constructor.newInstance("张三", "123", 20);
    System.out.println(user1);

    //通过反射调用方法
    User user3 =(User) c1.newInstance();//调用无参构造器
    //通过反射获取方法
    Method setName = c1.getDeclaredMethod("setName", String.class);
    //invoke:激活
    //用法:(对象,"方法的值")
    setName.invoke(user3,"张三");
    System.out.println(user3.getName());

    //通过反射操作属性
    User user4 =(User) c1.newInstance();//调用无参构造器
    Field name =c1.getDeclaredField("name");
    //关掉权限检测,来访问私有属性
    name.setAccessible(true);//关闭检测,默认为false
    name.set(user4,"张三2");
    System.out.println(user4.getName());
    }
    }

性能对比分析

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.jokerdig.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* @author Joker大雄
* @data 2021/9/10 - 19:40
**/
//分析性能问题
public class Demo08 {

//普通方式调用
public static void test1(){
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法执行10000次需要"+(endTime-startTime)+"ms");
}

//反射方式调用
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方法执行10000次需要"+(endTime-startTime)+"ms");
}
//放射方式调用,关闭检查
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(false);//关闭检测
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
getName.invoke(user,null);

}
long endTime = System.currentTimeMillis();
System.out.println("关闭检测反射方法执行10000次需要"+(endTime-startTime)+"ms");
}

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test1();
test2();
test3();
}
}

获取泛型信息

反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题;但是,一旦编译完成,所有和泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

常用的类型

  • ParameterizedType:表示一种参数化类型,比如Collection<String>

  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型

  • TypeVarable:是各种类型变量的公共父接口

  • WildcardType:代表一种通配符类型表达式

    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    package com.jokerdig.reflection;

    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;
    import java.util.Map;

    /**
    * @author Joker大雄
    * @data 2021/9/10 - 19:58
    **/
    //通过反射获取泛型
    public class Demo09 {
    public void test1(Map<String,User> map, List<User> list){
    System.out.println("test1");
    }

    public Map<String,User> test2(){
    System.out.println("test2");
    return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
    Method method = Demo09.class.getMethod("test1", Map.class,List.class);
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
    System.out.println(" "+genericParameterType);
    if(genericParameterType instanceof ParameterizedType){
    Type[] actual =((ParameterizedType) genericParameterType).getActualTypeArguments();
    for (Type type : actual) {
    System.out.println(type);
    }
    }
    }
    method = Demo09.class.getMethod("test2",null);
    Type genericReturnType=method.getGenericReturnType();
    if(genericReturnType instanceof ParameterizedType){
    Type[] actualType =((ParameterizedType) genericReturnType).getActualTypeArguments();
    for (Type type : actualType) {
    System.out.println(type);
    }
    }

    }
    }