数据结构——包装类泛型
一:包装类
在Java中,由于基本类型不是继承⾃Object,为了在泛型代码中可以⽀持基本类型,Java给每个基本类型都对应了⼀个包装类型。
类型、基础数据类型(内置数据类型):byte int short long float double boolean char 、
引用类型:所有的类(引用类型)都是直接间接继承Object类。
java是希望把所有的类型统一达成Object体系
包装类:标准库中创建了一组类,把内置类型的变量,包一下。
1.1:基本数据类型和对应的包装类
除了 Integer 和 Character, 其余基本类型的包装类都是⾸字⺟⼤写。
1.2:装箱和拆箱
虽然包装类,确实是类,在使用的时候,进行算术运算,还是需要转成内置类型,在进行运算。
包装类和内置类型转换。
内置类型-》包装类:装箱
包装类-》内置类型:拆箱int i = 10;// 装箱操作,新建⼀个 Integer 类型对象,将 i 的值放⼊对象的某个属性中Integer ii = Integer.valueOf(i);Integer ij = new Integer(i);// 拆箱操作,将 Integer 对象中的值取出,放到⼀个基本数据类型中int j = ii.intValue();
这个我们了解一下就可以了,现在的从ava8开始,已经可以自动拆箱装箱了。
1.3:自动拆箱和装箱
public class Test { public static void main(String[] args) { //内置类型,赋值给包装类 // Integer i = 10; //包装类,赋值给内置类型、自动拆箱 // int n=i; Integer i = 10; Integer j = 20; System.out.println(i+j);//这里的+就是针对两个Integer先转int在计算
对于包装类来说,拿过来直接用进行了,当内置类型来用。
但有的地方需要使用包装类,不能使用内置类型。
面试题: 下列代码输出什么,为什么? Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; System.out.println(a == b); System.out.println(c == d); //true //false
jvm针对Integrt这样的常量也做了优化
-128->127这个范围的数字,Integrt对象提前创建好了,保存到了一个“常量池”中。
jvm缓存了哪些integer对象,取决于jvm的实现。
二:范型
2.1:什么是泛型
⼀般的类和⽅法,只能使⽤具体的类型: 要么是基本类型,要么是⾃定义的类。如果要编写可以应⽤于多种类型的代码,这种刻板的限制对代码的束缚就会很⼤。----- 来源《Java编程思想》对泛型的介绍。
泛型是在JDK1.5引⼊的新的语法,通俗讲,泛型:就是适⽤于许多许多类型。从代码上讲,就是对类型实现了参数化。
2.2:引出泛型
泛型就是把所有程序员所需要的类型都包含上。
1. 我们以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10]; String[] strs =new String[10];
2. 所有类的⽗类,默认为Object类。数组是否可以创建为Object?
class MyArray {public Object[] array = new Object[10];public Object getPos(int pos) {return this.array[pos];}public void setVal(int pos,Object val) {this.array[pos] = val;}}public class TestDemo {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.setVal(0,10);myArray.setVal(1,"hello");//字符串也可以存放String ret = myArray.getPos(1);//编译报错System.out.println(ret);}}
问题:以上代码实现后 发现
1. 任何类型数据都可以存放
2. 1号下标本⾝就是字符串,但是确编译报错。必须进⾏强制类型转换
虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有⼀种数据类型。⽽不是同时持有这么多类型。所以,泛型的主要⽬的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传⼊什么类型。
三:语法
基础语法
class 泛型类名称<类型形参列表> { // 这⾥可以使⽤类型参数 } class ClassName<T1, T2, ..., Tn> { }
其他写法:
class 泛型类名称<类型形参列表> extends 继承类/* 这⾥可以使⽤类型参数 */ { // 这⾥可以使⽤类型参数 } class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> { // 可以只使⽤部分类型参数 }
四:泛型的使用
package object.package1; class MyArrayTemplate<T> { //此时arr是T[]类型 T是啥类型? 还不知道,得在后面实例化的时候才知道 //由于T类型位置,不能直接new T[] private T[] arr=(T[]) new Object[10]; public T get (int index){ return arr[index]; } public void set (int index, T value){ arr[index]=value; } } public class Test3 { public static void main(String[] args) { //针对MyArrayTemplate的进行实例化 //泛型参数类型T是Integer //针对arr来说,就只能存储Integer类型 MyArrayTemplate<Integer> myArray = new MyArrayTemplate<Integer>(); myArray.set(0, 10); System.out.println(myArray.get(0)); //针对MyArrayTemplate的进行实例化 //泛型参数类型T是String MyArrayTemplate<String> myArray2 = new MyArrayTemplate<String>(); myArray2.set(0, "hello"); System.out.println(myArray2.get(0)); } } //10 //hello
针对泛型参数来说,只能指定引用类型,不能指定内置类型(int 就不可以。)
泛型这一套语法体系,底层还是靠Object这一些列的继承来完成的。
了解: 【规范】类型形参⼀般使⽤⼀个⼤写字⺟表⽰,常⽤的名称有:
E 表⽰ Element
K 表⽰ Key
V 表⽰ Value
N 表⽰ Number
T 表⽰ Type
S, U, V 等等 - 第⼆、第三、第四个类型
也可以省略实例化的一部分:(类型推导) MyArrayTemplate<Integer> myArray = new MyArrayTemplate<Integer>(); 转换为: MyArrayTemplate<Integer> myArray = new MyArrayTemplate<>();
五:裸类型
5.1: 说明
裸类型是⼀个泛型类但没有带着类型实参,例如 MyArrayList 就是⼀个裸类型
MyArray list = new MyArray();
只是警告但不报错,
注意: 我们不要⾃⼰去使⽤裸类型,裸类型是为了兼容⽼版本的 API 保留的机制
下⾯的类型擦除部分,我们也会讲到编译器是如何使⽤裸类型的。
⼩结:
1. 泛型是将数据类型参数化,进⾏传递
2. 使⽤ <T> 表⽰当前类是⼀个泛型类。
3. 泛型⽬前为⽌的优点:数据类型参数化,编译时⾃动进⾏类型检查和转换
六:泛型如何编译的
6.1 :擦除机制
那么,泛型到底是怎么编译的?
1. 基本概念:
在编译时,Java 编译器会将泛型类型信息从代码中移除,这个过程就叫做类型擦除。
擦除后,泛型类型会被替换为其边界类型(通常是 Object)或者指定的类型。
2. 擦除过程:
将泛型参数替换为其边界或 Object。
在必要的地⽅插⼊类型转换以保持类型安全。
⽣成桥接⽅法以保持多态性。
七 :泛型的上界
针对某个类泛型参数,能填啥,做出限制。
上界:约定了泛型参数的父类,然后有一个界限。
例如:泛型参数只能填数字不能填字符串
或者泛型参数只能填Animal,就是有限制。
7.1:语法
class 泛型类名称<类型形参 extends 类型边界> { ... }
7.2:实例
public class Test4 <T extends Number> { } 继承Number父类,它的子类只能是数字 Test4<Integer> l1; // 正常,因为 Integer 是 Number 的⼦类型Test4<String> l2;//// 编译错误,因为 String 不是 Number 的⼦类型 这样就不可以使用, eg: public class Test4 <T extends Number> { public static void main(String[] args) { Test4<Integer> t1 = new Test4<Integer>(); Test4<Double> t2 = new Test4<Double>(); Test4<Float> t3 = new Test4<Float>(); Test4<Long> t4 = new Test4<Long>(); Test4<Short> t5 = new Test4<Short>(); Test4<Byte> t6 = new Test4<Byte>(); Test4<String> t7 = new Test4<String>();//String表示Number子类
也就是继承,然后类型匹配,不能匹配就不执行。
八:通配符(统统匹配)
public class Test5 <T>{ public static void main(String[] args) { Test5<Integer> t1 = new Test5<Integer>(); //报红就不能实例化,因为泛型参数不匹配 t1= new Test5<String>(); //这个情况就是不挑食,无论泛型参数是什么,都可以通过t来实例化 //此处?就是通配符 Test5<?> t= new Test5<Integer>(); t= new Test5<String>();
在"?"的基础上⼜产⽣了两个⼦通配符:
? extends 类:设置通配符上限:
//指定了通配符的上界 //此时t1可以实例化Integer,String,Double等所有Number的子类 Test5<? extends Number> t1= new Test5<Integer>(); //错误写法,String类型不是Number的子类,所以编译报错 t1 = new Test5<String>();
? super 类:设置通配符下限(都是一样的规则,也就是不能超过子类)
九:总结
本片博客讲述了包装类和泛型,为接下来进入数据结构,开启新篇章。
大家也要了解以下几个问题:
包装类:
1包装类是什么
2包装类和内置类型的对应关系
3包装类和内置类型的转换(装箱拆箱)泛型:
1:泛型解决了什么
2:泛型类咋怎么定义
3:泛型类怎么实例化