Java反射的应用

反射的应用

后续使用代码使用User类

class User{
    private String name;
    private int id;
    private int age;

    public User(){}

    public User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }

    //get&set
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}

创建运行时类的对象

获取运行时类的完整结构

通过反射获取运行时类的完整结构

类名、属性、指定属性、方法、指定方法、构造器、指定构造器

Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");

//获得类的名字
System.out.println(c1.getName()); //包+类名
System.out.println(c1.getSimpleName()); //类名

//类的属性
Field[] fields = c1.getFields(); //只能找到public属性

fields = c1.getDeclaredFields(); //得到所有属性
for (Field field : fields) {
    System.out.println(field);
}

//获得指定属性的值(获得属性时传入属性)
//Field name1 = c1.getField("name");//只能找到public属性
Field name = c1.getDeclaredField("name");
System.out.println(name);

//获得类的方法
Method[] methods = c1.getMethods(); //获得本类及其父类的所有public方法
for (Method method : methods) {
    System.out.println("getMethods:"+method);
}
methods = c1.getDeclaredMethods(); //获得本类的所有方法
for (Method method : methods) {
    System.out.println("getDeclaredMethods:"+method);
}

//获得指定方法(类名,参数)
//参数是为了避免重载的情况
Method getName = c1.getMethod("getName", null);
System.out.println(getName);
Method setName = c1.getMethod("setName", String.class);

//获得构造器
Constructor[] constructors = c1.getConstructors();//获得public
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获得全部方法
for (Constructor declaredConstructor : declaredConstructors) {
    System.out.println(declaredConstructor);
}

//获得指定构造器
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);

动态创建对象

  • 创建类的对象:调用Class类对象的newInstance()方法
    • 类必须有一个无参构造器
    • 类的构造器的访问权限需要足够
  • 没有无参构造器的对象创建
    1. 通过Class类的getDeclaredConstructor(Class ... parameterTypes)获得本类的指定形参类型的构造器
    2. 向构造器的形参中传递一个对象数组,其中包含构造器中所需的各个参数
    3. 通过Construtor实例化对象

代码实现

  • 获得class对象
//获得class对象
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");
  • 构造对象
//构造对象
//过时替代:.getDeclaredConstructor().newInstance()
User user = (User)c1.newInstance(); //调用了无参构造器,使用需要有无参构造器,权限最好是public
System.out.println(user);

//通过构造器创建对象(无参构造器不存在时也可使用)
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
User user2 = (User)constructor.newInstance("chachan53", 1, 20);
System.out.println(user2);

调用指定方法

  • 通过反射调用类中的方法:

    1. 通过getMethod()方法获得Method对象,并设置此方法操作时需要的参数类型
    2. 使用Object.invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的参数信息
  • Object.invoke(Object obj, Object[] args)注意事项:

    • Object对应原方法的返回值,若原方法无返回值,返回null
    • 若原方法为静态方法,形参Object obj可为null
    • 原方法形参列表为空,Object[] argsnull
    • 原方法声明为private,需要在调用此invoke()方法前,显示调用方法对象的setAccessible(true)方法,才可询问private方法(降低使用效率)
  • setAccessible(true):

    • 方法Method、属性Field、构造器Constructior都有setAccessible(true)方法
    • setAccessible()是启动和禁用访问安全检测的开关,默认为false,进行检查
    • 参数值为true则指示反射的对象在使用时应取消Java语言访问检查
      • 提高反射效率,代码中必须使用而该代码需要频繁调用时,应设为true
      • 使无法访问的private可被访问

代码实现

  • 获得class对象
//获得class对象
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");
  • 通过反射调用普通方法
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);

//激活:invoke(对象,"方法的值")
setName.invoke(user3,"chachan53");
System.out.println(user3.getName());
  • 通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要关闭程序的安全检测,使用属性或方法的setAccessible(ture)
name.setAccessible(true);
name.set(user4,"chachan53");
System.out.println(user4.getName());

性能分析

  • 普通方法调用
