在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了 一个包装类型。
除了 Integer 和 Character, 其余基本类型的包装类都是在基本类型的基础上首字母大写即可。
1、 装箱操作,新建一个 Integer 类型对象,将 基本数据类型 i 的值放入到包装类对象的某个属性中
2、拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
public static void main(String[] args) {
int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);
// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();
}
通过下面控制板和Interger.valueOf()的底层代码了解----装箱和拆箱
下图为装箱:
public static void main(String[] args) {
int i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱
}
深入了解intreger包装类,运行以下代码且查看其结果:
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.println(a == b);
Integer c = 128;
Integer d = 128;
System.out.println(c == d);
}
}
// true
// false
详细图解如下:
由上图所示IntergerCache.low=-128;IntergerCache.high=127;当我们用包装成interger类的int数据类型的数据在(-128,127)之间,我们最后包装类的分配引用和之前类型的数据结果是一样;但是如果我们要包装的数值结果不在上述这个区间,系统会进行当前数值+(-128)操作并返回给方法,这样的话会导致系统分配给包装类的引用不太一样。
一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。
泛型是在JDK1.5引入的新的语法,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化
泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
代码如下:
class MyArray {
public T[] array = (T[])new Object[10];//1
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
public class TestDemo {
public static void main(String[] args) {
MyArray myArray = new MyArray<>();//2
myArray.setVal(0,10);
myArray.setVal(1,12);
int ret = myArray.getPos(1);//3
System.out.println(ret);
myArray.setVal(2,"bit");//4
}
}
代码解释:
1. 类名后的代表占位符,表示当前类是一个泛型类
拓展:【**】类型形参一般使用一个大写字母表示,常用的名称有:
2. 注释1处,不能new泛型类型的数组,如下图所示:
3. 注释2处,类型后加入 指定当前类型
4. 注释3处,不需要进行强制类型转换
5. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时 候帮助我们进行类型检查。
上述代码泛型类改造之后:
class MyArray {
public Object[] array = new Object[10];//1
public T getPos(int pos) {
return (T)array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
代码展示:
注意:泛型只能接受类,所有的基本数据类型必须使用包装类!
裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型,如下图所示:
小结:
1. 泛型是将数据类型参数化,进行传递
2. 使用 表示当前类是一个泛型类。
3. 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换
那么,泛型到底是怎么编译的?(面试题哦)
通过命令:javap -c 查看字节码文件,所有的T都是Object。
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。
Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息,下面详细了解一下擦除机制:
但是根据下图的getArray()所示:
虽然我们创建的myarray1对象存储的数据是integer的,但是在使用getArray()方法时,我们将myarray1的存储的数据类型擦除,变为object类,所以将被擦除成object类的数据传给strings(被定义为integer时),会导致有的数据可能是非integer而系统报错。
泛型类的使用时我们给的类型实参主要起两个作用:
(1)存储数据的时候,可以帮我们进行自动的类型检查
(2)获取数据的时候,可以帮我们进行类型转换
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
代码eg:
public class MyArray {
...
}
MyArray
MyArray l1; // 正常,因为 Integer 是 Number 的子类型
MyArray l2; // 编译错误,因为 String 不是 Number 的子类型
当没有指定类型边界 E,可以视为 E extends Object
方法限定符 < 类型形参列表 > 返 回值类型 方法名称 ( 形参列表 ) { ... }
public class Text2 {
public static > E Max(E[]arr) {
E ret = arr[0];
for (int i = 1; i < arr.length; i++) {
if (ret.compareTo(arr[i]) < 0) {
ret = arr[i];
}
}
return ret;
}
public static void main(String[] args) {
Integer[] arr={6,2,3,4,1000,1};
System.out.println(Max(arr));
}
}
1、使用示例-可以类型推导
Integer[] a = { ... };
Max(a, 0, 9);
String[] b = { ... };
Max(b, 0, 9);
2、使用示例-不使用类型推导
Integer[] a = { ... };
Util.swap(a, 0, 9);
String[] b = { ... };
Util.swap(b, 0, 9);
ps:本次内容就到这里了,如果喜欢的话还请一键三连,后序有问题的话,我会修改的哦!!!