目录
一. 包装类
1.1 基本数据类型和对应的包装类
1.2 装箱和拆箱
二. 泛型
2.1什么是泛型
2.2泛型的引入
2.3 泛型类语法
2.4 泛型类的使用
2.5 裸类型(Raw Type)(了解)
2.6 泛型是如何编译的
2.7 泛型的上界
2.8 泛型方法
一. 包装类
1.1 基本数据类型和对应的包装类
包装类在之前我们就使用过, 使我们写代码更加的方便
1.2 装箱和拆箱
装箱(装包): 把一个基本数据类型转变为包装类型
拆箱(拆包): 把一个包装类型转变为基本数据类型
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数组是怎么回事呢?
我们计算一下这个数组:
二. 泛型
2.1什么是泛型
一般的类和方法,只能使用具体的类型 : 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。----- 来源《 Java 编程思想》对泛型的介绍。
2.2泛型的引入
代码示例:
① MyArray<T> ----- 类名后的 <T> 代表占位符,表示当前类是一个泛型类
了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有: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<Integer> myArray = new MyArray<>() ----- 创建泛型类对象, 类型后加入 <Integer> 指定当前类型 后面的<>中的内容可以省略
④ myArray.setVal(2,"bit") ----- 代码编译报错,此时因为在③处指定类当前的类型,此时编译器会在存放元素的时候帮助我们进行类型检查。
泛型, 是编译时期的机制, 在运行时, 没有泛型的概念
注:<>中的内容不能是基本数据类型, 只能是引用类型/包装类
想要存放String类型的数据:
2.3 泛型类语法
class 泛型类名称 < 类型形参列表 > {// 这里可以使用类型参数}class ClassName < T1 , T2 , ..., Tn > {}class 泛型类名称 < 类型形参列表 > extends 继承类 /* 这里可以使用类型参数 */ {// 这里可以使用类型参数}class ClassName < T1 , T2 , ..., Tn > extends ParentClass < T1 > {// 可以只使用部分类型参数}
2.4 泛型类的使用
泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象MyArray < Integer > list = new MyArray < Integer > ();//当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写MyArray < Integer > list = new MyArray <> (); // 可以推导出实例化需要的类型实参为 Integer
2.5 裸类型(Raw Type)(了解)
裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型
MyArray list = new MyArray();
2.6 泛型是如何编译的
2.7 泛型的上界
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
语法:
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我们点进去查看:
Integer是继承了Comparable接口的.
如果我们重新定义一个类当参数:
是不被允许的, 因为没有Person没有实现Comparable接口
正确写法:
2.8 泛型方法
语法:
方法限定符 < 类型形参列表 > 返回值类型 方法名称 ( 形参列表 ) { ... }
上述代码可以写成 泛型方法:
当这个泛型方法是静态的时, 我们就不用实例化对象, 直接通过类来调用泛型方法