public static void test1(){
    User user = new User();

    long startTime = System.currentTimeMillis();

    for (int i = 0; i < 1000000000; i++) {
        user.getName();
    }

    long endTime = System.currentTimeMillis();

    System.out.println("普通方法:" + (endTime - startTime) + "ms");
}
  • 反射调用
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getMethod("getName",null);

    long startTime = System.currentTimeMillis();

    for (int i = 0; i < 1000000000; i++) {
        getName.invoke(user,null);
    }

    long endTime = System.currentTimeMillis();

    System.out.println("反射:" + (endTime - startTime) + "ms");
}
  • 反射调用(关闭检测)
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();
    Method getName = c1.getMethod("getName",null);
    getName.setAccessible(true);//关闭检测

    long startTime = System.currentTimeMillis();

    for (int i = 0; i < 1000000000; i++) {
        getName.invoke(user,null);
    }

    long endTime = System.currentTimeMillis();

    System.out.println("反射关闭权限:" + (endTime - startTime) + "ms");
}

实验结果

普通方法:4ms
反射:1576ms
反射关闭权限:948ms

操作泛型

  • Java采用泛型擦除的机制引入泛型,其中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,编译完成所有和泛型有关的类型全部擦除(class文件中留有信息)
  • 通过反射操作这些类型,一些与原始类齐名的类型:
    • ParameterizedType:表示一种参数化类型,比如Collection<String>
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable:是各种类型变量的公共父接口
    • WildcardType:代表一种通配符类型的表达式

代码实现

  • 设置方法
public void test01(Map<String,User> map, List<User> list){
    System.out.println("test01");
}

public Map<String, User> test02(){
    System.out.println("test02");
    return null;
}
  • 实现主函数
public static void main(String[] args) throws NoSuchMethodException {
    //通过类获得方法
    Method method1 = Test11.class.getMethod("test01", Map.class, List.class);
    //获得方法的泛型参数
    Type[] genericParameterTypes = method1.getGenericParameterTypes();
    //输出泛型
    for (Type genericParameterType : genericParameterTypes) {
        System.out.println("#"+genericParameterType);
        //判断是否属于ParameterizedType参数化类型(是不是泛型)
        if (genericParameterType instanceof ParameterizedType){
            //属于则进行类型强制转换,并用getActualTypeArguments获得其中参数的类型
            Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

    Method method2 = Test11.class.getMethod("test02", null);
    //获得返回值的泛型类型
    Type genericReturnType = method2.getGenericReturnType();
    if (genericReturnType instanceof ParameterizedType){
        //属于则进行类型强制转换,并用getActualTypeArguments获得其中参数的类型
        Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }
    }
}
  • 实验结果
class java.lang.String
class com.chachan53.class13annotation.reflection.User
#java.util.List<com.chachan53.class13annotation.reflection.User>
class com.chachan53.class13annotation.reflection.User

class java.lang.String
class com.chachan53.class13annotation.reflection.User

反射操作注解

  • getAnnotations
  • getAnnotation

练习:ORM

ORM:Object relationship Mapping,对象映射关系

  • 类和标结构对应
  • 属性和字段对应
  • 对象和记录对象

代码实现

  • 自定义注释和类
//类名的注解
@Target(ElementType.TYPE)//注解的使用位置
@Retention(RetentionPolicy.RUNTIME)//注解的有效范围
@interface Tabeltest{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)//注解的使用位置
@Retention(RetentionPolicy.RUNTIME)//注解的有效范围
@interface Fieldtest{
    String columuName();//列名
    String type();//类型
    int length();//长度
}

@Tabeltest("test_table")
class Student2{
    @Fieldtest(columuName = "table_id",type = "int", length = 10)
    private int id;
    @Fieldtest(columuName = "table_age",type = "int", length = 10)
    private int age;
    //字符串类型一般使用varchar
    @Fieldtest(columuName = "table_name",type = "varchar", length = 3)
    private String name;

    public Student2() {}

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
    //get&set
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 使用
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    Class c1 = Class.forName("com.chachan53.class13annotation.reflection.Student2");

    //通过反射获得注解
    Annotation[] annotations = c1.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }//@com.chachan53.class13annotation.reflection.Tabeltest(value=test_table)

    //获得注解的value值
    //获得指定注解,进行强制转换,通过value()获得方法的值
    Tabeltest annotation = (Tabeltest)c1.getAnnotation(Tabeltest.class);
    System.out.println(annotation.value());//test_table

    //获得类指定的注解
    Field field = c1.getDeclaredField("name");
    Fieldtest annotation1 = field.getAnnotation(Fieldtest.class);
    System.out.println(annotation1.columuName());//table_name
    System.out.println(annotation1.type());//varchar
    System.out.println(annotation1.length());//3
}

学习参考:狂神说Java反射和注解

posted @ 2022-04-22 10:38  chachan53  阅读(93)  评论(0编辑  收藏  举报