一、包装类
在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型
1.1 基本数据类型和对应的包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
除了 Integer 和 Character ,其余基本类型的包装类都是首字母大写
1.2 装箱(装包)和拆箱(拆包)
1.2.1 概念:
把 基本数据类型 变为 包装类类型 的过程叫做 装箱
把 包装类类型 变为 基本数据类型 的过程叫做 拆箱
tip:快速打开代码存储位置
out目录下有class文件,在地址栏输入cmd,打开cmd窗口,输入javap -c,查看源码
1.2.2 装箱:
通过源码可以发现,显示装箱和自动装箱都是调用了valueOf方法
1.2.3 拆箱:
1.2.4 面试题:
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b);
Integer c = 200;
Integer d = 200;
System.out.println(c == d);
}
运行结果:
解析:
该段代码涉及到了装箱,我们查看 valueOf 方法的源码
通过查看源码,得出初步结论:i 应该在一个范围内时,返回某个数组中的值,不在这个范围时,返回新对象,新对象用 == 进行比较,比较的是地址,肯定不一样了
继续查看源码,发现low = -128,high = 127,共256个数字
构成一个256个元素的缓存数组
所以代码中当元素范围为-128~127时,是传值比较,当超出这个范围,就是创建新对象,传址比较了
二、泛型
泛型是JDK1.5引入的新的语法
通俗讲:泛型就是适用于许多类型
代码上讲:就是对类型实现了参数化
2.1 示例:
实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值
思路:所有类的父类默认为Object,所以创建一个Object数组符合需求
代码:
解析:每次获得数组元素都需要强制类型转换
上面的代码太乱了,什么类型都能存放,要用的时候,都需要强转,而更过的情况下,我们只希望他能够只持有一种数据类型,而不是同时持有多种类型,因此就引入了泛型
泛型的主要目的:指定当前容器要持有什么类型的对象,让编译器去做检查,此时就需要把类型作为参数传递,需要什么类型,传入什么类型
2.2 泛型语法
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分类型参数
}
引入泛型,上述示例代码可改为:
class MyArray<E> {
public Object[] array = new Object[10];
public void setVal(int pos,E val) {
this.array[pos] = val;
}
public E getVal(int pos) {
return (E)this.array[pos];
}
}
public class Test {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();
myArray.setVal(0,10);
myArray.setVal(1,20);
//myArray.setVal(2,"hello");//编译器会自动类型检查
Integer a = myArray.getVal(1);//自动类型转换
System.out.println("======");
MyArray<String> myArray1 = new MyArray<>();
myArray1.setVal(0,"hello");
myArray1.setVal(2,"abc");
}
}
解析:
类名后的<E>代表占位符,表示当前类是一个泛型类
2.3 泛型类使用的注意事项:
2.4 泛型的擦除机制
在编译的过程中,将所有的 E 替换为 Object ,这种机制称为擦除机制
Java的泛型机制是在编译级别实现的,编译器生成的字节码在运行期间并不包含泛型的类型信息
2.5 泛型的上界
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束
2.5.1 语法:
class 泛型类名称<类型形参 extends 类型边界> {
...
}
2.5.2 示例:
public class MyArray<E extends Number> {
...
}
只接收 Number 的子类型作为 E 的类型实参
MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型
没有指定类型边界的 E ,可视为 E extends Object
2.5.3 复杂示例:
public class MyArray<E extends Comparable<E>> {
...
}
E 必须是实现了Comparable 接口的