大家好,我是雄雄,欢迎关注公众号【雄雄的小课堂】。
本文参考自:《编写高质量代码 改善java程序的151个建议》
作者:秦少波
反射的效率是一个老生常谈的问题,有“经验”的开发人员经常使用这句话恐吓新人:反射的效率是非常低的,不到万不得已就不要使用。事实上,这句话的前半句是对的,后半句是错的。
反射的效率相对于正常的代码执行确实低很多(经过测试,相差15倍左右),但是它是一个非常有效的运行期工具类,只要代码结构清晰、可读性好那就先开发起来,等到进行性能测试时证明此处性能确实有问题时再修改也不迟(一般情况下反射并不是性能的终极杀手,而代码结构混乱、可读性差则很可能会埋下性能隐患)。我们看这样一个例子:在运行期获得泛型类的泛型类型,代码如下:
前面我们讲过,Java的泛型类型只存在于编译期,那为什么这个工具类可以取得运行期的泛型类型呢?那是因为该工具只支持继承的泛型类,如果是在Java编译时已经确定了泛型类的类型参数,那当然可以通过泛型获得了。例如有这样一个泛型类:
对于UserDao类,编译器编译时已经明确了其参数类型是String,因此可以通过反射的方式来获取其类型,这也是getGenricClassType方法使用的场景。
BaseDao和UserDao是ORM中的常客,BaseDao实现对数据库的基本操作,比如增删改查,而UserDao则是一个比较具体的数据库操作,其作用是对User表进行操作,如果BaseDao能够提供足够多的基本方法,比如单表的增删改查,那些与UserDao类似的BaseDao子类就可以省去大量的开发工作。但问题是持久层的session对象(这里模拟的是HibernateSession)需要明确一个具体的类型才能操作,比如get查询,需要获得两个参数:实体类类型(用于确定映射的数据表)和主键,主键好办,问题是实体类类型怎么获得呢?
读取配置问题?可行,但效率不高。
最好的办法就是父类泛型化,子类明确泛型参数,然后通过反射读取相应的类型即可,于是就有了我们代码中的clz变量:通过反射获得泛型类型。如此实现后,UserDao可以不用定义任何方法,继承过来的父类操作方法已经能满足基本需求了,这样代码结构清晰,可读性又好。我已将这种方式使用在多个项目中了,目前没有出现因为该反射引起的性能问题。
想想看,如果考虑反射效率问题,没有clz变量,不使用反射,每个BaseDao的子类都要实现一个查询操作,代码将会大量重复,违反了“Don’t Repeat Yourself”这条最基本的编码规则,这会致使项目重构、优化难度加大,代码的复杂度也会提高很多。
对于反射效率问题,不要做任何的提前优化和预期,这基本上是杞人忧天,很少有项目是因为反射问题引起系统效率故障的(除非是拷贝工的垃圾代码,这不在我们的讨论范围之内),而且根据二八原则,80%的性能消耗在20%的代码上,这20%的代码才是我们关注的重点,不要单单把反射作为重点关注对象。
领取专属 10元无门槛券
私享最新 技术干货