君子以自强不息。
泛型与类型擦除
泛型是 JDK 1.5 的一项新增特性,它的本质是参数化类型的应用。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
在 Java 没有出现泛型之前只能通过 Object 是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化。
泛型技术在 C# 和 Java 之中的使用方式看似相同,但实现上却有着根本性的分歧:
C# 泛型
C# 泛型无论在程序源码中、编译后的 IL 中,或是运行期的 CLR 中,都是切实存在的,
List
Java 泛型
Java 泛型只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型了,并且在相应的地方插入了强制转型代码。
因此,对于运行期的 Java 语言来说,ArrayList
泛型擦除示例
把 Java 代码编译成 class 文件,然后再通过字节码反编译工具进行反编译后, 将会发现泛型都不见了,程序又变回了Java泛型出现之前的写法,泛型类型都变回了原生类型。
泛型擦除前:
1 |
|
泛型擦除后:
1 |
|
泛型与重载
下列代码是否可以编译通过?
1 |
|
答案是:不能。
因为参数 List
自动装箱、拆箱与遍历循环
自动装箱、拆箱与遍历循环编译之前:
1 |
|
自动装箱、拆箱与遍历循环编译之后:
1 |
|
自动装箱、拆箱在编译之后被转化成了对应的包装和还原方法,如上面的 Integer.valueOf() 与 Integer.intValue() 方法。
遍历循环则把代码还原成了迭代器的实现,这也是为何遍历循环需要被遍历的类实现 Iterable 接口的原因。
测试
1 |
|
==符号: 如果是基本数据类型,则直接对值进行比较,如果是引用数据类型,则是对他们的地址进行比较(但是只能比较相同类型的对象,或者比较父类对象和子类对象。类型不同的两个对象不能使用==)。
equals方法: 继承自Object类,在具体实现时可以覆盖父类中的实现。它的实现也是对对象的地址进行比较,此时它和”==”的作用相同。JDK 类中有一些类覆盖了 Object 类的 equals() 方法,比较规则为:如果两个对象的类型一致,并且内容一致,则返回 true,这些类有: java.io.file,java.util.Date,java.lang.string,包装类(Integer, Double 等)。
包装类的“==”运算在不遇到算术运算的情况下不会自动拆箱,以及它们 equals() 方法不处理数据转型的关系。
(注:Integer 使用一个内部静态类中的一个静态数组保存了 -128~127 范围内的数据,静态数组在类加载以后是存在方法区的,并不是什么常量池。在自动装箱的时候,首先判断要装箱的数字的范围,如果在 -128~127 的范围则直接返回缓存中已有的对象,否则 new 一个新的对象。)