丹丹丹学妹哭着对我说:学长,Java语法糖 你了解嘛(基于《深入理解Java虚拟机》之第10章前端编译与优化

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

list = ilist;

list = slist;

aas如何实现裸类型呢(2种方法)?

aa

aaasdass①、在运行期由Java虚拟机来自动地、真实地构造出ArrayList< Integer>这样的类型,并且自动实现从ArrayList< Integer>派生自ArrayList的继承关系来满足裸类型的定义;

aa

aaasdass②、简单粗暴地直接在编译时把ArrayList< Integer>还原回ArrayList,只在元素访问、修改时自动插入一些强制类型转换和检查指令,这样看起来也是能满足需要,这两个选择的最终结果大家已经都知道了(Java选择的实现方式)。

aa

aasdsasdsadsadsadsadasdsadadasdas泛型擦除前的例子:

public static void main(String[] args) {

Map<String, String> map = new HashMap<String, String>();

map.put(“hello”, “你好”);

map.put(“how are you?”, “吃了没?”);

System.out.println(map.get(“hello”));

System.out.println(map.get(“how are you?”));

}

aaasdass【注】:把这段Java代码编译成Class文件,然后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,程序又变回了Java泛型出现之前的写法,泛型类型都变回了裸类型。

aa

aasdsasdsadsadsadsadasdsadadasdas泛型擦除后的例子:

public static void main(String[] args) {

Map map = new HashMap();

map.put(“hello”, “你好”);

map.put(“how are you?”, “吃了没?”);

System.out.println((String) map.get(“hello”));

System.out.println((String) map.get(“how are you?”));

}

aas擦除式泛型的缺陷:

aa

aasdas①、使用擦除法实现泛型直接导致了 对原始类型数据的支持 又成了新的麻烦。

aasdsadasd

aasdsasdsadsadsadsadasdsadadasdas原始类型的泛型(目前的Java不支持)

ArrayList ilist = new ArrayList();

ArrayList llist = new ArrayList();

ArrayList list;

list = ilist;

list = llist;

aasasaas这种情况下,一旦把泛型信息擦除后,到了要插入强制转型代码的地方就没办法往下做了,因为不支持int、long与Object之间的强制转型。

aasasaas解决方法:当遇到原生类型(int 、long)时,对其进行装箱、拆箱,变成ArrayList< Integer>、ArrayList< Long>。

aaasdass【注】:装箱、拆箱的开销是Java泛型慢的重要原因。也成为今天Valhalla项目要重点解决的问题之一。

aa

aasdas②、运行期无法取到泛型类型信息。会让一些代码变得相当啰嗦。比如不支持对泛型进行实例判断、不支持使用泛型创建对象、不支持使用泛型创建数组,都是由于运行期Java虚拟机无法取得泛型类型而导致的。

aaasdass【注】:比如我们去写一个泛型版本的从List到数组的转换方法,由于不能从List中取得参数化类型T,所以不得不从一个额外参数中再传入一个数组的组件类型进去。

aasdsadasd

aasdsasdsadsadsadssadsaadasdsadadasdas不得不加入的类型参数

public static T[] convert(List list, Class componentType) {

T[] array = (T[])Array.newInstance(componentType, list.size());

}

aa

aasdas③、通过擦除法来实现泛型,还丧失了一些面向对象思想应有的优雅,带来了一些模棱两可的模糊状况。

aasdsadasd

aasdsasdsadsadsadssadsaadasdsadadasdas当泛型遇见重载1

public class GenericTypes {

public static void method(List list) {

System.out.println(“invoke method(List list)”);

}

public static void method(List list) {

System.out.println(“invoke method(List list)”);

}

}

aasasaas分析:这段代码是不能被编译的,因为参数List< Integer>和List< String>编译之后都被擦除了,变成了同一种的裸类型List,类型擦除导致这两个方法的特征签名变得一模一样。初步看来,无法重载的原因已经找到了,但是真的就是如此吗?其实这个例子中泛型擦除成相同的裸类型只是无法重载的其中一部分原因。再看:

aasdsadasd

aasdsasdsadsadsadssadsaadasdsadadasdas当泛型遇见重载2

public class GenericTypes {

public static String method(List list) {

System.out.println(“invoke method(List list)”);

return “”;

}

public static int method(List list) {

System.out.println(“invoke method(List list)”);

return 1;

}

public static void main(String[] args) {

method(new ArrayList());

method(new ArrayList());

}

}

执行结果:

invoke method(List list)

invoke method(List list)

aasasaas分析:这里的重载当然不是根据返回值来确定的,之所以这次能编译和执行成功,是因为两个method()方法加入了不同的返回值后才能共存在一个Class文件之中。 由于List< String>和List< Integer>擦除后是同一个类型,我们只能添加两个并不需要实际使用到的返回值才能完成重载,这是一种毫无优雅和美感可言的解决方案,并且存在一定语意上的混乱。

aaasdass【注】:前面的文章已经提到:方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名中,所以返回值不参与重载选择,但是在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存。也就是说两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是可以合法地共存于一个Class文件中的。

aaasdass总结:

aaaasdsasas①、由于Java泛型的引入,各种场景(虚拟机解析、反射等)下的方法调用都可能对原有的基础产生影响并带来新的需求,如在泛型类中如何获取传入的参数化类型等。所以《Java虚拟机规范》做出了相应的修改,引入了诸如SignatureLocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题

aaaasdsasasdas⒈Signature:存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。

aaaasdsasas②、从Signature属性的出现我们还可以得出结论:擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们在编码时能通过反射手段取得参数化类型的根本依据。 (很重要,后面会进一步分析,因为现在也不能很明白的表达。)


自动装箱、拆箱与遍历循环

aa

aaas就纯技术的角度而论,自动装箱、自动拆箱与遍历循环 这些语法糖,无论是实现复杂度上还是其中蕴含的思想上都不能与泛型相提并论,两者涉及的难度和深度都有很大差距。

aa

aaas我们看一段代码,其中包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数5种语法糖:

aasdsadasd

aasdsasdsadsadsadssadsaadasdsadadasdas编译前的代码

public static void main(String[] args) {

List list = Arrays.asList(1, 2, 3, 4);

int sum = 0;

for (int i : list) {

sum += i;

}

System.out.println(sum);

}

aasdsasdsadsadsadssadsaadasdsadadasdas编译后的代码

public static void main(String[] args) {

List list = Arrays.asList( new Integer[] { //泛型消除

Integer.valueOf(1),

Integer.valueOf(2),

Integer.valueOf(3),

Integer.valueOf(4) });

int sum = 0;

for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) {

int i = ((Integer)localIterator.next()).intValue();

sum += i;

}

System.out.println(sum);

}

aasasaas分析:

aasasasdasdaas⒈泛型:编译后泛型消除,List< Integer>变成了List 。

aasasasdasdaas⒉自动装箱、拆箱:编译之后被转化成了对应的包装和还原方法,如本例中的Integer.valueOf()intValue()方法,

紧跟潮流

大前端和全栈是以后前端的一个趋势,懂后端的前端,懂各端的前端更加具有竞争力,以后可以往这个方向靠拢。

这边整理了一个对标“阿里 50W”年薪企业高级前端工程师成长路线,由于图片太大仅展示一小部分

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
工程师成长路线,由于图片太大仅展示一小部分

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-XjG1VjIo-1713446961573)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值