目录
前言:
这节中我们来一起看看我们之前在javaSE阶段所提到过的包装类究竟是什么,以及我们即将接触到的泛型是什么?我们又应该怎么用这些。
1.泛型
1.1什么是泛型
一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义的类,如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。其实简单来讲泛型就是适用于许多许多的类型。从代码角度来看就是对类型实现了参数化。
1.2为什么要使用泛型
下面我直接来通过代码来进行解释。
class MyArray1{
public Object[] obj = new Object[3];
public Object getPos(int pos) {
return obj[pos];
}
public void setPos(int pos, Object val) {
obj[pos] = val;
}
}
public class Test1 {
public static void main(String[] args) {
MyArray1 myArray = new MyArray1();
myArray.setPos(2,10);
myArray.setPos(1,"hello");
double d = (double)myArray.getPos(2);//需要进行强转
Object c = myArray.getPos(1);//采用Object可以接收任何数据,但是使用起来毫无意义
}
}
如上述代码所示任何数据都可以存放,但是在取出时1号下标本身存储的是字符串,但是我们使用了double来接收,所以我们必须要进行强制类型转换,在double d = (double)myArray.getPos(2)时必须要进行强转,否则就会报错。
虽然在这种情况下,当前数据都可以存放,但是我们还是希望他只存放一种数据类型,而不是同时存放多种数据类型,这样的话我们在取出的时候就不好取出,所以,泛型的主要目的就是指定当前的容器要持有什么类型的对象,让编译器帮助我们去做检查。此时就需要把类型作为什么类型,就传入什么类型。
继续跟着小编往下走,一起来看看泛型究竟怎么使用。
1.3泛型的语法及使用
语法格式:
class 泛型类名称<类型形参列表>{
//这里可以直接使用类型参数
}
使用:
我们直接以上面的实例来进行改编。
class MyArray1<E>{
public E[] obj = (E[]) new Object[3];//这种表示不够规范!!!
public E getPos(int pos) {
return obj[pos];
}
public void setPos(int pos, E val) {
obj[pos] = val;
}
}
public class Test1 {
public static void main(String[] args) {
//进行实例化一个对象
MyArray1<Integer> myArray1 = new MyArray1<>();
//存放一个数据
myArray1.setPos(2,10);
myArray1.setPos(1,20);
//取出一个数据
Integer a1 = myArray1.getPos(1);
Integer a2 = myArray1.getPos(2);
}
}
在上述代码中 public E[] obj = (E[]) new Object[3];//这种表示不够规范!!!
那么我们来看一下规范的表示方法应该怎么表示:
代码如下所示:
class MyArray2<E> {
//官方的表示方式
public Object[] obj = new Object[3];
public E getPos (int pos){
return (E)obj[pos];//进行强转
}
public void setPos(int pos, E val) {
obj[pos] = val;
}
}
public class Test2 {
public static void main(String[] args) {
MyArray1<String> myArray1 = new MyArray1<>();
myArray1.setPos(1,"hello");
myArray1.setPos(2,"world");
String s1 = myArray1.getPos(1);
String s2 = myArray1.getPos(2);
System.out.println(s1 + " " + s2);
}
}
结果如下所示:
1.4泛型是如何编译的
1.4.1擦除机制
究竟何为擦除机制呢?在编译期间直接将所有的泛型类替换为Object的机制我们称之为擦出机制。我们在学习C语言的时候有一个叫做宏定义的,这种机制就是在编译的时候将所有宏定义出来的关键字的地方替换成实际的数字或者是字母,那么我们现在所接触到的擦除机制也就和这种宏替换一样,都是在编译期间就已经擦除掉以前的,直接换成Object。
如下面的解释所示:
1.5泛型类的上界
在定义泛型类时,有时候需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
具体可以通过一下的一个代码来说明为什么会有泛型的上界。
语法如下所示:
class 泛型类名称<类型参数 extends 类型边界>{ ... }
代码如下所示:
class Alg<E extends Comparable<E>> {
//E继承了Comparable所以下面才可以使用CompareTo来进行比较
//此时Comparable就是E的上界
public E findMax(E[] array) {
E max = array[0];
for (int i = 0; i < array.length; i++) {
if(max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
public class Test3 {
public static void main(String[] args) {
Alg<Integer> alg = new Alg<>();
Integer[] array = {1,2,3,6,19};
Integer val = alg.findMax(array);
System.out.println(val);
}
}
结果如下所示:
1.6为什么不能实例化泛型类数组
代码如下所示:
class MyArray4<E> {
public E[] array = (E[])new Object[3];//写法不够规范
// public Object[] array = new Object[3];//规范写法
public void setPos(int pos, E val) {
array[pos] = val;
}
public E getPos(int pos) {
return array[pos];
}
public E[] getArray() {
return array;
}
}
public class Test4 {
public static void main(String[] args) {
MyArray4<Integer> myArray4 = new MyArray4<>();
Integer[] a2 = myArray4.getArray();
}
}
结果如下所示:
你会发现在运行的时候会出现类型转换异常,为什么呢?
因为在编译的时候E会被擦除成Object类,所以在Integer[] a2 = myArray4.getArray()的时候会发生向下转型,编译器会检查出向下转型的时候的不安全,所以会报错,通俗的讲就是:返回的Object数组里面,可能存放的是任何数组类型,可能是String,可能是Person类型的,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。
1.7泛型方法
泛型也可以是一个方法,那么接下来我们来看具体的实现方法。
代码如下所示:
class Alg1{
public <E extends Comparable<E>> E findMax(E[] array) {
E max = array[0];
for (int i = 0; i < array.length; i++) {
if (max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
public class Test6 {
public static void main(String[] args) {
Alg1 alg1 = new Alg1();
Integer[] array = {1,4,2,76,2,9};
Integer s1 = alg1.<Integer>findMax(array);
// Integer s1 = alg1.findMax(array);//上面类型的指定也可以省略,省略后系统会根据后面的类型自动识别。
System.out.println(s1);
}
}
结果如下所示:
当然我们也可以不用实例化对象来实现调用。
可以将该方法写成static的。
代码如下所示:
class Alg2 {
public static <E extends Comparable<E>> E findMax(E[] array) {
E max = array[0];
for (int i = 0; i < array.length; i++) {
if (max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
public class Test7 {
public static void main(String[] args) {
Integer[] array = {3,2,5,6,1,9};
Integer ret = Alg2.findMax(array);//直接通过类名调用
System.out.println(ret);
}
}
结果如下所示:
2.包装类
为什么会有包装类?在Java中
2.1基本数据类型所对应的数据类型
基本数据类型 | 包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
其中除了Integer和Character之外其他都时直接有将首字母大写即可。
2.2装箱和拆箱
2.2.1显示装箱
代码如下所示:
public class Test9 {
public static void main(String[] args) {
int a1 = 10;
Integer b1 = Integer.valueOf(a1);//显示装箱
System.out.println(b1);
}
}
结果如下所示:
2.2.2显示拆箱
代码如下所示:
public class Test10 {
public static void main(String[] args) {
Integer a2 = 30;
int b2 = a2.intValue();//显示拆箱
double b3 = a2.doubleValue();//也可以拆成其他的类型。
System.out.println(b2);
System.out.println(b3);
}
}
结果如下所示:
2.3自动装箱和自动拆箱
2.3.1自动装箱
代码如下所示:
public class Test11 {
public static void main(String[] args) {
int a3 = 20;
Integer b3 = a3;
System.out.println(b3);//自动装箱
}
}
结果如下所示:
2.3.2自动拆箱
代码如下所示:
public class Test12 {
public static void main(String[] args) {
Integer a4 = 30;
int b4 = a4;
double b5 = a4;
System.out.println(b4);//自动拆箱
System.out.println(b5);
}
}
结果如下所示:
2.4包装类题讲解
大家看下面的代码,想想玩什么会出现这种情况。
代码如下所示:
public class Test13 {
public static void main(String[] args) {
Integer a1 = 127;
Integer a2 = 127;
Integer b1 = 128;
Integer b2 = 128;
System.out.println(a1 == a2);
System.out.println(b1 == b2);
}
}
结果如下所示:
解释:
相信聪明的大家一定也想到了吧,是滴没错,在每次赋值的时候都会发生装箱的操作,这时候我们就得去看看Integer底层的逻辑了。
根据Integer的底层比较代码我们不难看出当i的值处于[-128,127]之间的时候我们是返回cache缓存中的数值,但是一旦超出了那个范围就会new一个对象,所以在上述代码中我们可以看到当判断a1 == a2的时候,返回值是true,当b1 == b2的时候返回的值是false,因为当new一个对象的时候我们相当于比较的是地址,所以会返回false。
结束语:
这次小编主要与大家分享了包装类,包装类中的拆箱和装箱,自动拆箱和自动拆箱,以及简单的带着大家认识了一下泛型,讲述了为什么会出现泛型,如何使用泛型,泛型在编译的时候采用的擦除机制,以及泛型的上界,后期小编还会继续给大家仔细讲解有关泛型更多的知识,大家记得查看哦!大家继续跟紧小编的步伐一起往下走吧!!!希望对大家有所帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)