aaasdass【注】:装箱、拆箱的开销是Java泛型慢的重要原因。也成为今天Valhalla项目要重点解决的问题之一。
aa
aasdas②、运行期无法取到泛型类型信息。会让一些代码变得相当啰嗦。比如不支持对泛型进行实例判断、不支持使用泛型创建对象、不支持使用泛型创建数组,都是由于运行期Java虚拟机无法取得泛型类型而导致的。
aaasdass【注】:比如我们去写一个泛型版本的从List到数组的转换方法,由于不能从List中取得参数化类型T,所以不得不从一个额外参数中再传入一个数组的组件类型进去。
aasdsadasd
aasdsasdsadsadsadssadsaadasdsadadasdas不得不加入的类型参数
public static <T> T[] convert(List<T> list, Class<T> componentType) {
T[] array = (T[])Array.newInstance(componentType, list.size());
...
}
aa
aasdas③、通过擦除法来实现泛型,还丧失了一些面向对象思想应有的优雅,带来了一些模棱两可的模糊状况。
aasdsadasd
aasdsasdsadsadsadssadsaadasdsadadasdas当泛型遇见重载1
public class GenericTypes {
public static void method(List<String> list) {
System.out.println("invoke method(List<String> list)");
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
}
}
aasasaas分析:这段代码是不能被编译的,因为参数List< Integer>和List< String>编译之后都被擦除了,变成了同一种的裸类型List,类型擦除导致这两个方法的特征签名变得一模一样。初步看来,无法重载的原因已经找到了,但是真的就是如此吗?其实这个例子中泛型擦除成相同的裸类型只是无法重载的其中一部分原因。再看:
aasdsadasd
aasdsasdsadsadsadssadsaadasdsadadasdas当泛型遇见重载2
public class GenericTypes {
public static String method(List<String> list) {
System.out.println("invoke method(List<String> list)");
return "";
}
public static int method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
return 1;
}
public static void main(String[] args) {
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
}
执行结果:
invoke method(List<String> list)
invoke method(List<Integer> list)
aasasaas分析:这里的重载当然不是根据返回值来确定的,之所以这次能编译和执行成功,是因为两个method()方法加入了不同的返回值后才能共存在一个Class文件之中。 由于List< String>和List< Integer>擦除后是同一个类型,我们只能添加两个并不需要实际使用到的返回值才能完成重载,这是一种毫无优雅和美感可言的解决方案,并且存在一定语意上的混乱。
aaasdass【注】:前面的文章已经提到:方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名中,所以返回值不参与重载选择,但是在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存。也就是说两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是可以合法地共存于一个Class文件中的。
aaasdass总结:
aaaasdsasas①、由于Java泛型的引入,各种场景(虚拟机解析、反射等)下的方法调用都可能对原有的基础产生影响并带来新的需求,如在泛型类中如何获取传入的参数化类型等。所以《Java虚拟机规范》做出了相应的修改,引入了诸如Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题。
aaaasdsasasdas⒈Signature:存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。
aaaasdsasas②、从Signature属性的出现我们还可以得出结论:擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们在编码时能通过反射手段取得参数化类型的根本依据。 (很重要,后面会进一步分析,因为现在也不能很明白的表达。)
自动装箱、拆箱与遍历循环
aa
aaas就纯技术的角度而论,自动装箱、自动拆箱与遍历循环 这些语法糖,无论是实现复杂度上还是其中蕴含的思想上都不能与泛型相提并论,两者涉及的难度和深度都有很大差距。
aa
aaas我们看一段代码,其中包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数5种语法糖:
aasdsadasd
aasdsasdsadsadsadssadsaadasdsadadasdas编译前的代码
public static void main(String[] args) {
List<Integer> 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()
方法,
aasasasdasdaas⒊遍历循环:编译之后代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现Iterable接口的原因。
aasasasdasdaas⒋变长参数:它在调用的时候变成了一个数组类型的参数,在变长参数出现之前,程序员的确也就是使用数组来完成类似功能的。
aa
aasdsasdsadsadsadssadsaadasdsadadasdas自动封装带来的陷阱:
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.print(c == d ); true
System.out.print(e == f ); false //超出了范围[-128,127],所以封装的地址不一样了
System.out.print(c == (a + b) ); true //自动拆箱了,
System.out.print(c.equals(a + b) ); true
System.out.print(g == (a + b) ); true //“==”运算遇到了"+",自动拆箱
System.out.print(g.equals(a + b) ); false //equals()方法不处理数据转型的关系
}
执行结果:
true false true true true false
aasasaas分析:包装类的 “==”运算 在不遇到算术运算的情况下不会自动拆箱,以及它们equals()方法不处理数据转型的关系,在实际编码中尽量避免这样使用自动装箱与拆箱。
条件编译
aa
aaasC、C++中使用预处理器指示符(#ifdef)来完成条件编译。C、C++的预处理器最初的任务是解决编译时的代码依赖关系(如极为常用的#include预处理命令)。
aa
aaasJava语言实现条件编译,方法就是使用条件为常量的if语句,该代码中的if语句不同于其他Java代码,它在编译阶段就会被“运行”。
aasdsasdsadsadsadssadsaadasdsadadasdasJava语言的条件编译:
public static void main(String[] args) {
if (true) {
System.out.println("block 1");
} else {
System.out.println("block 2");
}
}
aasdsasdsadsadsadssadsasadadasdas该代码编译后Class文件的反编译结果:
public static void main(String[] args) {
System.out.println("block 1");
}
最后
权威指南-第一本Docker书
引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。
总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。
关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
-W63MTdK4-1630937025990)]
[外链图片转存中…(img-hpew3WZz-1630937025992)]
[外链图片转存中…(img-bO8iFREL-1630937025993)]
[外链图片转存中…(img-zM2ERgdN-1630937025995)]
关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!