精华内容
下载资源
问答
  • java 非常好用的反射框架Reflections
    2020-04-10 09:44:51

    Reflections通过扫描classpath,索引元数据,并且允许在运行时查询这些元数据。

    使用Reflections可以很轻松的获取以下元数据信息:

    1)获取某个类型的所有子类;比如,有一个父类是TestInterface,可以获取到TestInterface的所有子类。

    2)获取某个注解的所有类型/字段变量,支持注解参数匹配。

    3)使用正则表达式获取所有匹配的资源文件

    4)获取特定签名方法。

    通常的用法有:

    引入依赖jar

    <dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections</artifactId>
        <version>0.9.10</version>
    </dependency>


    项目中使用:

    // 初始化工具类
    Reflections reflections = new Reflections(new ConfigurationBuilder().forPackages(basePackages).addScanners(new SubTypesScanner()).addScanners(new FieldAnnotationsScanner()));
     
    // 获取某个包下类型注解对应的类
    Set<Class<?>> typeClass = reflections.getTypesAnnotatedWith(RpcInterface.class, true);
     
    // 获取子类
    Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
     
    // 获取注解对应的方法
    Set<Method> resources =reflections.getMethodsAnnotatedWith(SomeAnnotation.class);
     
    // 获取注解对应的字段
    Set<Field> ids = reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
     
    // 获取特定参数对应的方法
    Set<Method> someMethods = reflections.getMethodsMatchParams(long.class, int.class);
     
    Set<Method> voidMethods = reflections.getMethodsReturn(void.class);
     
    Set<Method> pathParamMethods =reflections.getMethodsWithAnyParamAnnotated(PathParam.class);
     
    // 获取资源文件
    Set<String> properties = reflections.getResources(Pattern.compile(".*\\.properties"));

    具体也可以参见官方文档:Java Code Examples for org.reflections.scanners.MethodAnnotationsScanner

    更多相关内容
  • java编程中,使用反射来增强灵活性(如各类框架)、某些抽象(如各类框架)及减少样板代码(如Java Bean)。因此,反射在实际的java项目中被大量使用。由于项目里存在反射的性能瓶颈,使用的是ReflectASM高性能反射库来...

    java编程中,使用反射来增强灵活性(如各类框架)、某些抽象(如各类框架)及减少样板代码(如Java Bean)。

    因此,反射在实际的java项目中被大量使用。

    由于项目里存在反射的性能瓶颈,使用的是ReflectASM高性能反射库来优化。

    因此,在空闲时间研究了下的这个库,并做了简单的Beachmark。

    介绍

    ReflectASM是使用字节码生成来加强反射的性能。

    反射包含多种反射,这个库很简单,它提供的特性则是:

    根据匹配的字符串操作成员变量。

    根据匹配的字符串调用成员函数。

    根据匹配的字符串调用构造函数。

    这三种也恰恰是实际使用中最多的,且在特殊场景下也容易产生性能问题。

    例子

    举个例子,使用MethodAccess来反射调用类的函数:

    Person person = new Person();

    MethodAccess m = MethodAccess.get(Person.class);

    Object value = m.invoke(person, "getName");

    更多的例子参考官方文档,这个库本身就不大,就几个类。

    实现原理

    MethodAccess.get方法

    static public MethodAccess get (Class type) {

    ArrayList methods = new ArrayList();

    boolean isInterface = type.isInterface();

    if (!isInterface) {

    Class nextClass = type;

    while (nextClass != Object.class) {

    addDeclaredMethodsToList(nextClass, methods);

    nextClass = nextClass.getSuperclass();

    }

    } else {

    recursiveAddInterfaceMethodsToList(type, methods);

    }

    int n = methods.size();

    String[] methodNames = new String[n];

    Class[][] parameterTypes = new Class[n][];

    Class[] returnTypes = new Class[n];

    for (int i = 0; i < n; i++) {

    Method method = methods.get(i);

    methodNames[i] = method.getName();

    parameterTypes[i] = method.getParameterTypes();

    returnTypes[i] = method.getReturnType();

    }

    String className = type.getName();

    String accessClassName = className + "MethodAccess";

    if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName;

    Class accessClass;

    AccessClassLoader loader = AccessClassLoader.get(type);

    synchronized (loader) {

    try {

    accessClass = loader.loadClass(accessClassName);

    } catch (ClassNotFoundException ignored) {

    String accessClassNameInternal = accessClassName.replace('.', '/');

    String classNameInternal = className.replace('.', '/');

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

    MethodVisitor mv;

    /* ... 字节码生成 */

    byte[] data = cw.toByteArray();

    accessClass = loader.defineClass(accessClassName, data);

    }

    }

    try {

    MethodAccess access = (MethodAccess)accessClass.newInstance();

    access.methodNames = methodNames;

    access.parameterTypes = parameterTypes;

    access.returnTypes = returnTypes;

    return access;

    } catch (Throwable t) {

    throw new RuntimeException("Error constructing method access class: " + accessClassName, t);

    }

    }

    大致逻辑为:

    通过java反射获取必要的函数名、函数类型等信息。

    动态生成一个用于调用被反射对象的类,其为MethodAccess的子类。

    反射生成动态生成的类,返回。

    由于里面包含字节码生成操作,所以相对来说这个函数是比较耗时的。

    我们来分析一下,如果第二次调用对相同的类调用MethodAccess.get()方法,会不会好一些?

    注意到:

    synchronized (loader) {

    try {

    accessClass = loader.loadClass(accessClassName);

    } catch {

    /* ... */

    }

    }

    因此,如果这个动态生成的MethodAccess类已经生成过,第二次调用MethodAccess.get是不会操作字节码生成的。

    但是,前面的一大堆准备反射信息的操作依然会被执行。所以,如果在代码中封装这样的一个函数试图使用ReflectASM库:

    Object reflectionInvoke(Object bean, String methodName) {

    MethodAccess m = MethodAccess.get(bean.getClass());

    return m.invoke(bean, methodName);

    }

    那么每次反射调用前都得执行这么一大坨准备反射信息的代码,实际上还不如用原生反射呢。这个后面会有Beachmark。

    为什么不在找不到动态生成的MethodAccess类时(即第一次调用)时,再准备反射信息?这个得问作者。

    动态生成的类

    通过idea调试器获取动态生成类的字节码

    那么那个动态生成的类的内部到底是什么?

    由于这个类是动态生成的,所以获取它的定义比较麻烦。

    一开始我试图寻找java的ClassLoader的API获取它的字节码,但是似乎没有这种API。

    后来,我想了一个办法,直接在MethodAccess.get里面的这行代码打断点:

    byte[] data = cw.toByteArray();

    通过idea的调试器把data的内容复制出来。但是这又遇到一个问题,data是二进制内容,根本复制不出来。

    一个一年要400美刀的IDE,为啥不能做的贴心一点啊?

    既然是二进制内容,那么只能设法将其编码成文本再复制了。通过idea调试器自定义view的功能,将其编码成base64后复制了出来。

    然后,搞个python小脚本将其base64解码回.class文件:

    #!/usr/bin/env python3

    import base64

    with open("tmp.txt", "rb") as fi, open("tmp.class", "wb") as fo:

    base64.decode(fi, fo)

    反编译.class文件,得到:

    //

    // Source code recreated from a .class file by IntelliJ IDEA

    // (powered by Fernflower decompiler)

    //

    package io.github.frapples.javademoandcookbook.commonutils.entity;

    import com.esotericsoftware.reflectasm.MethodAccess;

    public class PointMethodAccess extends MethodAccess {

    public PointMethodAccess() {

    }

    public Object invoke(Object var1, int var2, Object... var3) {

    Point var4 = (Point)var1;

    switch(var2) {

    case 0:

    return var4.getX();

    case 1:

    var4.setX((Integer)var3[0]);

    return null;

    case 2:

    return var4.getY();

    case 3:

    var4.setY((Integer)var3[0]);

    return null;

    case 4:

    return var4.toString();

    case 5:

    return Point.of((Integer)var3[0], (Integer)var3[1], (String)var3[2]);

    default:

    throw new IllegalArgumentException("Method not found: " + var2);

    }

    }

    }

    可以看到,生成的invoke方法中,直接根据索引使用switch直接调用。

    所以,只要使用得当,性能媲美原生调用是没有什么问题的。

    MethodAccess.invoke方法

    来看invoke方法内具体做了哪些操作:

    abstract public Object invoke (Object object, int methodIndex, Object... args);

    /** Invokes the method with the specified name and the specified param types. */

    public Object invoke (Object object, String methodName, Class[] paramTypes, Object... args) {

    return invoke(object, getIndex(methodName, paramTypes), args);

    }

    /** Invokes the first method with the specified name and the specified number of arguments. */

    public Object invoke (Object object, String methodName, Object... args) {

    return invoke(object, getIndex(methodName, args == null ? 0 : args.length), args);

    }

    /** Returns the index of the first method with the specified name. */

    public int getIndex (String methodName) {

    for (int i = 0, n = methodNames.length; i < n; i++)

    if (methodNames[i].equals(methodName)) return i;

    throw new IllegalArgumentException("Unable to find non-private method: " + methodName);

    }

    如果通过函数名称调用函数(即调用invoke(Object, String, Class[], Object...),

    则MethodAccess是先遍历所有函数名称拿到索引,然后根据索引调用对应方法(即调用虚函数invoke(Object, int, Object...),

    实际上是通过多态调用字节码动态生成的子类的对应函数。

    如果被反射调用的类的函数很多,则这个遍历操作带来的性能损失不能忽略。

    所以,性能要求高的场合,应该预先通过getIndex方法提前获得索引,然后后面即可以直接使用invoke(Object, int, Object...)来调用。

    Beachmark

    谈这种细粒度操作级别的性能问题,最有说服力的就是实际测试数据了。

    下面,Talk is cheap, show you my beachmark.

    首先是相关环境:

    操作系统版本: elementary OS 0.4.1 Loki 64-bit

    CPU: 双核 Intel® Core™ i5-7200U CPU @ 2.50GHz

    JMH基准测试框架版本: 1.21

    JVM版本: JDK 1.8.0_181, OpenJDK 64-Bit Server VM, 25.181-b13

    Benchmark Mode Cnt Score Error Units

    // 通过MethodHandle调用。预先得到某函数的MethodHandle

    ReflectASMBenchmark.javaMethodHandleWithInitGet thrpt 5 122.988 ± 4.240 ops/us

    // 通过java反射调用。缓存得到的Method对象

    ReflectASMBenchmark.javaReflectWithCacheGet thrpt 5 11.877 ± 2.203 ops/us

    // 通过java反射调用。预先得到某函数的Method对象

    ReflectASMBenchmark.javaReflectWithInitGet thrpt 5 66.702 ± 11.154 ops/us

    // 通过java反射调用。每次调用都先取得Method对象

    ReflectASMBenchmark.javaReflectWithOriginGet thrpt 5 3.654 ± 0.795 ops/us

    // 直接调用

    ReflectASMBenchmark.normalCall thrpt 5 1059.926 ± 99.724 ops/us

    // ReflectASM通过索引调用。预先取得MethodAccess对象,预先取得某函数的索引

    ReflectASMBenchmark.reflectAsmIndexWithCacheGet thrpt 5 639.051 ± 47.750 ops/us

    // ReflectASM通过函数名调用,缓存得到的MethodAccess对象

    ReflectASMBenchmark.reflectAsmWithCacheGet thrpt 5 21.868 ± 1.879 ops/us

    // ReflectASM通过函数名调用,预先得到的MethodAccess

    ReflectASMBenchmark.reflectAsmWithInitGet thrpt 5 53.370 ± 0.821 ops/us

    // ReflectASM通过函数名调用,每次调用都取得MethodAccess

    ReflectASMBenchmark.reflectAsmWithOriginGet thrpt 5 0.593 ± 0.005 ops/us

    可以看到,每次调用都来一次MethodAccess.get,性能是最慢的,时间消耗是java原生调用的6倍,不如用java原生调用。

    最快的则是预先取得MethodAccess和函数的索引并用索引来调用。其时间消耗仅仅是直接调用的2倍不到。

    jmh框架十分专业,在基准测试前会做复杂的预热过程以减少环境、优化等影响,基准测试也尽可能通过合理的迭代次数等方式来减小误差。

    所以,在默认的迭代次数、预热次数下,跑一次基准测试的时间不短,CPU呼呼的转。。。

    最后总结

    在使用ReflectASM对某类进行反射调用时,需要预先生成或获取字节码动态生成的MethodAccess子类对象。

    这一操作是非常耗时的,所以正确的使用方法应该是:

    在某个利用反射的耗时函数启动前,先预先生成这个MethodAccess对象。

    如果是自己里面ReflectASM封装工具类,则应该设计缓存,缓存生成的MethodAccess对象。

    如果不这样做,这个ReflectASM用的没有任何意义,性能还不如java的原生反射。

    如果想进一步提升性能,那么还应该避免使用函数的字符串名称来调用,而是在耗时的函数启动前,预先获取函数名称对应的整数索引。

    在后面的耗时的函数,使用这个整数索引进行调用。

    展开全文
  • Java反射机制:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的...

    Java反射机制:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

    反射的好处:

          1.可以在程序运行过程中,操作这些对象。

          2.可以进行解耦,提高程序的扩展性。

    • Java代码在计算机中的三个阶段

    1. Sources源代码阶段:*.java被编译成*.class字节码文件。

    2.Class类对象阶段:*.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于描述在内存中描述字节码文件),

    Class对象将原字节码文件中的成员变量,构造函数,方法等的做了封装。

    3.Runtime运行阶段:创建对象的过程new

    •  讲解获取Class对象的方式

    获取Class对象的三种方式对应着java代码在计算机中的三个阶段:

    • 源代码阶段

    Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。

    • Class类对象阶段

    类名.class:通过类名的属性class获取

    • Runtime运行时阶段

    对象.getClass():getClass()方法是定义在Object类中的方法。

    结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。

    测试代码:

    package com.company.reflect;
    
    import com.company.reflect.domain.Person;
    
    /**
     * ⊙﹏⊙&&&&&&⊙▽⊙
     *
     * @Auther: pangchenbo
     * @Date: 2020/5/9 10:37
     * @Description:
     */
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
            //方式一:Class.forName("全类名")
            Class<?> aClass = Class.forName("com.company.reflect.domain.Person");
            System.out.println(aClass);
            //方式二:类名.class
            Class<Person> personClass = Person.class;
            System.out.println(personClass);
            //方式三:对象.getClass()
            Person person = new Person();
            Class<? extends Person> aClass1 = person.getClass();
            System.out.println(aClass1);
            //比较 == 三个对象
            System.out.println(aClass == aClass1);
            System.out.println(personClass==aClass1);
        }
    }
    

    两个true表示Class对象是同一个。

    •  获取Class对象功能

    (1)获取成员变量们
    		Field[] getFields() :获取所有public修饰的成员变量
    		Field getField(String name)   获取指定名称的 public修饰的成员变量
    
    		Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
    		Field getDeclaredField(String name)
    (2)获取构造方法们
    		Constructor<?>[] getConstructors()  
    		Constructor<T> getConstructor(类<?>... parameterTypes)  
    
    		Constructor<?>[] getDeclaredConstructors()  
    		Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
    (3)获取成员方法们
    		Method[] getMethods()  
    		Method getMethod(String name, 类<?>... parameterTypes)  
    
    		Method[] getDeclaredMethods()  
    		Method getDeclaredMethod(String name, 类<?>... parameterTypes)
    •  Field:成员变量

    先写一个测试类

    public class Person {
        private String name;
        private int age;
        public String a;
        protected String b;
        String c;
        private String d;
    
        public Person() {
    
        }
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", a='" + a + '\'' +
                    ", b='" + b + '\'' +
                    ", c='" + c + '\'' +
                    ", d='" + d + '\'' +
                    '}';
        }
        //无参方法
        public void eat(){
            System.out.println("eat...");
        }
    
        //重载有参方法
        public void eat(String food){
            System.out.println("eat..."+food);
        }
    }
    • 获取所有的public修饰的成员变量
    //0.获取Person对象
            Class<Person> personClass = Person.class;
            //1.获取所有public修饰的成员变量
            Field[] fields = personClass.getFields();
            for (Field field : fields) {
                System.out.println(field);
            }

     

    • 获取特定的成员变量(public)
    //2.Field getField(String name)
            Field a = personClass.getField("a");
            //获取成员变量a 的值 [也只能获取公有的,获取私有的或者不存在的字符会抛出异常]
            Person person = new Person();
            Object o = a.get(person);
            System.out.println("o  value: "+o);
            //设置属性a的值
            a.set(person,"haha");
            System.out.println(person);

     

    • 获取全部的成员变量
    //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
            Field[] declaredFields = personClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField+" ");
            }
            System.out.println("==============================");

     

    • 获取特定的成员变量,在这里如果需要对private进行修改,就必须进行暴力反射,将
    d.setAccessible(true);设置为true
    System.out.println("==============================");
            Field d = personClass.getDeclaredField("d");
            d.setAccessible(true);//暴力反射
            d.get(person);
            d.set(person,"222");
            System.out.println(person);

    •  普通方法获取

    获取指定名称的方法(不带参数的获取)

    Class<Person> personClass = Person.class;
            //获取指定名称的方法
            Method eat = personClass.getMethod("eat");
            Person person = new Person();
            eat.invoke(person);//执行方法

     

    获取指定名称的方法(带参数获取)

    //获取具有参数的构造方法
            Method eat1 = personClass.getMethod("eat", String.class);
            eat1.invoke(person,"fans");
            System.out.println("===============================");

     

    获取方法列表

    Method[] methods = personClass.getMethods();
            for (Method method : methods) {
                System.out.println(method);//继承的方法也会被访问(前提是方法是public)
            }

     

     如果设置的方法中含有私有的方法,也可以设置d.setAccessible(true);设置为true,然后就可以访问私有方法。

    • 构造方法

    • 获取无参数的构造器
    Class<Person> personClass = Person.class;
            //Constructor<?>[] getConstructors()
            Constructor<?>[] constructors = personClass.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            //Constructor<T> getConstructor(类<?>... parameterTypes)
            //获取无参
            Constructor<Person> constructor1 = personClass.getConstructor();
            System.out.println(constructor1);
                //利用获取的构造器创建对象
            Person person = constructor1.newInstance();
            System.out.println(person);

     

    • 获取有参数的构造器
    //获取有参
            Constructor<Person> constructor = personClass.getConstructor(String.class,Integer.class);
            System.out.println(constructor);
            Person person1 = constructor.newInstance("PCB",100);
            System.out.println(person1);
            //理应Class类对象进行对象的构建获取
            Person person2 = personClass.newInstance();
            System.out.println(person2);
            //对于getDeclaredConstructor方法和getDeclaredConstructors方法,此外在构造器的对象内也有setAccessible(true);方法,并设置成true就可以操作了。

    •  设计简单的框架,理解反射的好处

    准备测试类

    package com.company.reflect.domain;
    
    /**
     * ⊙﹏⊙&&&&&&⊙▽⊙
     *
     * @Auther: pangchenbo
     * @Date: 2020/5/9 13:27
     * @Description:
     */
    public class Student {
        public void sleep(){
            System.out.println("sleep...");
        }
    }
    

     准备文件properties文件

    className = com.company.reflect.domain.Student
    methodName = sleep
    •  需求

    写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。

    • 实现

    (1)配置文件 (2)反射

    • 步骤

    (1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中 (2)在程序中加载读取配置文件 (3)使用反射技术来加载类文件进内存 (4)创建对象 (5)执行方法

     

    package com.company.reflect.反射案例;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    /**
     * ⊙﹏⊙&&&&&&⊙▽⊙
     *
     * @Auther: pangchenbo
     * @Date: 2020/5/9 13:30
     * @Description:
     */
    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            /**
             * 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
             */
            //1.加载配置文件
            //1.1创建Properties对象
            Properties properties = new Properties();
            //1.2加载配置文件,转换为一个集合
            //1.2.1获取class目录下的配置文件  使用类加载器
            ClassLoader classLoader = ReflectTest.class.getClassLoader();
            InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
            properties.load(resourceAsStream);
            //2.获取配置文件中定义的数据
            String className = properties.getProperty("className");
            String methodName = properties.getProperty("methodName");
    
            //加载类到内存中
            Class<?> aClass = Class.forName(className);
            //创建对象
            Object o = aClass.newInstance();
            //获取对象方法
            Method method = aClass.getMethod(methodName);
            //执行方法
            method.invoke(o);
    
        }
    }
    

    改变配置文件

    className = com.company.reflect.domain.Person
    methodName = eat

    • 好处

    我们这样做有什么好处呢,对于框架来说,是人家封装好的,我们拿来直接用就可以了,而不能去修改框架内的代码。但如果我们使用传统的new形式来实例化,那么当类名更改时我们就要修改Java代码,这是很繁琐的。修改Java代码以后我们还要进行测试,重新编译、发布等等一系列的操作。而如果我们仅仅只是修改配置文件,就来的简单的多,配置文件就是一个实实在在的物理文件。

     

    展开全文
  •  既然反射机制是框架设计的灵魂,那我们就先简单说一下什么是框架:是一个半成品软件,框架中的代码已经是软件的一部分,但因为是半成品所以不能独立运行,我们可以在框架的基础上进行软件开发;利用框架开发软件可以做到...

    框架

     既然反射机制是框架设计的灵魂,那我们就先简单说一下什么是框架:是一个半成品软件,框架中的代码已经是软件的一部分,但因为是半成品所以不能独立运行,我们可以在框架的基础上进行软件开发;利用框架开发软件可以做到简化编码的作用.
     反射机制是框架设计的灵魂;其实我们在使用框架的时候,不会使用反射也没有关系,因为框架都为我们写好了;假如你要开发一套框架需要用到深入理解掌握反射机制
     但是!我们理解掌握了反射,在学习框架的时候就能用的更好,因为你知道了内部框架实现的一些原理,你也就能清晰的明白了,所以对于反射还是得重点的去掌握.

    反射

    • 概念:将类的各个组成部分封装为其他对象,这就是反射机制

     概念是比较抽象的,让我来通俗易懂的语言来表述下:

     我们先忘记反射那个概念,然后我们聊一个你熟悉的东西:Java代码。运行Java代码可以通过一个Java类创建一个对象,这个过程是什么样子?也就是Java代码在计算机中经历的阶段

     Java代码从无到有到运行结束一共经历三个阶段:Source源代码阶段、Class类对象阶段、Runntime运行时阶段

    第一阶段:Source源代码阶段

     这个阶段的大体过程是类文件(Person.java)中写一个类(public class Person),类中有成员变量、构造方法、成员方法,但是这样一个完整类文件还不能够直接运行,还需要一个编译的操作(javac)后形成一个字节码文件(Person.class).字节码文件中有三个比较重要的部分分别对应着Java类文件中的成员变量、构造方法、成员方法.源代码阶段的类文件和字节码文件并没有进内存,还是在硬盘上.这时候要是按照我们之前的做法,就是new一个Person对象,这就到了我们的第三阶段.

    第二阶段:Class类对象阶段

     字节码文件要是能变成对象(对象在内存里),我们需要把字节码文件加载到内存中才能够有对象.第二阶段做的是一件非常重要的是事情就是把源代码阶段的Class文件通过类加载器(ClassLoader)加载进内存,在内存里会有一个对象(Class类对象)来描述Class字节码文件的共同的特征和行为.
     内存中的Class类对象也有比较重要的三部分(成员变量、构造方法、成员方法),每一部分又有各自的特点,比如说成员变成可以get set ,构造方法可以创建对象,成员方法可以来运行执行,所以将他们分别封装为 Field对象、Contructor对象、Method对象
    通过Class类对象的行为我们就可以来创建对象(Person对象)

    第三阶段:Runntime运行时阶段

     new对象

    java代码三个阶段

     然后我们在回头看一下概念,就容易理解了,那反射机制有什么好处呢?

    好处

    1. 可以在程序运行过程中,操作这些对象。
    2. 可以解耦,提高程序的可扩展性。

    怎么理解程序运行中操作对象?

     我们在代码管理工具中新建一个类文件,定义一个字符串变量并赋值,然后我们用变量名加点的形式,代码管理工具就会提示我们很多方法

    提示

     代码管理工具之所有可以提示那么多方法,其实内部就是利用的反射机制,我们定义了一个字符串,类加载器就会把这个字节码文件加载进内存,在内存中有一个Class类对象.Class类对象已经把所有的方法抽取出来封装成Method对象,然后把所有的方法都放进了一个Method[] 中,然后需要提示的时候,工具只需要遍历成员展示出来就可以了.

    获取Class字节码对象的三种方式

     概念的解释是需要大量的语言去表述的,理解概念之后的操作就变得简单易懂起来,下面获取Class字节码对象我们就可以使用代码简单的体现下就可以啦,都是很好理解的.

    1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象 多用于配置文件,将类名定义在配置文件中。(第一阶段获取方式)

    2. 类名.class:通过类名的属性class获取 多用于参数的传递(第二阶段获取方式)

    3. 对象.getClass():getClass()方法在Object类中定义着 多用于对象的获取字节码的方式(第三阶段获取方式)

    注意:
     同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

    package cn.icnfox.Java;
    
    public class Person {
    
    	private String name;
    	private int age;
    	
    	public String sex;
    	public String cn;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public Person(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	public Person() {
    		super();
    		// TODO Auto-generated constructor stub
    	}
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", cn=" + cn + "]";
    	}
    		
            public void eat() {
    		System.out.println("吃饭饭");
    	}
    	public void eat(String food) {
    		System.out.println("吃饭饭"+food);
    	}
    }
    
    package cn.icnfox.Java;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class Test {
    
    	public static void main(String[] args) throws Exception {
    		
    		//获取class 方法一  class.forname
    		Class class1 = Class.forName("cn.icnfox.Java.Person");
    		System.out.println(class1);
    		
    		//获取class 方法二  类名.class
    		Class class2 =Person.class;
    		System.out.println(class1==class2);
    
    		//获取class 方法三  类名.getclass
    		Person p = new Person();
    		Class class3 =p.getClass();
    		System.out.println(class1==class3);
    	}
    }
    
    
    //运行结果
    class cn.icnfox.Java.Person
    true
    true
    

    使用Class对象

    获取功能:

    • 获取成员变量们

    • Field[] getFields() :获取所有public修饰的成员变量

    • Field getField(String name) 获取指定名称的 public修饰的成员变量

    • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

    • Field getDeclaredField(String name)

    package cn.icnfox.Java;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class Test {
    
    	public static void main(String[] args) throws Exception {
    		//获取成员变量
    		Field[] fields = class1.getFields();
    		//遍历输出
    		for (Field field : fields) {
    			System.out.println(field);
    		}
    		
    		//获取成员变量 sex
    		Field sex = class1.getField("sex");
    		//设置sex
    		sex.set(p, "男");
    		//获取sex
    		Object value = sex.get(p);
    		System.out.println(value);
    		System.out.println("-----");
    		
    		Field[] fields2 = class1.getDeclaredFields();
    		for (Field field : fields2) {
    			System.out.println(field);
    		}
    		
    		Field dname = class1.getDeclaredField("name");
            //Class获取到私有变量,通过设置 setAccessible(true) 直接对私有变量进行getset操作为暴力反射
    		dname.setAccessible(true);//暴力反射
    		dname.set(p, "张三");
    		System.out.println(p);
    	}
    }
    
    
    //运行结果
    public java.lang.String cn.icnfox.Java.Person.sex
    public java.lang.String cn.icnfox.Java.Person.cn
    男
    -----
    private java.lang.String cn.icnfox.Java.Person.name
    private int cn.icnfox.Java.Person.age
    public java.lang.String cn.icnfox.Java.Person.sex
    public java.lang.String cn.icnfox.Java.Person.cn
    Person [name=张三, age=0, sex=, cn=null]
    
    • 获取构造方法们

    • Constructor<?>[] getConstructors()

    • Constructor getConstructor(类<?>… parameterTypes)

    • Constructor getDeclaredConstructor(类<?>… parameterTypes)

    • Constructor<?>[] getDeclaredConstructors()

    package cn.icnfox.Java;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class Test2 {
    
    	public static void main(String[] args) throws Exception {
    
    			Person p = new Person();
    			Class class1 =p.getClass();
    			System.out.println("--------1");
    		//获取构造器数组
    			Constructor[] constructors = class1.getConstructors();
    			for (Constructor constructor : constructors) 
    				{
    			System.out.println(constructor); 
    			}
    			System.out.println("--------2");
    		//获取构造器
    		    Constructor con= class1.getConstructor(String.class, int.class);
    			System.out.println(con);
    		//使用构造器创建对象
    			Object Persont = con.newInstance("张三",21);
    			System.out.println(Persont);
    			System.out.println("--------3");
    		//我们尝试下获取空参数构造器
    		//获取空参数构造器
    		    Constructor con1= class1.getConstructor();
    			System.out.println(con1);
    		//使用构造器创建对象
    			Object Persont1 = con1.newInstance();
    			System.out.println(Persont1);
    			System.out.println("--------4");
    		//空参数构造可以简化成一下代码	
    			Object Person2 = class1.newInstance();
    			System.out.println(Person2);
    			
    		//其他的获取就不挨个演示了
    		//con1.setAccessible(true); 也有暴力反射
    	}
    }
    
    //运行结果
    --------1
    public cn.icnfox.Java.Person(java.lang.String,int)
    public cn.icnfox.Java.Person()
    --------2
    public cn.icnfox.Java.Person(java.lang.String,int)
    Person [name=张三, age=21, sex=null, cn=null]
    --------3
    public cn.icnfox.Java.Person()
    Person [name=null, age=0, sex=null, cn=null]
    --------4
    Person [name=null, age=0, sex=null, cn=null]
    
    • 获取成员方法们

    • Method[] getMethods()

    • Method getMethod(String name, 类<?>… parameterTypes)

    • Method[] getDeclaredMethods()

    • Method getDeclaredMethod(String name, 类<?>… parameterTypes)

    • 获取全类名

    • String getName()

    package cn.icnfox.Java;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class Test3 {
    
    	public static void main(String[] args) throws Exception {
    
    			Person p = new Person();
    			Class class1 =p.getClass();
    			System.out.println("--------1");
    			//获取方法无参
    			Method method = class1.getMethod("eat");
    			//执行方法
    			Person p1= new Person();
    			method.invoke(p1);
    			
    			//获取方法有参
    			Method method2 = class1.getMethod("eat",String.class);
    			//执行方法
    			method2.invoke(p1,"和肉肉");
    			System.out.println("--------2");
    			
    			//获取所有方法 此处会打印出object的方法
    			Method[] methods = class1.getMethods();
    			for (Method method3 : methods) {
    				System.out.println(method3);
    				//获取方法名
    				String name = method3.getName();
    				System.out.println(name);
    				//method3.setAccessible(true);支持暴力反射
    			}
    			//获取类名
    			System.out.println("--------3");
    			String name2 = class1.getName();
    			System.out.println(name2);
    			
    	}
    }
    
    
    //运行结果
    --------1
    吃饭饭
    吃饭饭和肉肉
    --------2
    public java.lang.String cn.icnfox.Java.Person.toString()
    toString
    public java.lang.String cn.icnfox.Java.Person.getName()
    getName
    public void cn.icnfox.Java.Person.setName(java.lang.String)
    setName
    public void cn.icnfox.Java.Person.eat()
    eat
    public void cn.icnfox.Java.Person.eat(java.lang.String)
    eat
    public int cn.icnfox.Java.Person.getAge()
    getAge
    public void cn.icnfox.Java.Person.setAge(int)
    setAge
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    wait
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    wait
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    wait
    public boolean java.lang.Object.equals(java.lang.Object)
    equals
    public native int java.lang.Object.hashCode()
    hashCode
    public final native java.lang.Class java.lang.Object.getClass()
    getClass
    public final native void java.lang.Object.notify()
    notify
    public final native void java.lang.Object.notifyAll()
    notifyAll
    --------3
    cn.icnfox.Java.Person
    
    

    本人个人网站: https://www.icnfox.cn 欢迎来访
    有任何问题可以在个人网站的评论区留言,看到就会第一时间回复 啾咪ヾ(≧▽≦*)o

    展开全文
  • 思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式? Java中创建对象大概有这几种方式: 1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式 2、使用Clone的方法:无论何时...
  • 框架框架要解决的核心问题我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用...
  • java反射技术,号称是编程界的九阳神功,也可以说是框架的灵魂。也正是这种反射机制使静态语言的java具备了动态语言的某些特质。就是有了反射,才让java动态,编程的时候更加灵活。 补充:动态语言 和 静态语言 ...
  • Java反射

    千次阅读 2021-08-27 14:46:18
    反射就是把Java类中的各个成员映射成一个个的Java对象。         即在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;      &...
  • 7.Java反射面试题

    2021-12-27 21:42:47
    Java反射Java反射面试题1、除了使用new创建对象之外,还可以用什么方法创建对象?2、Java反射创建对象效率高还是通过new创建对象的效率高?3、java反射的作用4、哪里会用到反射机制?5、反射的实现方式6、实现Java...
  • java反射机制原理详解

    千次阅读 2021-03-22 13:49:40
    反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java反射机制。这里你将颠覆原来对java的理解。Java反射机制的...
  • 图解java反射机制及常用应用场景

    千次阅读 多人点赞 2022-02-27 17:13:53
    一、什么是java反射? 二、Hello World 三、类加载与反射关系 四、操作反射的java类 五、反射的常用场景 六、反射的优缺点
  • Java反射的作用

    2021-03-12 20:20:57
    灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助。那么什么是Java的反射呢? 大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的...
  • Java 反射最佳实践

    2021-03-14 15:07:30
    概要:最简单优雅的使用反射。本文的例子都可以在示例代码中看到并下载,如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request。本文的示例代码主要是基于一、需求今天一个“懒”...
  • java反射机制的实现原理反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java的反射机制。这里你将颠覆原来对java的...
  • 反射之Class对象2.1 Java代码在计算机的三个阶段2.2 获取Class对象的三种方式3. 反射之Class对象功能3.1 构造方法的获取与使用3.2 成员方法的获取与使用3.3 成员变量的获取与使用4. 综合案例 1. 反射的概念 在说...
  • Java反射机制详解——框架的灵魂所在ClassClass的获取Class的名字获取修饰符获取Class的成员获取Field获取Method获取Constructor构造器FieldField类型获取获取修饰符Field的读取和赋值Method获取方法名获取方法参数...
  • Java框架总结

    万次阅读 多人点赞 2020-01-17 14:14:13
    本系列用来记录常用java框架的基本概念、区别及...SSH框架JAVA EE中三种框架所集成,分别是Struts,Spring,Hibernate框架所组成,是当前比较流行的java web开源框架。 集成SSH框架的系统从职责上分为(Struts2--...
  • 总结Java反射面试题(附答案)

    千次阅读 2021-02-26 10:09:08
    今天总结了java反射面试题10道,各位java程序员可以根据这篇java反射面试题,来回顾一下java反射的相关知识。1、java反射的作用是什么?答:反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法...
  • 原标题:关于java反射技术实例介绍背景介绍:什么是反射?它的应用场景是什么?反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。重点:...
  • Java反射的常用用法

    2022-01-25 06:58:12
    反射获取class对象的四种方式?
  • 对于Java反射因为不是经常使用,所以记录一下,方便自己查阅 测试类: public class Test { private String name; public String getName() { return name; } public void setName( String name) { this....
  • 文章目录什么是反射反射能干什么实现反射的几种方式反射的实现反射的缺点优点:缺点:需要考虑反射的慢吗有什么方法可以替代反射吗javassit生成代码BCELASM其他框架应该选哪个呢扩展字面常量不会触发初始化 ...
  • Java反射机制与框架原理 邵发,清华大学毕业,从业软件开发十余年,自201...
  • Java--反射

    千次阅读 2021-03-03 15:20:16
    Java反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取...
  • Java反射框架设计的灵魂 框架:办成品软件,可以在框架的基础上进行开发 反射:将类的各个部分封装成对象,这就是反射机制 反射的好处 在程序运行的过程中,操作这些对象 可以降低程序的耦合性,提高程序的可...
  • 本篇文章主要给大家介绍java反射机制有什么用,希望对需要的朋友有所帮助!Java语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象(根据安全性限制),无需提前硬编码目标类。这些特性...
  • Java Spring 框架详解

    千次阅读 2021-01-11 18:18:02
    2.3.1 使用构造方法注入 Spring 框架可以采用java反射机制,通过构造方法完成依赖注入。下面开始代码演示: 我们在demo2中演示。 目的:在service 中使用构造方法依赖注入 TestDIDao 接口对象。 创建 dao 包 ...
  • Java基础篇:反射机制详解

    万次阅读 多人点赞 2018-09-29 10:19:50
    反射Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,784
精华内容 34,313
关键字:

java 反射框架 对比

java 订阅