首页 > 其他 > 详细

反射机制

时间:2020-08-18 20:13:15      阅读:58      评论:0      收藏:0      [点我收藏+]

反射

反射机制的概述

反射(Reflection)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。

反射的特征:动态性

技术分享图片

技术分享图片

  • 动态语言:运行时可以改变其结构的语言
  • 静态语言:运行时结构不可变的语言

反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

Class类

Class类的理解

Class类定义在java.lang包下(java.lang.Class)

类的加载过程

程序在经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾),此过程是编译的过程。

使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载。

加载到内存中的类称为运行时类,此运行时类就作为Class的一个实例。Class的实例就对应一个运行时类。

  • 类加载的作用

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

  • 类缓存

    标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

ClassLoader(类的加载器)的理解

类加载器作用是用来把类(class)装载进内存的

获取Class实例的方式

加载到内存中的运行时类会缓存一定的时间,在此时间之内可以通过不同的方式来获取此运行时类。以下几种方式是获取运行时类,不是创建运行时类。(personClass1、personClass2等等都是获取的唯一存在的运行时类)

  • 方式一

    调用运行时类的属性:.class

    • 有泛型
      Class<Person> personClass = Person.class;
      
    • 无泛型
      Class personClass = Person.class;
      
  • 方式二

    通过运行时类的对象调用getClass()方法

    Person p1 = new Person();
    Class personClass2 = p1.getClass();
    
  • 方式三(主要使用)

    可以更好的体现动态性

    调用Class的静态方法:forName(String classPath)

    此时的classPath是包含包在内的完整路径(否则无法准确知道准确的类)

    Class personClass3 = Class.forName("Understand.Person");
    
  • 方式四(了解)

    使用类的加载器:ClassLoader

    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class personClass4 = classLoader.loadClass("Understand.Person");
    

Class实例对应的结构

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface:接口
  • []:数组
    • 只要数组的元素类型和维度一样,就是同一个Class
  • enum:枚举
  • annotation:注解
  • primitive type:基本数据类型
  • void

创建运行时类的对象

newInstance()方法

调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器

使用要求:

  • 类必须有一个无参数的构造器。
  • 类的构造器的访问权限需要足够,通常设置为public

类中要求提供一个public空参构造器的原因:

  • 便于通过反射去创建运行时类的对象
  • 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

使用过程

  1. 根据全类名获取对应的Class对象。
  2. 调用newInstance()方法。
Class<Person> pclass = Person.class;
Person person = pclass.newInstance();
System.out.println(person);

获取运行时类的完整结构

此时的Person类完整代码如下

@MyAnnotation(value = "hi")
public class Person extends Creature<String> implements Comparable<String>, MyInterface{

    private String name;
    int age;
    public int id;

    public Person(){

    }

    @MyAnnotation(value = "abc")
    private Person(String name){
        this.name = name;
    }

    Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    private String show(String nation){
        System.out.println("国籍:" + nation);
        return nation;
    }

    public String display(String interest){
        return interest;
    }

    @Override
    public void info() {
        System.out.println("我是一个人");
    }

    @Override
    public int compareTo(String s) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("可爱的猫");
    }

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

Person类的父类Creature类

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath(){
        System.out.println("生物呼吸");
    }

    public void eat(){
        System.out.println("生物进食");
    }
}

自定义接口MyInterface

public interface MyInterface {
    void info();
}

自定义注释

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
}

获取当前运行时类的所有属性

  • getFields():获取当前运行时类及其父类中声明为public访问权限的属性

    Field[] fields = personClass.getFields();
    for(Field f : fields){
        System.out.println(f);
    }
    

    技术分享图片

  • getDeclaredFields():获取当前运行时类当中声明的所有属性(不包含父类中声明的属性)

    Field[] declaredFields = personClass.getDeclaredFields();
    for(Field f : declaredFields){
        System.out.println(f);
    }
    

    技术分享图片

