Java反射机制详解

写在前面的话:
很多人会说我直接new一个对象不就完了么,干嘛还用反射来获取对象。因为new属于静态编译,而反射属于动态编译,反射只有到运行时他才会去获得该对象的实例。从这点上可以看出反射的强大之处,我们接下来就来详细介绍Java反射机制。

首先我们先来看几种获取class对象的方法。(铺垫)####

  1. 所有的引用数据类型(类-类型)的类名、基本数据类型都可以通过.class方式获取其Class对象(对于基本数据类型的封装类还可以通过.TYPE 的方式获取其Class对象,但要注意,TYPE实际上获取的是封装类对应的基本类型的Class对象的引用,那么你可以判断出int.class==Integer.TYPE 返回true,int.class==Integer.class返回false)。通过这种方式不会初始化静态域。使用.class.TYPE的方式获取Class对象叫做类的字面常量。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且它根除了对forName方法的引用,所以也更高效。
  2. Class的forName(String name)传入一个类的完整类路径也可以获得Class对象,但由于使用的是字符串,必须强制转换才可以获取泛型的Class<T>的Class对象,并且你必须获取这个方法可能抛出的ClassNotFoundException异常。这种方法可以初始化静态域。
  3. 还可通过类的对象实例下的getClass()方法来获取Class对象,即实例名.getClass()。

再来看看反射机制的定义。####

百科上给出的定义是:Java反射机制是指在运行状态中对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。

使用反射机制有什么好处呢,哪些场合会用到呢?####

上面反射机制的定义还是很言简意赅的。因为反射可以在运行时加载、探知、使用完全未知的类,你只要给我一个路径就可以了,我通过这个路径拿到我需要的东西,我的操作与你无关了,灵活性更高了,类之间的耦合性也就降低了。

同时我们可以获取到类的私有信息,破坏了类的封装,使类变得不安全。使用反射还会降低程序性能。所以说使用反射优点突出,缺点同样突出。

反射应用到的场合:

  1. 在运行时判断任意一个对象所属的类。
Class aClass = Class.forName("com.Dan.Consumer");
System.out.print("?"+aClass.isInstance(new Consumer()));
// public boolean isInstance(Object obj)
// 当该 Class 对象表示一个已声明的类时,若指定的 Object 参数是所表示类(或其任一子类)的一个实例,则此方法返回 true;否则返回 false。
  1. 在运行时构造任意一个类的对象。
Class aClass1 = Class.forName("com.Dan.Consumer");
Object object = aClass1.newInstance();
// throws InstantiationException, IllegalAccessException
  1. 在运行时判断任意一个类所具有的成员变量和方法。
Class aClass2 = Class.forName("com.Dan.Consumer");
Method[] methods = aClass2.getDeclaredMethods();
Field[] fields = aClass2.getDeclaredFields();
  1. 在运行时调用任意一个对象的方法。
Class aClass3 = Class.forName("com.Dan.Consumer");
Method[] methods[]= aClass3.getDeclaredMethods();
Object object = aClass3.newInstance();
Object returnObject = methods.invoke(object,null);

简单的说就是程序运行时,如果需要外部传进来一个对象,然后在程序中运用这个对象,我们就可以通过配置文件中类的路径,得到这个对象的所有信息,从而加以应用。

Java的很多框架的底层都是用反射来实现的,比如Struts2,JDBC,SQLite...

那反射具体是怎么实现的呢?####

首先我们要知道Java里实现反射的那个类是java.lang.reflect。我们查看API在类摘要里主要有以下四个类:
① java.lang.reflect.AccessibleObject
② java.lang.reflect.Constructor<T>
③ java.lang.reflect.Field
④ java.lang.reflect.Method
其中AccessibleObject 是另外三个类的基类。我们接下来涉及的主要就是这几个类。

在本文一开始就介绍了三种获取class对象的方法,那么反射用到的是第2个方法。

  1. 获取类的对象
Class<?> clazz  = Class.forName("com.Dan.Consumer");
Object object = clazz.newInstance();
  1. 获取类的方法
public Method[] getDeclaredMethods()

返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

public Method getDeclaredMethod(String name,
                                Class<?>... parameterTypes)

返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

public Method[] getMethods()

返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共成员方法

public Method getMethod(String name,
                        Class<?>... parameterTypes)

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
调用方法(这里就不全贴了,可以参考下面总示例代码):

Method method = clazz.getMethod("info", String.class, long.class);//获取方法
method.invoke(object, "隔壁老王",2017032009);//通过invoke调用该方法

invoke:如果底层方法是静态的,或底层方法所需的形参数为 0,那么可以忽略指定的 obj 参数,该参数可以为 null。

  1. 获取成员变量信息
