谈谈java中的反射

2,581 阅读5分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金

抽奖说明在文末哦!

什么是反射

反射机制指的是程序在运行时能够获取自身的信息即在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。反射是Java的特征之一,是一种间接操作目标对象的机制。

反射的基础之class

Class类是反射实现的基础,要想理解反射,首先要理解Class类。在程序运行期间,虚拟机为所有的对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象所属的类的完整结构信息,包括包名、类名、实现的接口、拥有的方法和字段等。可以通过特殊的Java类访问这些信息,这个类就是Clas 类。可以把Class类理解为类的类型,一个Class对象,称为类的类型对象,一个Class对象对应一个加载到JVM中的一个.class文件。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }
    
    ...
}    

首先JVM会将代码编译成一个.class字节码文件,然后被类加载器(Class Loader)加载进JVM的内存中,同时会创建一个类的Class对象存到堆中(注意这个不是new出来的对象,而是类的类型对象)。JVM在创建类对象前,会先检查其类是否加载,寻找类对应的Class对象,若加载好,则为其分配内存,然后再进行初始化。

在加载完一个类后,堆内存的方法区就产生了一个Class对象,这个对象就包含了完整的类的结构信息,通过这个Class对象可以看到类的结构,就好比一面镜子。所以称之为:反射。 更多class类加载知识请参考前面的文章:jvm系列:类加载机制

反射的基本使用

反射机制的常用的类

Java.lang.Class; 

Java.lang.reflect.Constructor;  

Java.lang.reflect.Field;

Java.lang.reflect.Method; 

Java.lang.reflect.Modifier;

反射常用的API

对象. setAccessible(true);

Class:

获取公开属性:getField("属性名");

获取私有属性:getDeclaredField("属性名"); 

通过指定方法名称获取公开无参方法对象:getMethod("方法名", null);

获取所有公开方法对象:getMethods();

获取所有方法对象:getDeclaredMethods();

通过指定方法名称获取私有有参方法对象:getDeclaredMethod("方法名", 方法参数的类型......);

调用公开有参构造:getConstructor(构造参数类型);

调用私有有参构造:getDeclaredConstructor(构造参数类型);

判断是否是某个类的实例:isInstance(obj);

Field:

获取属性名:getName();

获取属性的类型:getType();

获取属性的修饰符:getModifiers();

设置属性值 1:类实例化对象 要设置的参数值:set(obj,obj);

Method:

方法对象.invoke(类实例化对象, 方法的参数数组);  //执行方法

Constructor:

NewInstance();//通过构造获取到类的实例对象

下面看代码:

public class Test(){
A a=new A();

Class c1=A.class;//任何一个类都有一个隐含的静态成员变量class

Class c2=a.getClass();//在已知类的对象的情况下通过getClass方法获取

Class c3 = null;

try {
    c3 = Class.forName("com.xxxx.A");//类的全称
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
}

class A{}

方法的反射:getName

getName方法可以打印出类类型的类名称。

public class Test2 {
    public static void main(String[] args) {
        Class c1 = String.class;//String类的类类型
        Class c2 = void.class;
        System.out.println(c1.getName());
        System.out.println(c2.getName());
    }
}

成员变量反射

public class TestUtil {
    public static void printFieldMessage(Object obj){
        Class c = obj.getClass();
        //Field[] fs = c.getFields();
    }

getFields()方法获取的所有的public的成员变量的信息。和方法的反射那里public的成员变量,也有一个获取所有自己声明的成员变量的信息:Field[] fs = c.getDeclaredFields(),然后遍历得到的结果如下:

for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}

构造函数的反射

public static void Test(Object obj){
Class c = obj.getClass();
/*
* java.lang. Constructor中封装了构造函数的信息,它有两个方法:
* getConstructors()方法获取所有的public的构造函数
* getDeclaredConstructors()方法得到所有的自己声明的构造函数
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表->参数列表的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}

总结

反射在实际编程中应用并不多,但是很多设计都与反射机制有关,比如:动态代理机制,JDBC连接数据库, Spring/Hibernate框架(实际上是因为使用了动态代理,所以才和反射机制有关)。

抽奖说明

1.本活动由掘金官方支持 详情可见juejin.cn/post/701221…

2.通过评论和文章有关的内容即可参加,评论越走心越有机会中奖哦。

3.本月的文章都会参与抽奖活动,欢迎大家多多互动哦!

4.除掘金官方抽奖外本人也将送出周边礼物(马克杯一个和掘金徽章若干,马克杯将送给走心评论,徽章随机抽取,数量视评论人数增加)。