讲透Java反射概念

什么是反射

我始终认为要彻底理解一个概念,对这个东西出现的前因后果搞搞清楚一定是一个不错的方法。所以我们先来讨论为什么会出现反射这个东西。

想一想,我们平时构造一个对象用的是什么方法?我想,90%的情况下都是这样的:Student student = new Student()。之所以可以这么做是因为我们可以确定这个场景下我们此时此刻要用的类就是这个 Student 类,更深一步,我们明确地知道这个类里面的哪几个属性是我们需要用到的,然后调用对应的getset 方法即可。但往往,一个系统里肯定不止一个类,因此有时候,我们可能需要一些通用的方法来实现一些相同或相似的功能。举个例子:比如我们的系统中有两个类: StudentTeahcer ,它们的定义如下:
Student:

public class Student {
    private String sno;
    private String name;
    @Override
    public String toString() {
        return "Student{" +
                "sno='" + sno + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

Teacher:

public class Teacher {
    private String tno;
    private String name;
    private Integer salary;
    @Override
    public String toString() {
        return "Teacher{" +
                "tno='" + tno + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

现在想实现这样一个功能:

打印每一个对象的详细信息(调用toString方法)

你可以怎么做呢?
首先,最简单的,你可以编写两个方法: printStudentDetail(Student student)printTeacherDetail(Teacher teacher),然后在每一个方法中调用对应类型的toString()方法即可。但这样会带来一个问题,当我们系统中元数据类型不断增加,对于每一个类型我们都需要编写对应的 print 方法,这是一个很麻烦的工作。因此我们想能不能通过一个方法来实现呢?于是你有了下面的主意:

void printDetail(Object obj){
    if (obj instanceof Student){
        Student student = (Student) obj;
        System.out.println(student.toString());
    }
    if (obj instanceof Teacher){
        Teacher teacher = (Teacher) obj;
        System.out.println(teacher.toString());
    }
}

通过一个Object类传入对象,然后在方法内部判断这个对象到底是哪一个类的实例对象,然后调用对应的方法。但是还是会面临几乎一样的问题:随着系统中元数据类型不断增加,这个方法中就需要增加更多的判断,代码非常冗杂,毫无优雅可言。
既然这个过程这么麻烦,我们不妨想一想造成这种复杂的根本原因是什么?似乎是因为我们的实例对象并不知道它自己是谁。 如果有一个聪明的对象知道它自己是谁(是哪个类)并且知道自己可以干嘛(这个类中有哪些方法),那么我们让它调用一下自己的打印方法就可以了,不用我们再去判断它的类型,在手动调用对应类中的方法了。举一个不恰当的例子:

某一天一个兄弟找到你让你帮他回家,但是他并不知道自己家在哪里,甚至并不知道自己是谁,你可以怎么做呢?

一个笨办法:你先去北京市户籍部门问一下这兄弟是不是北京人,是的话交给你们了,不是的话那我再去河南省户籍部门问问。如此反复,你需要问34次。但是如果这兄弟知道自己是谁这个问题就好了,直接问他是哪里人,然后把他送到哪里就好啦。

谈到这里,我们的需求很明显了。我们需要一个东西,它可以让每一个对象知道自己是谁。这样的话在一些通用的方法中就可以少掉很多冗余代码。这个东西有吗?有!这就是反射。
现在再来谈反射的概念就很清晰了,我贴一下百度来的概念。

JAVA机制反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的**反射**机制。

现在再来看这个“标准答案”是不是清晰很多了呢。

回到刚才的那个问题。我们利用反射机制,可以让这个对象知道自己是哪个一类,这个类有哪些属性和方法,然后让去调用它的方法就好了。于是就有了下面的代码:

void printDetail(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class clazz = obj.getClass(); //问它到底是哪个类
    Method[] methods = clazz.getDeclaredMethods();  //拿到这个类的所有方法
    for(Method method : methods){
        if (method.getName().equals("toString")){  //如果它有这个方法,就执行它
            System.out.println(method.invoke(obj));
        }
    }
}

通过一个函数,可以实现对所有类的详细信息的打印,真正实现了“通用”的目的。

反射的作用

明白了反射是什么?我们接下来讨论反射可以用来干嘛。简单来说,它可以帮助在代码运行的过程中,动态地判断一个对象所属的类,动态地构造一个对象,动态地判断拿到这个类的属性和方法甚至动态地调用这个类的方法。没错,关键词:“动态”。如果你认为反射仅仅能完成第一节中的那个问题就片面了。实际上,那并不是一个非常常规的反射应用场景。反射的功能非常强大,我们可能会在很多场景下用到它,我这里举几个常规的例子:

Spring框架

没错,如此强大的Spring框架也是应用了反射的原理。行内有一句这样的老话:反射机制是Java框架的基石。一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经封装好了,自己基本用不着写。典型的除了hibernate之外,还有spring也用到很多反射机制。最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:

  1. 将程序内所有 XML 或 Properties 配置文件加载入内存中
  2. Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息
  3. 使用反射机制,根据这个字符串获得某个类的Class实例
  4. 动态配置实例的属性

Spring这样做的好处是:

  1. 不用每一次都要在代码里面去new或者做其他的事情
  2. 以后要改的话直接改配置文件,代码维护起来就很方便了
  3. 有时为了适应某些需求,Java类里面不一定能直接调用另外的方法,可以通过反射机制来实现

动态sql

当我们需要写一些复杂查询的时候,可能会涉及到非常多的字段和诸多实体类,如果我们一个属性一个属性装配的话会导致代码非常冗杂。这时候可以使用反射机制,直接拿传入参数中的属性名(比如参数以 Map<String,Object> 的形式接收)去某个对象中查是否有这个属性,有的话就进行生成sql的操作,这样可以帮助我们在写复杂sql的时候省掉100句“cnmd”。

一些其他的说明

本文产生于我工作时的一个小困境,也就是类似于动态生成sql的内容。于是上网学习了关于反射的相关知识。但我看到的大多数文档更加聚焦于反射的语法和代码的使用,而对其如何产生和因何存在的问题的表述都较为笼统,不利于学习者真正理解反射这个东西。本文是我在自己的理解的基础上所做出的一些解释。对于有争议的内容,希望和大家一起讨论,以达到更深层次的理解。而至于语法层面的内容,我始终认为学习者不应该陷入语法的沼泽,因此不做讨论,如有需要可自行查阅。

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

推荐阅读更多精彩内容