aas 几乎所有的编程语言都或多或少提供过一些语法糖来方便程序员的代码开发,这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高效率,或能提升语法的严谨性,或能减少编码出错的机会。现在也有一种观点认为语法糖并不一定都是有益的,大量添加和使用含糖的语法,容易让程序员产生依赖,无法看清语法糖的糖衣背后,程序代码的真实面目。

asdsadasdasdasdsadasdasdasdsadassdasdsasdsadsdasdasdsadasdasdsadasdsadassadasdas**————《Java虚拟机规范》**


Java语法糖

aa

泛型:

aas

aas泛型的本质是参数化类型或者参数化多态的应用,即可以将操作的数据类型指定为方法签名中的一种特殊参数,这种参数类型能够用在类、接口和方法的创建中,分别构成泛型类、泛型接口和泛型方法。泛型让程序员能够针对泛化的数据类型编写相同的算法,这极大地增强了编程语言的类型系统及抽象能力。

aas[注]:Java和C#两门语言各自添加了泛型的语法特性,但实现方式却截然不同,其实Java的泛型直到今天依然作为Java语言不如C#语言好用。

aa

aasC#实现的泛型

aa

aaasdass实现方式:“具现化式泛型”,C#里面泛型无论在程序源码里面、编译后的中间语言表示(这时候泛型是一个占位符)里面,抑或是运行期的CLR里面都是切实存在的。

aaasdsads[注]:List< int>与List< string>就是两个不同的类型,它们由系统在运行期生成,有着自己独立的虚方法表和类型数据。

aa

aasJava实现的泛型

aa

aaasdass实现方式:“类型擦除式泛型”,它只在程序源码中存在,在编译后的字节码文件中,全部泛型都被替换为原来的裸类型。并且在相应的地方插入了强制转型代码。

aaasdsads[注]:对于Java语言来说,ArrayList< int>与ArrayList< String>其实是同一个类型。

aas比较:

aa

aaasdass①、Java中不支持对泛型进行实例判断、不支持使用泛型创建对象、不支持使用泛型创建数组。

aa

aaasdass②、C#2.0引入了泛型之后,带来的显著优势之一便是对比起Java在执行性能上的提高,因为在使用平台提供的容器类型(如List< T>,Dictionary<TKey,TValue>)时,无须像Java里那样不厌其烦地拆箱和装箱,如果在Java中要避免这种损失,就必须构造一个与数据类型相关的容器类(譬如IntFloatHashMap这样的容器)。显然,这除了引入更多代码造成复杂度提高、复用性降低之外,更是丧失了泛型本身的存在价值。

aa

aaasdass③、Java中擦除式泛型的实现几乎只需要在Javac编译器上做出改进即可,不需要改动字节码、不需要改动Java虚拟机,也保证了以前没有使用泛型的库可以直接运行在Java 5.0之上。

aa

aas类型擦除:

aa

aaasdass为了保证编译出来的Class文件可以在Java 5.0引入泛型之后继续运行,有两种方式:

aaasdaasdass①、将已有的类型泛型化(Java选择)

aaasdaasdass②、平行地加一套泛型化版本的新类型(C#选择)

aaasdass要让所有需要泛型化的已有类型,譬如ArrayList,原地泛型化后变成了ArrayList< T>,而且保证以前直接用ArrayList的代码在泛型新版本里必须还能继续用这同一个容器,这就必须让所有泛型化的实例类型,譬如ArrayList< Integer>、ArrayList< String>这些全部自动成为ArrayList的子类型才能可以,否则类型转换就是不安全的。由此就引出了“裸类型”的概念,裸类型应被视为所有该类型泛型化实例的共同父类型 。 只有这样,我们才能实现如下转型:

ArrayList ilist = new ArrayList();

ArrayList slist = new ArrayList();

ArrayList list; // 裸类型

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编译后的代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

img

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

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

前端面试题汇总


前端面试题是我面试过程中遇到的面试题,每一次面试后我都会复盘总结。我做了一个整理,并且在技术博客找到了专业的解答,大家可以参考下:

由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~

感悟

https://i-blog.csdnimg.cn/blog_migrate/f46e8e11054b068a2e2ed047e5b218a4.png)

由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~

感悟

春招面试的后期,运气和实力都很重要,自己也是运气比较好,为了回馈粉丝朋友们(毕竟自己也玩了这么久哈哈哈),整理个人感悟和总结以上。最后祝愿大家能够收获理想offer!!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