反射

反射简介

反射是指程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法。
使用反射,对于任意一个对象,都能调用它的任意一个属性和方法。因为类的信息保存在Class对象中,而这个Class对象是程序运行时由ClassLoader动态加载到内存中的。当ClassLoader加载了类之后,此类的Class信息就会保存到Java虚拟机内存模型中的方法区中。

反射所涉及到的类

  • java.long.Class
  • java.lang.reflect.Field
  • java.lang.reflect.Method
  • java.lang.reflect.Modifier
  • java.lang.reflect.Constructor

实现反射的三种方法

  • Class clazz = Class.forName("com.doctor.reflect.ReflectTest");
    Class.forName还有另外一个重载函数,forName(String name, boolean initialize, ClassLoader loader)用于标识是否初始化类和指定ClassLoader。
    Class.forName()会执行类的初始化,此初始化为加载、验证、准备、解析、初始化中的初始化,即在方法区中分配内存空间并执行static变量的初始化,其非static的全局变量不会在此时进行初始化,此初始化的意义和new一个对象对其初始化的意义不同。
  • Class clazz = ReflectTest.class();
    此操作不会对类进行初始化
  • Class clazz = new ReflectTest.getClass();
    此函数返回的类对象为运行时真正所指的对象,它所属的类型为Class对象

使用反射

  1. 创建对象
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Object object = clazz.newInstance();

使用newInstance()是创建类的一个实例,使用newInstance()有一个限制,即只能使用无参构造函数。

  1. 构造函数调用
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Constructor<?> constructor = clazz.getConstructor(int.class, String.class);
Object object = constructor.newInstance(18, "Doctor");

getConstructor函数会返回一个Constructor对象,它表示了反射的类对象中指定的公共构造方法

  1. 获取类中的属性
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Field field = clazz.getDeclaredField("fieldName");
  1. 修改类中属性和方法的访问权限
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true);

使用Field类的setAccessible方法访问限制,用它可以取消private访问限制或protected访问限制
field.setAccessible(true);设置这个属性可以被访问,假设在原始类中,field的修饰符为private,则其他类不能访问此属性,
调用setAccessible(true)并不是把此属性的访修饰符改为public。
在Java虚拟机的安全管理器中会使用checkPermission()方法来检查访问权限,而setAccessible(true)并不是把访问权限修改为public,而是取消Java的访问权限检查,因此对于public修饰的属性和方法,其默认access属性也为false。

  1. 修改属性的值
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Object object = clazz.newInstance();
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true);
// 假设属性的类型为一个String类型
field.set(object, "Doctor");

给属性设置值,为什么要在set函数中传入一个对象呢?因为目前使用的属性是属于某一对象。

  1. 获取属性的修饰符
Field field = clazz.getDeclaredField("fieldName");
String privStr = Modifier.toString(field.getModifiers());

getModifiers()返回值为int类型,代表类、成员变量、方法的修饰符。
其类型分别为:public, protected, private, static, final……

  1. 反射获取类中的方法
Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Object object = clazz.newInstance();
// 第一个参数为反射要调用的函数名,后面的参数为反射函数中要传入的参数类型
Method method = class.getDeclaredMethod("methodName", String.class, int.class);
method.invoke(object, "Doctor", 18);

Method中的invoke方法用于检测AccessibleObject的override属性是否为true。
AccessibleObject是Method, Field, Constructor的父类,override属性默认为false,可通过调用setAccessible()方法改变override的值,如果设置为true,则表示可以忽略访问权限进行方法调用,注意是忽略访问权限的限制,而不是改变其访问权限

注意事项

  1. getField 方法只能获取对象的public修饰的属性,并且也可以获取父类中的public属性,仅限于public属性
  2. getDeclaredField 方法能够获取到对象中所有修饰符修饰的属性,但是不能获取到父类中的任何属性
  3. 通过反射获取一个对象,可以使用Class.newInstance(),也可以使用Constructor.newInstance()。不同的之处在于:Class.newInstance()的使用受到了严格的限制,对应的Class中必须要有一个无参数的构造函数,并且这个无参的构造函数必须要有访问权限。这个访问权限是指:可以是public, protected, 或是默认包访问权限,但是不能使用private修饰而Constructor.newInstance()适用于任何访问类型的构造函数,无论是否有参数,只需要使用setAccessible()函数消除访问权限的限制即可。

通过反射调用类的静态方法

Class clazz = Class.forName("com.doctor.reflect.ReflectTest"); 
Object object = clazz.newInstance();
// 第一个参数为反射要调用的函数名,后面的参数为反射函数中要传入的参数类型
Method method = class.getDeclaredMethod("staticMethodName", String.class, int.class);
method.invoke(null, "Doctor", 18);

反射调用类的静态方法与反射调用类的非静态方法的区别在于invoke()函数传递的第一个参数上,当反射调用静态方法时,第一个参数传入null,因为静态方法属于类本身,所以不需要传递某一特定对象,传递null即可。

反射调用泛型方法

public class GenericMethodTest<T> {
    
    public void genericMethod(T t) {
        System.out.println(t.getClass());
    }
}
----------------------------------------------------------------------------------------------
public class ReflectTest {
    
    public void reflect() {
        try {
            Class<?> clazz = Class.forName("com.test.GenericMethodTest");
            Object object = clazz.newInstance();
            Method method = clazz.getDeclaredMethod("genericMethod", Object.class);
            method.setAccessible(true);
            method.invoke(object, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

涉及到泛型擦除的知识,
当一个函数中有泛型参数时,编译器会自动将其向上转型,T向上转型为Object,实际上genericMethod()函数中的T就向上转型为Object,所以genericMethod函数实际上是genericMethod(Object t), 所以在clazz.getDeclaredMethod方法中传递的参数类型为Object.class。

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

推荐阅读更多精彩内容

  • 一、Java 反射机制 参考了许多博文,总结了以下个人观点,若有不妥还望指正: Java 反射机制在程序运行时,对...
    野梦M阅读 463评论 0 1
  • 本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 转载请注明出处:https://www.jians...
    ming152阅读 6,780评论 4 79
  • 对于Java反射,平常工作中虽然经常用到,但一直以来都没有系统总结过,所以趁着目前有空总结一下,加深一下理解。 如...
    西华子阅读 1,340评论 0 10
  • 一、概述 1、Java反射机制(Java-Reflect): 在运行状态中,对于任意一个类,都能够知道这个类中的所...
    年少懵懂丶流年梦阅读 4,381评论 0 5
  • 类加载机制 1 什么是反射 Java反射机制是在运行状态中对于任意一个类,都能知道这个类的所以属性和方法;对于任何...
    凯玲之恋阅读 13,820评论 3 28