获取属性的相关信息

  • 权限修饰符

    getModifiers()

    该方法返回值为int,是因为Modifier类将各种权限修饰符用数字来表示。想要显示变量的权限修饰符,要使用Modifier中的toString()方法

  • 数据类型

    getType()

  • 变量名

    getName()

Class personClass = Class.forName("Get.Person");
Field[] declaredFields = personClass.getDeclaredFields();
for(Field f : declaredFields){
    //1. 权限修饰符
    int modifiers = f.getModifiers();
    System.out.println("权限修饰符:" + Modifier.toString(modifiers));
    //2. 数据类型
    Class type = f.getType();
    System.out.println("数据类型:" + type.getName());
    //3. 变量名
    String name = f.getName();
    System.out.println("变量名:" + name);
}

技术分享图片

获取当前运行时类的方法结构

  • getMethods():获取当前运行类及其父类所有声明为public权限的方法

    Method[] methods = personClass.getMethods();
    for(Method m : methods){
        System.out.println(m);
    }
    

    技术分享图片

  • getDeclaredMethods():获取当前运行时类中声明的所有的方法(不包含父类中声明的方法)

    Method[] declaredMethods = personClass.getDeclaredMethods();
    for(Method m : declaredMethods){
        System.out.println(m);
    }
    

    技术分享图片

获取方法的相关信息

  • 注解

    getAnnotations()

  • 权限修饰符

    getModifiers()

  • 返回值类型

    getReturnType()

  • 方法名

    getName()

  • 形参列表

    getParameterTypes()

  • 抛出异常类型

    getExceptionTypes()

Class<?> personClass = Class.forName("Get.Person");
Method[] declaredMethods = personClass.getDeclaredMethods();
for(Method m : declaredMethods){
    //6. 注解
    Annotation[] annotations = m.getAnnotations();
    for(Annotation a : annotations){
        System.out.println("注解:" + a);
    }
    System.out.println();
    //1. 权限修饰符
    int modifiers = m.getModifiers();
    System.out.print(Modifier.toString(modifiers) + "\t");
    //2. 返回值类型
    System.out.print(m.getReturnType().getName() + "\t");
    //3. 方法名
    System.out.print(m.getName() + "\t");

    System.out.print("(");
    //4. 形参列表
    Class[] parameterTypes = m.getParameterTypes();
    if(!(parameterTypes == null || parameterTypes.length == 0)){
        for(int i = 0;i < parameterTypes.length;i++){
            if(i == parameterTypes.length - 1){
                System.out.print(parameterTypes[i].getName() + "args_" + i);
                break;
            }
        System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
        }
    }
    System.out.print(")");
    //5. 抛出异常
    Class[] exceptionTypes = m.getExceptionTypes();
    if(!(exceptionTypes == null || exceptionTypes.length == 0)){
        System.out.print("throws ");
        for (int i = 0; i < exceptionTypes.length; i++) {
            if(i == exceptionTypes.length - 1){
                System.out.print(exceptionTypes[i].getName());
                break;
            }
            System.out.print(exceptionTypes[i].getName() + ",");
        }
    }
}

技术分享图片

获取当前运行时类的构造器

  • getConstructors():获取当前运行时类当中声明为public类型的构造器

    Class personClass = Class.forName("Get.Person");
    Constructor[] constructors = personClass.getConstructors();
    for(Constructor c : constructors){
        System.out.println(c);
    }
    

    技术分享图片

  • getDeclaredConstructors():获取当前运行时类中所有的构造器

    Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
    for(Constructor c : declaredConstructors){
        System.out.println(c);
    }
    

    技术分享图片

