Java 反射技术

本贴最后更新于 1616 天前,其中的信息可能已经物是人非

为什么需要反射,反射解决了什么问题,有什么好处

反射英文为 Reflection 其实更为贴切的翻译为 内省(自省),这里可以认为 “我是谁”,表明可以知道自己本身的数据,在程序中这些“本省”的数据就是 类, 属性,方法,构造器 等

这样对于静态编译型语言来说 在运行时 就会有一些动态的能力,比如 获取(设置)对象的属性和方法,创建对象等,提升了程序的灵活性

按照一般的解释:Java 反射是在运行状态中,对于任意一个类。都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,是一种动态获取信息以及动态调用对象的方法的功能

反射技术应用场景举例

  1. 实现通用框架基础功能

    用户可以基于框架定义的描述手段进行系统功能定义开发,框架在运行时 解析 获取用户 在开发阶段定义的描述信息,然后根据这些描述信息 动态地 去在运行时获取所需的 对象,对象的方法/属性等信息 进而执行相应的操作,完成系统的功能。这样可以大大的提升程序的开发效率。比如 spring 框架,框架本身定义了一些 Bean 的描述手段( xml 或者 配置注解),用户只需要按照描述规则编写代码,那么在运行时,spring 就会根据你的描述信息 去动态的 创建你的 bean,并且填充属性,初始化等操作,这样你就可以直接拿到这个 bean 去使用,而不是对于系统中的每个 bean,你都自己去手写每一个 bean 的对象创建,属性填充,初始化,注入等操作,如果这样写的话就够你喝一壶的了。其实更简单一点可以理解为 这样的框架 就像一个模子(模板),你按照他的规则来,它就能给你想要的东西。底层得话 大致上就是先解析描述信息,然后根据反射技术从描述中动态创建对象/填充属性/初始化/注入等等

  2. 实现作用于 "任意类型" 的函数(或者 json 序列化或者属性编辑器)

    这里比方说要实现一个 打印的 函数,参数可以是任意类型(Java 中 Object,go 中 interface{}),然后需要打印出 这个对象的属性以及值。
    这样的话就可以 通过反射技术 动态获取传进来对象的属性和值,然后组装成想要的格式打印出来,这样不管什么属性和值都能打印出来。比如用 Java 写一个打印方法:

    public static void print(Object object) {
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            try {
                System.out.println(declaredField.getName()+":"+declaredField.get(object));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
    另外 golang的 `Println` 函数底层 部分 就是用了反射技术
    
    

反射有什么弊端

在执行时对于 CPU 和内存资源会进行占用,在数据量很大的时候,需谨慎使用反射

在封装性上有些破坏,不过这也是一种权衡,毕竟解决问题才是目的

Java 反射

Java 大体上分为编译期和运行期。我们编写好源代码后,会通过 javac 命令编译成 字节码,然后程序运行时 会根据 字节码 装载到 Java 虚拟机,那么在运行期时是怎么表示 我们源代码的呢?其实也就是自身的数据,在运行时 有一些内建类来表示我们的程序,这些类如下:

类名 用途
Class 类的表示,可以表示类或者接口
Field 属性(成员变成)的表示
Method 方法的表示
Constructor 构造方法的表示

获取 Class 类对象的方式

一般可以通过 有下面 3 中方式

  1. Class.forName("全限定类名")
    这种方式 我们会在 加载数据库驱动的会用到 比如 Class.forName("com.mysql.jdbc.Driver")
  2. 类名.class
    Class.class
  3. 实例对象.getClass()
    obj.getClass()
class Clazz {

}

public class ClazzTest {
    public static void main(String[] args) throws Exception {
        Class<Clazz> clazz1 = Clazz.class;
        Class<Clazz> clazz2 = (Class<Clazz>) Class.forName("com.linn.slarn.spring.bean.register.refrect.Clazz");
        Class<? extends Clazz> clazz3 = new Clazz().getClass();
        // clazz1 == clazz2 == clazz3
        // ClassLoader一样的 所以相等
        // 参考 https://segmentfault.com/a/1190000023108785
    }
}

反射 API

当拿到 Class 对象后,就可以通过语言本身提供的 API 来进行我们想要的操作了

Class API

获取 Class 类相关方法
方法 作用
getClassLoader() 获取类加载器
getClasses() 返回包含该类中所有**公共(public)类和公共(public)**接口的对象 的数组
getDeclaredClasses() 返回包含该类中所有类和接口类的对象的数组
getName() 获得类的完整路径名字
getSimpleName() 获得类(简单)的名字
getPackage() 获得类的包
getSuperclass() 获得当前类继承的父类的名字
getInterfaces() getInterfaces()
forName(String className) 根据类名返回类的对象(这种在类的加载时是有初始化操作的)
newInstance() 创建内的实例

package com.linn.slarn.spring.bean.register.refrect;

class SuperClazz {

}

interface Interface1 {

}

interface Interface2 {

}

class Clazz extends SuperClazz implements Interface1, Interface2 {

    public class MemberClazz1 {

    }

    class MemberClazz2 {

    }

    interface MemberInterface1 {

    }

    public interface MemberInterface2 {

    }
}

public class ClazzTest {
    public static void main(String[] args) throws Exception {

        // 返回包含该类中所有 公共(public)类 和 公共接口(public) 的对象 的数组
        Class<?>[] classes = Clazz.class.getClasses();
        for (Class<?> aClass : classes) {
            System.out.println(aClass);
        }
        //interface com.linn.slarn.spring.bean.register.refrect.Clazz$MemberInterface2
        //class com.linn.slarn.spring.bean.register.refrect.Clazz$MemberClazz1

        System.out.println("=====");

        // 返回包含该类中所有类和接口的数组
        Class<?>[] declaredClasses = Clazz.class.getDeclaredClasses();
        for (Class<?> aClass : declaredClasses) {
            System.out.println(aClass);
        }
        //interface com.linn.slarn.spring.bean.register.refrect.Clazz$MemberInterface2
        //interface com.linn.slarn.spring.bean.register.refrect.Clazz$MemberInterface1
        //class com.linn.slarn.spring.bean.register.refrect.Clazz$MemberClazz2
        //class com.linn.slarn.spring.bean.register.refrect.Clazz$MemberClazz1

        System.out.println("=====");

        // 获取 Name(全限定的): com.linn.slarn.spring.bean.register.refrect.Clazz
        System.out.println(Clazz.class.getName());

        // 获取简单Name: Clazz
        System.out.println(Clazz.class.getSimpleName());

        // 获取包名: package com.linn.slarn.spring.bean.register.refrect
        System.out.println(Clazz.class.getPackage());

        // 获取父类  class com.linn.slarn.spring.bean.register.refrect.SuperClazz  如果没有的就是 超级父类Object
        Class<? super Clazz> superclass = Clazz.class.getSuperclass();
        System.out.println(superclass);

        // 获取 ClassLoader: sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader classLoader = Clazz.class.getClassLoader();
        System.out.println(classLoader);

        // 获取类的接口
        // interface com.linn.slarn.spring.bean.register.refrect.Interface1
        // interface com.linn.slarn.spring.bean.register.refrect.Interface2
        Class<?>[] interfaces = Clazz.class.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }

        System.out.println("=====");

        // 创建对象  每种方式创建的对象都是不一样的
        Clazz obj1 = Clazz.class.newInstance();
        System.out.println("T.class.newInstance() 创建的对象" + obj1); //Clazz@5cad8086

        Clazz clazz = new Clazz(); //@6e0be858
        System.out.println("new T() 创建的对象:" + clazz);
        Clazz obj2 = clazz.getClass().newInstance();
        System.out.println("new T().getClass().newInstance创建的对象" + obj2); //@6e0be858

        Clazz obj3 = (Clazz) Class.forName("com.linn.slarn.spring.bean.register.refrect.Clazz").newInstance();
        System.out.println("Class.forName 创建的对象 obj3:" + obj3); //@610455d6

    }
}
获取和注解相关的方法
方法 作用
getAnnotations() 返回该类所有的公有注解对象
getAnnotation(Class 获取类上的指定公有注解
getDeclaredAnnotation(Class 返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations() 返回该类所有的注解对象
getAnnotationsByType(Class 根据类型获取注解
// 两个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Annotation1 {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Annotation2 {
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Annotation0 {

}

@Annotation0
@Annotation1
@Annotation2
class Clazz {}

public class ClazzTest {

    public static void main(String[] args) {
        Class<Clazz> clazzClass = Clazz.class;

        // 需要注意所有的注解 比如是 @Retention(RetentionPolicy.RUNTIME) 运行时的才行

        // 获取类上的指定公有(public)注解
        Annotation1 annotationOne = clazzClass.getAnnotation(Annotation1.class);
        System.out.println(annotationOne);
        // @com.linn.slarn.spring.bean.register.refrect.annotation.Annotation1()

        System.out.println("=====");
        // 返回该类所有的公有(public)注解对象
        Annotation[] annotations = clazzClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //@com.linn.slarn.spring.bean.register.refrect.annotation.Annotation1()
        //@com.linn.slarn.spring.bean.register.refrect.annotation.Annotation2()

        System.out.println("=====");

        // 获取类上 所有的 注解 包括不是public的
        Annotation[] declaredAnnotations = clazzClass.getDeclaredAnnotations();
        for (Annotation declaredAnnotation : declaredAnnotations) {
            System.out.println(declaredAnnotation);
        }
        //@com.linn.slarn.spring.bean.register.refrect.Annotation0()
        //@com.linn.slarn.spring.bean.register.refrect.annotation.Annotation1()
        //@com.linn.slarn.spring.bean.register.refrect.annotation.Annotation2()
    }
}
获取和构造器相关的方法
方法 作用
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructors() 获得该类所有构造方法
getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
class Clazz {
    public Clazz() {

    }

    public Clazz(String name) {

    }

    public Clazz(String name,Integer age) {

    }

    Clazz(Long id, String name,Integer age) {

    }

    Clazz(Long id, String name,Integer age,Object object) {

    }

}

public class ClazzTest {
    public static void main(String[] args) throws Exception {
        Class<Clazz> clazzClass = Clazz.class;

        // 获取所有的 public 构造器哦
        Constructor<?>[] constructors = clazzClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        //public com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.String,java.lang.Integer)
        //public com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.String)
        //public com.linn.slarn.spring.bean.register.refrect.Clazz()

        System.out.println("=====");

        // 获取指定 public 构造器
        Constructor<Clazz> constructor = clazzClass.getConstructor(String.class, Integer.class);
        System.out.println(constructor);
        //public com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.String,java.lang.Integer)

        System.out.println("=====");
        //获取所有的构造器
        Constructor<?>[] declaredConstructors = clazzClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        //com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.Long,java.lang.String,java.lang.Integer,java.lang.Object)
        //com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.Long,java.lang.String,java.lang.Integer)
        //public com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.String,java.lang.Integer)
        //public com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.String)
        //public com.linn.slarn.spring.bean.register.refrect.Clazz()

        System.out.println("=====");
        // 获取指定的构造器
        Constructor<Clazz> declaredConstructor = clazzClass.getDeclaredConstructor(Long.class, String.class, Integer.class);
        System.out.println(declaredConstructor);
        //com.linn.slarn.spring.bean.register.refrect.Clazz(java.lang.Long,java.lang.String,java.lang.Integer)

    }
}
获取和方法相关的方法
方法 作用
getMethods() 获得该类所有**公有(public)**的方法
getDeclaredMethods() 获得该类所有方法
getMethod(String name, Class...<?> parameterTypes) 根据参数类型获得该类某个**公有(public)**的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 根据参数类型获得该类某个方法
class SuperClazz {
    public void superM1() {
    }

    void superM2(Long id, String s) {
    }
}

class Clazz extends SuperClazz {

    public void m1(int a) {

    }

    public int m2(int a, int b) {
        return a + b;
    }

    void m3() {
    }

    void m4(String s) {
    }
}

public class ClazzTest {
    public static void main(String[] args) throws Exception {
        Class<Clazz> clazzClass = Clazz.class;
        // 获取所有的公共(public)方法  包括Object中的方法
        Method[] methods = clazzClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //public int com.linn.slarn.spring.bean.register.refrect.Clazz.m2(int,int)
        //public void com.linn.slarn.spring.bean.register.refrect.Clazz.m1(int)
        //public void com.linn.slarn.spring.bean.register.refrect.SuperClazz.superM1()
        // Object中方法 很多个

        System.out.println("=====");

        // 获取所有的方法 但是不能获取父类的方法
        Method[] declaredMethod = clazzClass.getDeclaredMethods();
        for (Method method : declaredMethod) {
            System.out.println(method);
        }
        //public void com.linn.slarn.spring.bean.register.refrect.Clazz.m1(int)
        //void com.linn.slarn.spring.bean.register.refrect.Clazz.m4(java.lang.String)
        //public int com.linn.slarn.spring.bean.register.refrect.Clazz.m2(int,int)
        //void com.linn.slarn.spring.bean.register.refrect.Clazz.m3()

        System.out.println("=====");

        // 获取指定方法 (只能获取public的) ,
        Method m1 = clazzClass.getMethod("m1", int.class);
        System.out.println(m1);

        // 报错 (本类中不是public的)
        //Method m4 = clazzClass.getMethod("m4", String.class);

        // 获取父类的public方法
        Method superM1 = clazzClass.getMethod("superM1");
        System.out.println(superM1);
        //public void com.linn.slarn.spring.bean.register.refrect.SuperClazz.superM1()

        // 报错
        //Method superM2 = clazzClass.getMethod("superM2",Long.class,String.class);

        System.out.println("======");
        Method m11 = clazzClass.getDeclaredMethod("m1", int.class);
        System.out.println(m11);
        //public void com.linn.slarn.spring.bean.register.refrect.Clazz.m1(int)

        // 获取不是public的方法
        Method m4 = clazzClass.getDeclaredMethod("m4", String.class);
        System.out.println(m4);
        //void com.linn.slarn.spring.bean.register.refrect.Clazz.m4(java.lang.String)

        // 报错 不能获取父类 public 方法
        //Method superM11 = clazzClass.getDeclaredMethod("superM1");
        //System.out.println(superM11);

        // 获取父类 非 public 方法 报错, 不能获取父类的非public方法
        //Method superM2 = clazzClass.getDeclaredMethod("superM2", Long.class, String.class);

        // getMethods 获取所有 本类和父类的所有 public 方法
        // getMethod  指定 获取 本类和父类的所有 public 方法

        // getDeclaredMethods 只能获取本类所有方法,不能获取其他的 比如父类的任务方法
        // getDeclaredMethod  指定获取到本类所有方法,不能获取到父类任何方法
    }
}

注意:

getMethods: 获取所有 本类和父类的所有 public 方法
getMethod:  指定 获取 本类和父类的所有 public 方法
etDeclaredMethods: 只能获取本类所有方法,不能获取其他的 比如父类的任务方法
getDeclaredMethod: 指定获取到本类所有方法,不能获取到父类任何方法
获取和方法相关的方法
方法 作用
getFields() 获得该类所有**公有(public)**的属性,以及父级 public 属性
getDeclaredFields() 获得该类所有属性,不能获取父级的
getField(String name) 根据参数名 获得该类(或者父类,接口)的某个**公有(public)**的属性
getDeclaredField(String name) 根据参数名获得该类某个属性,只能是当前类
interface Interface0{
    String interfaceName = "0";
}

class SuperClazz {
    public String superName;
    Integer superAge;
}

class Clazz extends SuperClazz implements Interface0{
    public String name;
    Integer age;
}

public class ClazzTest {
    public static void main(String[] args) {
        Class<Clazz> clazzClass = Clazz.class;

        // 获取公共(public)属性,包括父类的或者接口的
        // getField 获取单个
        Field[] fields = clazzClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //public java.lang.String com.linn.slarn.spring.bean.register.refrect.Clazz.name
        //public static final java.lang.String com.linn.slarn.spring.bean.register.refrect.Interface0.interfaceName
        //public java.lang.String com.linn.slarn.spring.bean.register.refrect.SuperClazz.superName

        System.out.println("=====");
        // 获取所有本类属性
        // getDeclaredField 获取单个
        Field[] declaredFields = clazzClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //public java.lang.String com.linn.slarn.spring.bean.register.refrect.Clazz.name
        //java.lang.Integer com.linn.slarn.spring.bean.register.refrect.Clazz.age
    }
}

Field API

方法 作用
equals(Object obj) 属性与 obj 相等则返回 true
get(Object obj) 获得 obj 中对应的属性值
set(Object obj, Object value) 设置 obj 中对应属性值
class Clazz {
    public String name;
    private Integer age;
}

public class ClazzTest {

    public static void main(String[] args) throws Exception {

        Field name = Clazz.class.getField("name");

        Clazz clazz = new Clazz();
        // 给 clazz 的属性 name 设置 值
        name.set(clazz, "lijun");

        // 获取 clazz 中 name 的值
        System.out.println(name.get(clazz));

        System.out.println("=====");

        Field age = Clazz.class.getDeclaredField("age");
        // 对于 private 修饰的属性 设置为 true 才能操作
        age.setAccessible(true);
        age.set(clazz, 18);
        System.out.println(age.get(clazz));
    }
}

也有一块和注解相关的

Method API

方法 作用
invoke(Object obj, Object... args) 传递 object 对象及参数调用该对象对应的方法
class Clazz {
    public void add(int a, int b) {
        System.out.println("计算a+b的值"+(a+b));
    }
    public int add(int a, int b,int c) {
        int ret = a + b + c;
        System.out.println("计算a+b+c的值"+ret);
        return ret;
    }
}

public class ClazzTest {
    public static void main(String[] args) throws Exception {
        Method method = Clazz.class.getMethod("add",int.class,int.class);
        Object invoke1 = method.invoke(new Clazz(), 1, 1);
        System.out.println(invoke1);  // null
        Object invoke2 = method.invoke(Clazz.class.newInstance(), 1, 1);
        System.out.println(invoke2); //null
    
        System.out.println("=====");
    
        Method method1 = Clazz.class.getMethod("add",int.class,int.class,int.class);
        Object invoke3 = method1.invoke(Class.forName("com.linn.slarn.spring.bean.register.refrect.Clazz").newInstance(), 1, 1, 2);
        System.out.println(invoke3); // 4

        // 执行invoke时 ,第一个参数是 目标对象,后面的参数都是 方法的参数,
        // 如果目标方法的返回值为 void, 则 invoke执行后 返回的Object 为 null
        // 如果目标方法的返回值不为 void, 则invoke执行后 返回的 Object 为 目标方法本省返回的值
    }
}

Constructor API

方法 作用
newInstance(Object... initargs) 根据传递的参数创建类的对象
class Clazz {
    public Clazz() {}

    public Clazz(String name,Integer age) {}

    Clazz(String name,Integer age,Double d) {}
    private Clazz(String name,Integer age,Long id) {
        System.out.println("Clazz(String name,Integer age,Long id)");
    }
}
public class ClazzTest {
    public static void main(String[] args) throws Exception {
        Constructor<Clazz> declaredConstructor = Clazz.class.getDeclaredConstructor(String.class, Integer.class, Long.class);
        System.out.println("=====");
        Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType);
        }
        //class java.lang.String
        //class java.lang.Integer
        //class java.lang.Long

        // 创建对象 相应的构造方法会被调用 这里的话会在控制台中打印
        // 如果 构造方法为private 则需要设置 setAccessible(true)
        declaredConstructor.setAccessible(true);
        Clazz name = declaredConstructor.newInstance("lijun", 2, 3L);
    }
}

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖
  • 反射
    19 引用 • 29 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...