前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文读懂《Effective Java》第53条:接口优先于反射机制

一文读懂《Effective Java》第53条:接口优先于反射机制

作者头像
后台技术汇
发布2022-05-28 12:47:15
2060
发布2022-05-28 12:47:15
举报
文章被收录于专栏:后台技术汇

给定一个Class实例,我们可以获得Constructor(构造器)、Method(方法)和Field(域),而这些对象提供了“通过程序来访问类的成员变量、域类型、方法签名等信息”的能力。

通过调用Constructor、Method、Field实例上的方法,可以:构造底层类的实例、调用底层类的方法、访问底层类中的域。例如,Method.invoke 使你可以调用任何类的任何对象上的任何方法(遵从常规的安全限制);反射机制甚至能允许一个类使用另一个类,即使当前者被编译时后者还根本不存在(实例化)。

反射机制使用的代价

使用反射机制能力,是要付出一些代价的:

  • 丧失了编译时类型检查的好处。(如果程序企图使用反射访问不存在的或者不可访问的方法,运行时便会失败抛出异常,除非我们采取特别的预防措施)
  • 执行反射访问所需要的代码非常笨拙和冗长。(反射代码块的可阅读性是非常低的)
  • 性能损失。(反射方法调用比普通方法调用慢了许多,具体慢了多少,是受到多个因素的影响的,包括虚拟机被分配的内存/堆栈使用情况/当前CPU使用情况等等,书作者给了一个大概的说法:速度差异可能小到2倍,也可能大到50倍)

反射应用描述

下面举个例子:创建一个Set<String> 实例,它的类是由第一个命令行参数指定的,然后将剩余参数插入到集合里面。

代码语言:javascript
复制
public class ReflectClass {
  public static void main(String[] args) {
    Class<?> c1 = null;
    try {
      c1 = Class.forName(args[0]);
    } catch (ClassNotFoundException e) {
      System.err.println("Class not found.");
      System.exit(1);
    }

    Set<String> s = null;
    try {
      s = (Set<String>) c1.newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }

    s.addAll(Arrays.asList(args).subList(1, args.length));
    System.out.println(s);
  }
}

输出结果:

代码语言:javascript
复制
[1,2,3,45]

这个程序像一个“玩偶”,但它演示的这种方法时很强大的:

  • 通用的集合测试器,通过侵入式操作一个或多个集合实例,并检查是否遵守Set 接口的约定
  • 缺点一:反射的使用可能导致3个运行时错误,如果不使用反射方式的实例化,那么这3个错误都会成为编译时错误
  • 缺点二:根据类名生成它的实例,需要20行冗长的代码,而调用一个构造器可以非常间接地使用一行代码。一旦对象被实例化,它与其他的Set 实例就难以区分。

总结

反射机制是一种功能强大的机制,对于特定的复杂系统编程任务,它是非常必要的,但也有一些缺点。如果你编写的程序必须要与编译时未知的类一起工作,如有可能,就应该仅仅使用反射机制来实例化对象,而访问对象时则使用编译时已知的某个接口或超类。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后台技术汇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 反射机制使用的代价
  • 反射应用描述
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档