键盘敲烂,月薪过万作业不做,等于没学
当前系列: J&C 修改讲义

什么是反射?

Reflection的直译。

角度一:镜子

一个对象,照照镜子:我是谁?(从哪儿来?要到哪儿去?^_^)

程序自己运行时(不是:程序员在读/写代码时),得到自己的:

  • 类型信息:类名
  • 类的成员信息:字段、构造函数、方法
  • 类的外部环境信息:程序集(C#中的.dll)或包(Java中的.jar)信息
static void grow(Person person) {
	//告诉我person是啥?Student,还是Teacher
	System.out.println(person.getClass().getName());
}
grow(new Student());

并且能根据这些信息构建对象,获取对象数据,调用方法等……

角度二:正反

正常的,如果要获得一个对象,必须得用代码new……

用反射,就可以通过一个(字符串格式的)类名,生成一个对象:

Object fg = Class.forName("Teacher").newInstance();


为什么需要反射?

实际上,作为应用(区别于框架)开发人员,使用反射的机会不多。

歪门邪道

拿到private的成员

康庄大道

框架开发人员使用:
  • MVC:根据url(字符串)生成相应的Controller实例,调用其方法。
  • ORM:拿到entity类名做表名,属性名做列名,生成SQL语句(字符串)
  • 依赖注入:生成注入的实现类对象

注意理解,作为框架开发人员,事先并不知道用户(应用开发人员)会定义什么Controller/ORM实体类/依赖注入的实现类,所以没有什么if...else


反射是如何实现的?

简单的说,在编译源代码的时候,编译器就

  • 收集了所有的类型信息,
  • 并且让开发人员可以通过Class(Java)/Type(C#)获取

jar和dll

反射收集的信息并不局限于类型,而是要一直扩展到项目的基本可执行单元:.jar(Java)或者.dll(C#)

这同样是编译器完成的,编译的时候,就创建了一个清单(manifest),把该可执行单元所有的信息列明,反射也依靠该manifest获取相关信息

关于性能

不用测试,想都想得到,使用反射(这种非常规方式)调用一个方法,和直接new出对象调用方法相比,肯定要慢一些。因为反射本质上就是把编译时做的事情放到了运行时做,编译时可以由编译器进行优化,运行时就不行。

但是,

  • 性能并不是一个项目的全部(再次复习,代码三要求:性能、安全和可维护性),而且
  • 性能就是用来“浪费”的嘛——很多同学笔记本从来不关,是不是浪费,是?为什么要这样做?方便嘛,第二天不用等着开机嘛

所以仍然有这么多框架大量的、广泛的使用了反射(当然也进行了性能优化,主要是使用缓存),天没有塌下来!


特性/注解

Java中被称之为注解(annotation);C#中被称之为特性(Attribute)。

使用起来就好像给类/接口/方法/其他类成员等打的(一个或多个)“标签”:Java使用@开头,C#用方括号包裹

编译识别

比如,标识一个类/方法等已经被废弃

@Deprecated  //Java
[Obsolete]    //C#

演示:被注解的类/方法能够运行,但是会报警告

这通常用于需要更改的老旧代码,相较于直接删除或更改,这种方式给了调用者详细的通知,更加的友好,不至于让调用者莫名其妙。

单元测试

复习:单元测试 / TDD

终于可以给大家隆重介绍单元测试利器JUnit了!(C#中对应NUnit

注意(理解):

  1. 框架开发人员在编写JUnit时并不知道(作为使用者的)“我”会写什么类和方法
  2. JUnit项目没有一个main()入口函数,所以它怎么知道从哪里开始呢?谁调用谁呢?
  3. JUnit通过注解/特性,识别那些方法应该测试(Test),哪些可以略过(Ignore)

演示:eclipse上运行Junit项目

测试难题/技巧

因为测试结果是基于测试数据的,单元测试要求稳定的、不变的测试数据。但是

  1. 测试数据很有可能是公用的,
  2. 运行单元测试,必然有可能影响到测试数据

此外单元测试要求跑得快,因为它应该随时随地的跑。

复习

  • 双向链表的所有测试,插入/删除/交换,都使用的同一个链表
  • 按TDD要求开发:定义一个方法,跑一次测试;完成一个方法,再跑一次测试)

使用JUnit,我们可以:

  1. 按类组织单元测试,
  2. 每一个类,定义一个公用的测试环境
  3. 类里面的每个测试方法,跑之前/后重新生成/清理测试环境
  4. 对单元测试分组归类,不用每次都跑所有的单元测试

其他框架

注解/特性还广泛应用于其他框架,比如:

  • MVC框架
  • ORM框架

这些我们都在后面继续学习。


作业

  1. 封装方法:
    1. GetPrivateField(),可以获得某对象的某私有字段值
    2. SetPrivateField(),可以修改某对象的某私有字段值
    提示:能不能用使用泛型?
  2. 自定义一个注解/特性HelpMoneyChanged(帮帮币变化):
    1. 该特性只能用于方法
    2. 默认可以接受一个int类型的参数amount,表示帮帮币变化的数量
    3. 有一个string类型的Message属性,记录帮帮币变化的原因
  3. 将HelpMoneyChanged应用于Publish()方法
  4. 用反射获取Publish()上的特性实例,输出其中包含的信息
  5. JUnit/NUnit
    1. 重写之前的双向链表单元测试。
    2. 为之前作业添加单元测试,包括但不限于:
      1. 数组中找到最大值
      2. 二分查找
      3. 栈的压入弹出
      4. 找到100以内的所有质数 [难]
      5. 猜数字游戏 [难]
学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 J&C 中继续学习:

上一课: J&C:Lambda表达式

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码