目录
一. 包装类
1.1 基本数据类型和对应的包装类
1.2 装箱和拆箱
二. 泛型
2.1什么是泛型
2.2泛型的引入
2.3 泛型类语法
2.4 泛型类的使用
2.5 裸类型(Raw Type)(了解)
2.6 泛型是如何编译的
2.7 泛型的上界
2.8 泛型方法
包装类在之前我们就使用过, 使我们写代码更加的方便
装箱(装包): 把一个基本数据类型转变为包装类型
拆箱(拆包): 把一个包装类型转变为基本数据类型
int i = 10 ;Integer ii = i ; // 自动装箱Integer ii = Integer . valueOf ( i ); //显示装箱 ---类名.valueOf 说明该方法时用static修饰的Integer jj = new Integer(10) ;int j = jj ; // 自动拆箱int j = jj . intValue (); //显示拆箱
注:拆箱时, 我们可以将数据拆成我们想要的类型
Integer jj = new Integer(10);
double d = jj.doubleValue();//拆成double类型
System.out.println(d);
//输出结果
10.0
下面看一道面试题:
思考 : 为什么上述代码只是将100改成200, 输出的结果却不同呢?
上述我们唯一做的动作就是装箱, 那么装箱时, 调用的方法是Integer.valueOf(), 按住Ctrl点进去查看valueOf的源码, 我们发现:
我们传入的参数, 如果在一个low和high之间, 那么就返回一个数组某下标的值, 如果不在这个范围内, 那么就new一个对象, 此时即使传入一样的参数, 返回的值肯定是不同的. 那么我们猜测, 200就不在low和high之间, 而100在low和high之间.
那么我们点进去low和high的源码, 发现low = -128 , high = 127, 我们的猜测是正确的, 那么cache数组是怎么回事呢?
我们计算一下这个数组:
一般的类和方法,只能使用具体的类型 : 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。----- 来源《 Java 编程思想》对泛型的介绍。
代码示例:
① MyArray
了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有:E 表示 ElementK 表示 KeyV 表示 ValueN 表示 NumberT 表示 TypeS, U, V 等等 - 第二、第三、第四个类型
②T[] array = (T[])new Object[10] ---- 创建泛型类数组
T[] array = (T[])new Object[10], 并不是一个最好的写法, 最好的写法是:
Object[] array = new Object[10];
不能new泛型类型的数组T [] ts = new T [ 5 ]; // 是不对的
③ MyArray
④ myArray.setVal(2,"bit") ----- 代码编译报错,此时因为在③处指定类当前的类型,此时编译器会在存放元素的时候帮助我们进行类型检查。
泛型, 是编译时期的机制, 在运行时, 没有泛型的概念
注:<>中的内容不能是基本数据类型, 只能是引用类型/包装类
想要存放String类型的数据:
class 泛型类名称 < 类型形参列表 > {// 这里可以使用类型参数}class ClassName < T1 , T2 , ..., Tn > {}class 泛型类名称 < 类型形参列表 > extends 继承类 /* 这里可以使用类型参数 */ {// 这里可以使用类型参数}class ClassName < T1 , T2 , ..., Tn > extends ParentClass < T1 > {// 可以只使用部分类型参数}
泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象MyArray < Integer > list = new MyArray < Integer > ();//当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写MyArray < Integer > list = new MyArray <> (); // 可以推导出实例化需要的类型实参为 Integer
裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型
MyArray list = new MyArray();
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
语法:
class 泛型类名称 < 类型形参 extends 类型边界 > {...}
例:
public class MyArray < E extends Number > {...}
表示: 只接受 Number 或 Number的子类型 作为 E 的类型实参
MyArray < Integer > l1 ; // 正常,因为 Integer 是 Number 的子类型MyArray < String > l2 ; // 编译错误,因为 String 不是 Number 的子类型
了解:
1. 没有指定类型边界 E,可以视为 E extends Object
2. 泛型没有下界
例: 写一个泛型类, 求一个数组中的最大值
显然这样是错误的, 因为擦除机制将T替换成Object类, 而引用类型是不能直接比较大小的, 需要使用compareTo方法, 并实现Comparable接口, 我们点进去Object的源码看发现:
Object类并没有实现Comparable接口
正确的写法应该是:
意味着, 传入的参数必须是继承了Comparable接口的类, 那么Integer我们点进去查看:
如果我们重新定义一个类当参数:
是不被允许的, 因为没有Person没有实现Comparable接口
正确写法:
语法:
方法限定符 < 类型形参列表 > 返回值类型 方法名称 ( 形参列表 ) { ... }
上述代码可以写成 泛型方法:
当这个泛型方法是静态的时, 我们就不用实例化对象, 直接通过类来调用泛型方法