获取运行时类的父类

  • getSuperclass():获取运行时类的父类

    Class personClass = Class.forName("Get.Person");
    Class superclass = personClass.getSuperclass();
    System.out.println(superclass);
    
  • getGenericSuperclass():获取运行时类带泛型的父类

    Type genericSuperclass = personClass.getGenericSuperclass();
    System.out.println(genericSuperclass);
    

    技术分享图片

  • getActualTypeArguments():获取运行时类的带泛型父类的泛型

    Type genericSuperclass = personClass.getGenericSuperclass();
    ParameterizedType parameterizedType = (ParameterizedType)genericSuperclass;
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    System.out.println(Arrays.toString(actualTypeArguments));
    

    技术分享图片

获取运行时类的接口、所在包、注解

  • getInterfaces():获取运行时类实现的接口

    Class[] interfaces = aClass.getInterfaces();
    for(Class i : interfaces){
        System.out.println(i);
    }
    

    技术分享图片

  • getPackage():获取运行时类当前所在的包

    Class aClass = Class.forName("Get.Person");
    Package aPackage = aClass.getPackage();
    System.out.println(aPackage);
    
  • getAnnotations():获取运行时类所声明的注解

    Annotation[] annotations = aClass.getAnnotations();
    for(Annotation a : annotations){
        System.out.println(a);
    }
    

调用运行时类的指定结构

属性

  1. 创建运行时类的对象

  2. 获取指定的属性

    • getDeclaredField(String fieldName)

      通常采用getDeclaredField(String fieldName)方法获取属性。为获取运行时类中指定变量名(fieldName)的属性

  3. 保证当前属性是可访问的:使用setAccessible()方法

    name.setAccessible(true);
    

    只用使用这个方法,才可以对权限是非public的变量进行修改。

    在编写的时候,可以不管变量的权限修饰符是那种,都写上此方法

  4. 对属性的操作

    • 设置当前属性的值
      • set(Object obj, Object value)

        • Object obj:指明设置哪个对象的属性
        • Object value:将此属性设置的值
    • 获取当前属性的值
      • get(Object obj)
        • Object obj:指明获取哪个对象的当前属性值
Class aClass = Class.forName("Get.Person");
//1. 创建运行时类的对象
Person p = (Person) aClass.newInstance();

//2. 获取指定的属性
Field name = aClass.getDeclaredField("name");

name.setAccessible(true);
        
//设置当前属性的值
name.set(p,"Tom");
//获取当前属性的值
String pName = (String) name.get(p);
System.out.println("name:" + pName);

技术分享图片

方法

非静态方法

  1. 创建运行时类的对象
  2. 获取指定的方法
    • getDeclaredMethod()
      • 参数一:指明获取方法的名称
      • 参数二:指明获取的方法的形参列表
    Method show = aClass.getDeclaredMethod("show", String.class)
    
  3. 保证当前方法是可访问的:使用setAccessible()方法
    show.setAccessible(true);
    
  4. 调用方法
    • invoke()
      • 参数一:方法的调用者
      • 参数二:给方法形参赋值的实参
      • 该方法有返回值,即为对应类中调用方法的返回值,没有返回值则返回null
    show.invoke(p,"CN");
    
Class aClass = Class.forName("Get.Person");

Person p = (Person) aClass.newInstance();

Method show = aClass.getDeclaredMethod("show", String.class);

show.setAccessible(true);

String returnCn = (String) show.invoke(p, "CN");
        System.out.println(returnCn);

静态方法

利用 当前类.class或者null 表示当前类

Method showDesc = aClass.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//Person.class表示当前类
 showDesc.invoke(Person.class);

构造器(不常用)

  1. 获取指定的构造器
    • getDeclaredConstructor()
      • 参数一:指明构造器的参数列表
  2. 保证此构造器可访问:使用setAccessible()方法
  3. 调用此构造器创建运行时类的对象:newInstance()方法
Class aClass = Class.forName("Get.Person");

Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);

Person p = (Person) declaredConstructor.newInstance("Tom");
System.out.println(p);

技术分享图片

反射机制

原文:https://www.cnblogs.com/CrabDumplings/p/13525023.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!