public Field[] getDeclaredFields()

返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段

public Field getDeclaredField(String name)

返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

public Field[] getFields()

返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。

public Field getField(String name)

返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
获得成员变量:

Field aField = clazz.getDeclaredField("name"); //因为name变量是private的,所以不能用getField方法
aField.setAccessible(true);//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
aField.set(object,"二大爷");
Object obj = aField.get(object);
System.out.println(obj);
  1. 获取构造器
public Constructor<?>[] getDeclaredConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getConstructor(Class<?>... parameterTypes)

构造器和上面的方法、属性都是大同小异的。需要注意的是,当返回一个数组的时候,返回类型是Constructor<?>[]而不是Constructor<T>[],因为从此方法返回之后,该数组可能被修改以保存不同类的 Constructor 对象。

代码示例####

一个关于顾客的类Consumer:

public class Consumer {
    private long id;//私有的
    public String name;//共有的
    /*没参数构造体*/
    public Consumer() {
    }
    /*有参数构造体*/
    public Consumer(long id, String name) {
        this.id = id;
        this.name = name;
    }
    /*getter setter*/
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    /*私有的无参buy方法*/
    private static void buy() {
        System.out.println("私有的无参buy方法");
    }
    /*共有的有参有返回值consume方法*/
    public String consume(String giftName) {
        System.out.println("买了一件礼物: " + giftName);
        return giftName;
    }
}

TestReflect类通过反射得到Consumer类的信息

public class TestReflect {
    public static void getProperty() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        try {
            /* 获取对象类型 */
         Class<?> clazz  = Class.forName("com.Dan.Consumer");
         Object object = clazz.newInstance();
            /* 获取到所有属性 */
            Field[] field = clazz.getDeclaredFields();
            for (Field f:field) {
                //String fieldName = f.getName();// 取到属性名字
                //System.out.println(fieldName);
                System.out.println(f);
            }
            /* 获取到所有的方法,包括私有的,但不包括父类的 */
            Method[] methods = clazz.getDeclaredMethods();
            for (Method m:methods){
                //String methodName = m.getName();
                //System.out.println(methodName);
                System.out.println(m);
            }
            /* 所有的构造体 */
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor c:constructors){
                System.out.println(c);
            }
            // 调用方法
            Method method = clazz.getMethod("info", String.class, long.class);//获取方法
            method.invoke(object, "隔壁老王",2017032009);
            //得到属性
            Field aField = clazz.getDeclaredField("name"); //因为name变量是private的,所以不能用getField方法
            aField.setAccessible(true);
            aField.set(object,"二大爷");
            Object obj = aField.get(object);
            System.out.println(obj);
            // 得到构造器
            Constructor constructor = clazz.getDeclaredConstructor(long.class, String.class);
            constructor.newInstance(2016040221, "王小二");
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, NoSuchFieldException {
        getProperty();
    }
}

// private long com.Dan.Consumer.id
// public java.lang.String com.Dan.Consumer.name
// public java.lang.String com.Dan.Consumer.getName()
// public void com.Dan.Consumer.setName(java.lang.String)
// public long com.Dan.Consumer.getId()
// private static void com.Dan.Consumer.buy()
// public void com.Dan.Consumer.setId(long)
// public void com.Dan.Consumer.info(java.lang.String,long)
// public com.Dan.Consumer()
// public com.Dan.Consumer(long,java.lang.String)
// 我是:隔壁老王,会员编号: 2017032009。
// 二大爷

写完喽!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ


知识重在总结和梳理,只有不断地去学习并运用,才能化为自己的东西。当你能为别人讲明白的时候,说明自己已经掌握了。

欢迎转载,转载请注明出处!

如果有错误的地方,或者有您的见解,还请不啬赐教!

喜欢的话,麻烦点个赞!

参考:https://zhidao.baidu.com/question/411858703.html

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 本篇文章继续介绍Java反射机制,不同的是侧重于介绍动态代理。动态代理是代理模式中的一种,是通过Java反射机制来...
    Ruheng阅读 3,263评论 1 44
  • 本篇文章主要讲解反射中泛型的一些知识点。主要分为两个部分:第一部分在反射中Class类使用泛型;第二部分通过反射来...
    Ruheng阅读 1,723评论 0 22
  • 真痛,真难受 认识那么久,发现自己还是不了解你,很多东西都猜测揣度不出 或许我根本不了解你吧 我知道你对我有期待,...
    阿立立哥阅读 167评论 0 0
  • 大数据时代,电商企业平台都有一个困扰点没有得到很好的解决:用户隐私不容侵犯,而为了提供更好的用户体验服务,必须获取...
    Redline阅读 878评论 0 0