美文网首页java开发Java
拼夕夕三轮面经:被问到反射的bug,你中招了吗?

拼夕夕三轮面经:被问到反射的bug,你中招了吗?

作者: 熬夜不加班 | 来源:发表于2021-07-02 15:49 被阅读0次

拼夕夕三轮面经:被问到反射的bug,你中招了吗?

看到题目,很多初级开发同学就慌了,我天天 crud,何时用过反射呀?而且它居然还有 bug 的吗?

其实不然,反射在我们日常开发中一直陪伴着我们,如果我们一点不重视反射的使用,就会不自觉地写出很多无法理解的 bug。今天我们就来看看增删改查是如何遇到反射bug的!

1 当反射遇见重载

重载level方法,入参分别是intInteger

image.png

若不使用反射,选用哪个重载方法很清晰,比如:

  • 传入666就走int参数重载
  • 传入Integer.valueOf(“666”)走Integer重载

那反射调用方法也是根据入参类型确定使用哪个重载方法吗? 使用getDeclaredMethod获取 grade方法,然后传入Integer.valueOf(“36”)

image.png

结果是:

image.png

因为反射进行方法调用是通过

方法签名

来确定方法。本例的getDeclaredMethod传入的参数类型Integer.TYPE其实代表int。

image.png

所以不管传包装类型还是基本类型,最终都是调用int入参重载方法。

将Integer.TYPE改为Integer.class,则实际执行的参数类型就是Integer了。且无论传包装类型还是基本类型,最终都调用Integer入参重载方法。

综上,反射调用方法,是以反射获取方法时传入的方法名和参数类型来确定调用的方法。

2 泛型的类型擦除

泛型允许SE使用类型参数替代精确类型,实例化时再指明具体类型。利于代码复用,将一套代码应用到多种数据类型。

泛型的类型检测,可以在编译时检查很多泛型编码错误。但由于历史兼容性而妥协的泛型类型擦除方案,在运行时还有很多坑。

案例

现在期望在类的字段内容变动时记录日志,于是SE想到定义一个泛型父类,并在父类中定义一个统一的日志记录方法,子类可继承该方法。上线后总有日志重复记录。

  • 父类
image.png
  • 子类1
image.png
  • 通过反射调用子类方法:
image.png

虽Base.value正确设置为了JavaEdge,但父类setValue调用了两次,计数器显示2


image.png

两次调用Base.setValue,是因为getMethods找到了两个setValue:

image.png image.png

子类重写父类方法失败原因

  • 子类未指定String泛型参数,父类的泛型方法setValue(T value)泛型擦除后是setValue(Object value),于是子类入参String的setValue被当作新方法
  • 子类的setValue未加@Override注解,编译器未能检测到重写失败
image.png

有的同学会认为是因为反射API使用错误导致而非重写失败:

  • getMethods

得到当前类和父类的所有public方法

  • getDeclaredMethods

获得当前类所有的public、protected、package和private方法

于是用getDeclaredMethods替换getMethods:

image.png image.png

虽然这样做可以规避重复记录日志,但未解决子类重写父类方法失败的问题

  • 使用Sub1时还是会发现有两个setValue
image.png

于是,终于明白还得重新实现Sub2,继承Base时将String作为泛型T类型,并使用 @Override 注解 setValue

image.png
  • 但还是出现重复日志
image.png

Sub2的setValue竟然调用了两次,难道是JDK反射有Bug!getDeclaredMethods查找到的方法肯定来自Sub2;而且Sub2看起来也就一个setValue,怎么会重复?

调试发现,Child2类其实有俩setValue:入参分别是String、Object。


image.png

这就是因为泛型类型擦除

反射下的泛型擦除“天坑”

Java泛型类型在编译后被擦除为Object。子类虽指定父类泛型T类型是String,但编译后T会被擦除成为Object,所以父类setValue入参是Object,value也是Object。 若Sub2.setValue想重写父类,那入参也须为Object。所以,编译器会为我们生成一个桥接方法。 Sub2类的class字节码:

➜  genericandinheritance git:(master) ✗ javap -c Sub2.class
Compiled from "GenericAndInheritanceApplication.java"
class com.javaedge.oop.genericandinheritance.Sub2 extends com.javaedge.oop.genericandinheritance.Base<java.lang.String> {
  com.javaedge.oop.genericandinheritance.Sub2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method com/javaedge/oop/genericandinheritance/Base."<init>":()V
       4: return

  public void setValue(java.lang.String);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String call Sub2.setValue
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_0
       9: aload_1
      10: invokespecial #5                  // Method com/javaedge/oop/genericandinheritance/Base.setValue:(Ljava/lang/Object;)V
      13: return

  public void setValue(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #6                  // class java/lang/String
       // 入参为Object的setValue在内部调用了入参为String的setValue方法
       5: invokevirtual #7                  // Method setValue:(Ljava/lang/String;)V
       8: return
}

若编译器未帮我们实现该桥接方法,则Sub2重写的是父类泛型类型擦除后、入参是Object的setValue。这两个方法的参数,一个String一个Object,显然不符Java重写。

入参为Object的桥接方法上标记了public synthetic bridge:

  • synthetic代表由编译器生成的不可见代码
  • bridge代表这是泛型类型擦除后生成的桥接代码
image.png

修正

知道了桥接方法的存在,现在就该知道如何修正代码了。

  • 通过getDeclaredMethods获取所有方法后,还得加上非isBridge这个过滤条件:
image.png
  • 结果
image.png

相关文章

  • 拼夕夕三轮面经:被问到反射的bug,你中招了吗?

    拼夕夕三轮面经:被问到反射的bug,你中招了吗? 看到题目,很多初级开发同学就慌了,我天天 crud,何时用过反射...

  • 今天你拼夕夕了吗?

    今天你拼多多了吗?哈哈,自己还真是不长心,又一次入了坑,紧张的邀请别人了两天,最后还是输在了那0.06%上,看着那...

  • 为何大厂总能掀起一阵风浪?

    王太虚,拼夕夕的前员工,于昨日发布一则自己为何被开除的视频。不出所料,拼夕夕再次被送上热搜榜。前有“守卫边疆”女孩...

  • 《拼夕夕》:见过的最烂营销

    只要打开手机,满眼都是拼夕夕拙劣的广告推销,不是让人喷饭,而是让人恶心至极。 好多次,想关闭拼夕夕的广告,结果被强...

  • 七旬老汉与霸道总裁

    文/未杨花名 七旬老汉与霸道总裁 今年的并夕夕,我笑了 请珍惜身边那个愿意为你下载拼多多的人 今年的拼夕夕链接真的...

  • 你敢在拼夕夕买手机吗

    我在拼夕夕买了两部手机,分别是华为P20pro和华为 mate20,主要是图便宜,经多方比较,夕夕比其他平台便宜5...

  • 有哪些东西你知道很便宜,但不知道居然那么便宜?

    拼?拼夕夕?? 我能想到的就是拼多多了~ 我以前一直以为拼夕夕本身已经够便宜了,但是没想到还能更便宜,便宜的就像白...

  • 卸载了拼夕夕

    最开始接触拼夕夕还是朋友发的链接,让帮忙砍一刀、拼个团。还是很新奇的,后来老妈也玩上了拼夕夕,在拼多多里面种果树,...

  • 拼夕夕

    许久不曾联系的高中同学发消息给我,我以为是什么事情,点开一看就是一串乱码,就是拼夕夕的快捷链接,发了两遍,一看就是...

  • 拼夕夕

    同事发过来个链接:请拼一刀。 我有点无奈,但是不好意思拒绝。 这种东西十有八九是不成功的,可是还总是有人乐此不疲。...

网友评论

    本文标题:拼夕夕三轮面经:被问到反射的bug,你中招了吗?

    本文链接:https://www.haomeiwen.com/subject/gxzeultx.html