当前位置: 首页 > news >正文

买了域名以后如何做网站西红门网站建设公司

买了域名以后如何做网站,西红门网站建设公司,做网站赚不到钱了,网站建设图片素材库作者 | 小志来源 | 程序员小灰导语笔者在最近的日常工作中#xff0c;因业务需要#xff0c;研究 Java 字节码层面的知识。具体是#xff0c;需要根据类字节码#xff0c;获取特定方法名的方法入参#xff0c;此方法名在源码中只有一个。但是在实际使用中发现#xff1a;… 作者 | 小志来源 | 程序员小灰导语笔者在最近的日常工作中因业务需要研究 Java 字节码层面的知识。具体是需要根据类字节码获取特定方法名的方法入参此方法名在源码中只有一个。但是在实际使用中发现在类实现泛型接口的情况下在字节码层面类却有两个同名方法导致无法确定哪个方法才是我们需要的方法。经过研究发现其中一个方法是编译器在编译的过程中自动生成的桥接方法bridge method两个方法可通过特定标识区分。注此处的桥接方法跟设计模式中的桥接模式不是一个概念。问题描述为了能够说明问题笔者模糊了实际业务场景的具体案例用一个稍微简单能够说明问题的示例来分析编译器自动生成的桥接方法bridge method。我们知道Java 泛型是JDK 5 中引入的一个新特性应用广泛。比如我们有一个操作算子泛型接口 OperatorT接口中有一个 process(T t) 方法其作用是对入参 T 进行逻辑处理。示例代码如下/*** author renzhiqiang* date 2022/2/20 18:30*/public interface OperatorT {/*** process method* param t*/void process(T t);}在实际业务场景中我们会有不同的操作算子实现OperatorT 接口进行业务逻辑处理。那么我们来创建一个具体的算子并实现OperatorT 接口重写 process(T t) 方法。如下/*** 用户信息算子* author renzhiqiang* date 2022/2/20 18:30*/public class UserInfoOperator implements OperatorString {Overridepublic void process(String s) {// do something}}其中泛型接口中的入参类型 T在实现类中替换成了实际需要的类型 java.lang.String。到这里我们就准备好了代码样例。那么我们的目标是什么呢就是要获取UserInfoOperator#process(String s) 方法的参数类型java.lang.String。读到这里读者可能会想这不很简单么通过反射根据Class#getDeclaredMethods()获取到 UserInfoOperator 的所有方法再找到方法名是 process 的方法然后再获取到参数列表不就可以获取参数类型java.lang.String 了么。如果正在阅读文章的你也这么想的话那请继续往下看。根据 Java 反射方法Class#getDeclaredMethods() 的描述Returns an array of Method objectsincluding public, protected, default (package) access, and private methods, butexcludes inherited methods.翻译过来就是返回方法对象数组包括公共方法、受保护方法、默认(包)访问方法和私有方法但不包括继承方法。根据我们的示例如果我们通过反射利用Class#getDeclaredMethods() 方法我们预期的返回方法数组中应该只有一个方法名是process 才对但是这里却有两个 process 方法。惊不惊奇意不意外图 debug 发现 UserInfoOperator 类的两个 process 方法产生原因编译器生成 bridge 方法我们知道Java 源码需要经过编译器编译生成对应的 .class 文件才能给 JVM 使用。在源码中我们只定义了一个名为 process 的方法。那么我们考虑编译器在编译源码的过程中是否会进行一些特的处理。为了更加直观的查看编译后的字节码文件在 Idea 安装 jclasslib 插件通过 jclasslib 查看 UserInfoOperator 和 OperatorT 的字节码。如下图 jclasslib 查看 UserInfoOperator 类的字节码第一个 process 方法图 jclasslib 查看 UserInfoOperator 类的字节码 第二个 process 方法图 jclasslib 查看 OperatorT 类的字节码通过 jclasslib 查看 .class 文件发现在 UserInfoOperator 类中确实存在两个 process 方法其中一个方法入参是 java.lang.String另一个方法的入参是 java.lang.Object。而在 Operator 字节码中只有一个 process 方法方法的入参是 java.lang.Object。同时我们注意到在 UserInfoOperator 类的字节码中 [访问标志]项其中一个方法的访问标志是 [public synthetic bridge]。其中 public 很好理解但是其中的 [synthetic bridge] 是怎么来的呢查阅相关资料后发现标识符 synthetic 表示此方法是否是由编译器自动产生的标识符 bridge表示此方法是否是由编译器产生的桥接方法。图 方法访问标志来源深入理解 Java 虚拟机第三版到此可以确定的是其中一个process 方法是编译器自动产生的桥接方法。那么为什么编译器会产生桥接方法呢以及在什么情况下会产生桥接方法以及如何判断一个方法是不是桥接方法我们继续往下分析。为何生成 bridge 方法正确编译在源码中Operator 类的 process 方法的参数定义是 process(T t)参数类型是 T。而在字节码层面我们看到process 方法在编译之后编译器将入参类型变成了 java.lang.Object。伪代码示意大概是这样public interface OperatorObject {/*** 方法参数变成 Object 类型* param object*/void process(Object object);}想象一下如果没有编译器自动生成的桥接方法那么在编译层面是不能通过的因为接口 OperatorT 中的 process 方法,经过编译之后参数类型变成了 java.lang.Object 类型而实现类 UserInfoOperator 中的 process 方法的参数是 java.lang.String 类型两者的方法参数不一致导致UserInfoOperator 并没有重写接口中的 process 方法因此编译无法通过。这种情况下编译器自动生成一个桥接方法 void process(Object obj) 方法则可以编译通过似乎是理所当然的事情。自动生成的 process方法方法签名为void process(Object object)。伪代码示意大概是这样// 自动生成的process 方法public void process(Object object) {process((String) object);}类型擦除我们知道Java 中的泛型在编译期间会将泛型信息擦除。如代码定义 ListString 和 ListInteger编译之后都会变成 List。我们再考虑一种常见的情形Java 类库中比较器的用法。我们自定义比较器的时候可以通过实现 Comparator 接口实现比较逻辑。示例代码如下public class MyComparator implements ComparatorInteger {public int compare(Integer a,Integer b) {// 比较逻辑 }}这种情况下编译器同样会产生一个桥接方法。方法签名为 intcompare(Object a, Object b) 。图 MyComparator 类的两个 compare 方法伪代码示意大概是这样public class MyComparator implements ComparatorInteger {public int compare(Integer a,Integer b) {// 比较逻辑}// 桥接方法 (bridge method)public int compare(Object a,Object b) {return compare((Integer)a,(Integer)b);}}因此当我们使用如下方式进行比较的时候能够通过编译并得到我们预期的结果Object a 5;Object b 6;Comparator rawComp new MyComparator();// 可以通过编译因为自动生成了桥接方法compare(Object a, Object b)int comp rawComp.compare(a, b);另外我们知道泛型编译之后类型信息会被擦除。如果我们有这样一个比较方法// 比较方法public T T max(ListT list, ComparatorT comparator){T biggestSoFar list.get(0);for ( T t : list ) {if (comparator.compare(t,biggestSoFar) 0) {biggestSoFar t;}}return biggestSoFar;}编译之后泛型被擦除掉伪代码表示大概是这样public Object max(List list, Comparator comparator) {Object biggestSoFar list.get(0);for ( Object t : list ) {if (comparator.compare(t,biggestSoFar) 0) { //比较逻辑biggestSoFar t;}}return biggestSoFar;}我们将 MyComparator 其中一个参数传入 max() 方法。如果没有桥接方法的话那么第四行的比较逻辑将无法正确编译因为MyComparator 类中没有两个参数是 Object 类型的比较方法只有参数类型是 Integer 类型的比较方法。读者可自行测试。解决方案通过以上的案例描述我们知道在实现泛型接口的场景下编译器会自动生成桥接方法保证编译能够通过。那么在这种情况下我们只要识别哪一个是桥接方法哪一个不是桥接方法就可以解决我们一开始的问题。很自然的既然编译器自动产生了一个桥接方法那么应该会有某种方式可以让我们判断一个方法是否是桥接方法。果然我们继续研究发现Method 类中提供了 Method#isBridge() 方法。查看源码中对方法的描述Method#isBridge()Returns true if this method is a bridge method;returns false otherwise。到此我们通过反射获取到 UserInfoOperator 类中的两个process 方法再调用 Method#isBridge() 方法即可锁定需要的方法因而进一步获取方法参数 java.lang.String。深入分析至此可以说就业务需求来说我们完美的找到了解决方案。但在此之后不禁会想除了上述示例还有哪些情况下编译器也会自动生成桥接方法呢我们继续深入研究。类继承通过查阅相关资料我们考虑如下一种情况/*** 如下会产生桥接方法吗* author renzhiqiang* date 2022/2/20 18:33*/public class BridgeMethodSample {static class A {public void foo() {}}public static class C extends A{}public static class D extends A{Overridepublic void foo() {}}}上述代码示例中我们定义了三个静态内部类A C D其中 C D 分别继承 A。经过编译通过jclasslib 查看 BridgeMethodSample 字节码我们也发现类 C 中编译器为其生成了桥接方法 void foo()而类 D 中却没有。图 类C 生成桥接方法图 类D 没有生成桥接方法深入分析并根据上述分析的经验我们猜测编译器生成桥接方法一定是在某种情况下需要一个方法来满足 Java 编程规范或者需要保证程序运行的正确性。通过字节码可以看出类 A 没有 public 修饰包范围以外的程序是没有访问类 A 的权限的更不用说类 A 中的方法。但是类 C 是有public 修饰C 类中的方法包括继承来的方法是可以被包外的程序访问的。因此编译器需要生成一个桥接方法以保证能够访问 foo() 方法满足程序的正确运行。但是类 D 同样继承 A却没有生成桥接方法根本原因是类 D 中重写了父类 A 中的 foo() 方法即没有必要生成桥接方法。方法重写我们再看一种情况方法重写。Java 中方法重写Override是子类对父类的允许访问的方法的实现过程进行重新编写的过程。重写需要满足一定的规则1. The method must have the same name as in the parentclass.2. The method must have the same parameter as in theparent class.3. There must be an IS-A relationship (inheritance).JDK 5 之后重写方法的返回类型可以与父类方法返回类型相同也可以不相同但必须是父类方法返回类型的子类。我们考虑如下代码示例// 定义一个父类包含一个 test() 方法public class Father {public Object test(String s) {return s;}}// 定义一个子类继承父类public class Child extends Father {Overridepublic String test(String s) {return s;}}以上在 Child 子类中我们重写了 test() 方法但是返回值的类型我们将 java.lang.Object 改变为它的子类 java.lang.String。编译之后我们同样使用 jclasslib 插件查看两个类的字节码如下所示图 Child 类字节码test() 方法1图 Child 类字节码test() 方法2图 Father类字节码test() 方法根据上图我们发现Child 类中我们重写了 test() 方法但是在字节码层面发现有两个 test() 方法其中一个方法的访问标志为 [public synthetic bridge] 表示这个方法是编译器为我们生成的。而当我们不改变 Child#test() 方法的返回类型时编译器并没有为我们生成桥接方法读者可自行试验。也就是说在子类方法重写父类方法返回类型不一致的情况下编译器也为我们生成了桥接方法。以上笔者罗列了几种编译器为我们自动生成桥接方法的情况。那么是否还有其他场景下编译器也会生成桥接方法呢如果您也曾研究过或者使用过 bridge 方法欢迎交流讨论。同时给出一个 bridge 方法的非官方定义希望能够给读者一些启发Bridge Method: These are methods that create an intermediate layerbetween the source and the target functions. It is usually used as part of thetype erasure process. It means that the bridge method is required as a typesafe interface.限于笔者水平有限难免有理解不准确、不到位的地方。欢迎交流讨论参考链接https://stackoverflow.com/questions/5007357/java-generics-bridge-methodhttps://stackoverflow.com/questions/14144888/find-generic-method-with-actual-types-from-getdeclaredmethodshttps://www.geeksforgeeks.org/method-class-isbridge-method-in-java/往期推荐为什么大家都在抵制用定时任务实现「关闭超时订单」功能如果被问到分布式锁应该怎样回答别再用 Redis List 实现消息队列了Stream 专为队列而生OpenStack 如何跨版本升级点分享点收藏点点赞点在看
http://www.huolong8.cn/news/34912/

