反射基础与原理

一、反射的定义

反射是Java被视为动态语言的关键。
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

二、三种方式获取Class对象

  1. 通过类名获取 类名.class
Class clazz = Person.class;
  1. 通过对象获取 对象名.getClass()
Person p = new Pserson();
Class clazz = p.getClass();
  1. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
 String className = "com.test.refle.Person";
 Class clazz = Class.forName(className);

三、反射基本操作

1.获取构造信息

//获取所有构造方法
Constructor<Person>[] constructors = (Constructor<Person>[]) clazz.getConstructors();
//获取单个指定参数的构造方法
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
//使用构造器创建对象
Person p= constructor.newInstance("fanshao", 21);

2.获取方法信息

//获取所有公共和父类方法
Method[] methods = clazz.getMethods();
//获取所有方法,包括私有方法
methods = clazz.getDeclaredMethods();
//获取指定方法
Method method = clazz.getDeclaredMethod("setName", String.class);

//反射调用方法
Object p = clazz.newInstance();
method.invoke(p ,"fanshao");

//反射调用私有方法
method = clazz.getDeclaredMethod("setRealName", String.class);
method.setAccessible(true);
method.invoke(p, "fanshao-real");

3.获取字段信息

//获取所有字段,包括私有字段
Field[] fields = clazz.getDeclaredFields();
//获取指定字段
Field field = clazz.getDeclaredField("name");
//获取字段的值
Person p = new Person("fnashao",21);
Object val = field.get(person);
//设置字段的值
field.setAccessible(true);
field.set(person,"fanshao2");

4.通过反射创建数组

Array.newInstance(String.class, 12);

5.通过反射获取泛型信息

1.TypeVariable

泛型类型变量,封装泛型上下限等信息。

public class TypeVariableTest<T extends Cloneable & Closeable, K> {

    T t;
    K k;
    public static void main(String[] args) {

        try {
            // 获取字段的类型
            Field field1 = TypeVariableTest.class.getDeclaredField("t");
            Field field2 = TypeVariableTest.class.getDeclaredField("k");
            TypeVariable typeT = (TypeVariable) field1.getGenericType();
            TypeVariable typeK = (TypeVariable) field2.getGenericType();
            //获取字段名称
            System.out.print(typeT.getName() + "\n"); // T
            System.out.print(typeK.getName() + "\n"); // K
            // 获取泛型上界
            /*泛型T的上界:interface java.lang.Cloneable
            泛型T的上界:interface java.io.Closeable*/
            for (Type type : typeT.getBounds()) {
                System.out.print("泛型T的上界:" + type + "\n");
            }

            /*泛型K的上界:class java.lang.Object   默认上界为Object*/
            for (Type type : typeK.getBounds()) {
                System.out.print("泛型K的上界:" + type + "\n");
            }
        } catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }
}
2.ParameterizedType

具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。

public class ParameterizedTypeTest {

    Map<String, Integer> map;
    List<Integer>[] list;
    public static void main(String[] args)  {

        try{
            Field f = ParameterizedTypeTest.class.getDeclaredField("map");
            
            // java.util.Map<java.lang.String, java.lang.Integer>
            System.out.println(f.getGenericType()); 
            ParameterizedType pType = (ParameterizedType) f.getGenericType();
            
            //interface java.util.Map
            System.out.println(pType.getRawType());
            
            //class java.lang.String
            //class java.lang.Integer
            for (Type type : pType.getActualTypeArguments()) {
                System.out.println(type);
            }
        }catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }
}

如果是数组,通过GenericArrayType获取

Field f = ParameterizedTypeTest .class.getDeclaredField("list");
GenericArrayType genericType = (GenericArrayType) f.getGenericType();

四、反射的原理

1.类加载机制

要理解反射的原理,需要先搞清楚类加载机制。

Java文件需要经历编译和运行两个过程。
编译就是通过javac命令将.java文件编译成字节码即.class文件;
运行则是把编译生成的.class文件交给Java虚拟机(JVM)执行。
类加载过程就是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。
另外JVM是按需加载,某个类在用到的时候才会加载,且只会加载一次。

类的加载机制有三个阶段:加载、链接、初始化;三个阶段交叉进行。
链接又有三个过程:验证、准备、解析。

2.Java的反射

Java的反射和类加载过程是类似的。
Java的反射就是利用上面第一步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。

3.Java的反射和ClassLoader对比

Class.forName 和 ClassLoader 都可以用来装载类,如前面说的包含加载、链接、初始化等操作,但是它们装载类的方式是有区别,简单来说:ClassLoader的loadClass不会执行类的初始化代码。
Class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
如果有静态变量,变量的赋值执行了静态方法,那么Class.forName()也会执行静态方法。
并且静态代码块的执行顺序先于静态方法。
Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容