相关文章:

  • 海原县城乡建设局网站网络管理系统的功能
  • 做网站公司选智投未来兰山网站建设
  • 百竞网站建设企业网站建设合同应注意什么
  • 怎样策划一个营销型网站能浏览外国网页的浏览器
  • 网站权重查询手表网站推荐
  • wordpress amp 8211整站优化方案
  • 电脑网站怎么创建到桌面上部队网站制作
  • 运城做网站公司搜索引擎
  • 工程建设管理网站建网站找哪家好
  • 中文 域名的网站用htlm做静态网站怎么用
  • 软件园做网站wordpress 嵌入pdf
  • 济南烨铭网站建设wordpress栏目设置
  • 仿银行网站 asp哈尔滨做网站哪家好强
  • 在建工程十堰优化网站哪家好
  • 离石做网站网络营销是什么专业学的
  • 举报网站建设公司哪里有网络推广
  • wordpress做网站宝安网
  • 网站推广分析网站开发必须要用js
  • 深圳网站设计 商城上海建站哪家好
  • 邢台网站建设电话营销型电子商务网站
  • php精品网站建设wordpress调用置顶分类
  • 848给我做一下88网站正规小说分销平台
  • 网站怎样做百度推广郑州seo顾问热狗网
  • 如何做网校网站seo网络推广软件
  • 济南市做网站公司烟台酒店网站建设
  • 做响应式网站的菜单栏做哪个视频网站赚钱的
  • 简单的电商网站开发老牛wordpress
  • 网站邮件发送功能怎么做20亿做网站
  • 出境旅游哪个网站做的好想做一个自己的网站 怎么做
  • 校园网站建设方案模板ui设计和网站开发