目录
1、包装类
1.1 包装类
在Java中,每一个基本数据类型都有一个对应的包装类:
在SE的学习中我们已有过简单了解。
我们可以注意到,除了int类型的包装类为Integer,char类型的包装类为Character外,其余基本类型的包装类均将首字母大写即可。
1.2 装箱和拆箱
装箱(装包):把基本数据类型变为包装类类型的过程,叫做装箱。
拆箱(拆包):把包装类类型变为基本数据类型的过程,叫做拆箱。
装箱又分为 自动装箱和显示装箱。
拆箱又分为 自动拆箱和显示拆箱。
1.2.1 装箱
装箱(装包):把基本数据类型变为包装类类型的过程,叫做装箱。
装箱分为 自动装箱和显示装箱。
1.2.1.1 自动装箱&显示装箱
public static void main(String[] args) {
Integer a = 10;//自动装箱
int b = 10;
Integer c = Integer.valueOf(b);//显示装箱
}
我们可以将数据直接赋值给包装类类型来自动装箱,也可以通过包装类中的方法来显示装箱。
1.2.2 拆箱
拆箱(拆包):把包装类类型变为基本数据类型的过程,叫做拆箱。
拆箱分为 自动拆箱和显示拆箱。
1.2.2.1 自动拆箱&显示拆箱
public static void main(String[] args) {
Integer a = 10;//自动装箱(先装好箱,再来拆箱)
int a1 = a;//自动拆箱
int a2 = a.intValue();//手动拆箱
double a3 = a.doubleValue();//手动拆箱
}
1.2.3 自动拆箱&自动装箱 底层原理
其实不管是自动装箱,还是自动拆箱,底层都是帮我们调用了valueOf或者intValue/doubleValue/..... 方法:
1.3 包装类面试题 --->缓存数组
我们先来看以下代码:
读到这里,大家可以先猜测一下结果。
结果是出人意料的:
为什么会出现以上的结果的?明明两组数据都是包装类啊,为什么一组结果是true,而另一组结果是false呢?
要解决问题,我们就需要找到问题的主要矛盾。
我们可以发现,这几行代码,仅仅只发生了装箱。那我们就去看装箱是怎么操作的,也就是valueOf的源码是怎么工作的:
我们发现,当我们传入的值满足一个范围的时候,返回了一个数组中的值,而不满足这个范围的时候,则新返回了一个对象,既然返回了一个新对象,新对象用 == 来进行比较,那结果必然是false!
那这个范围是多少呢?
我们可以看到,范围为[-128,127] 。
也就是说,当我们要装箱的数据在这个范围当中时,是直接从一个数组中拿的数据,而这个数组就是缓存数组。
缓存数组中共有256个数字,数组下标的范围为[0,255] ,存储着如下的数据:
所以当传入的数据在[-128,127]这个范围时,是直接从这个缓存数组中拿到的数据。
2、泛型
2.1 什么是泛型
顾名思义,泛型:就是适用于许多许多类型。
在我们之前的学习中,我们可以将一个数据当做参数传到一个方法中,而泛型,是将一个数据类型当做参数传入,我们需要什么类型,就传入什么类型。
2.2 泛型的语法
2.3 泛型的使用
我们将Integer作为参数传入,那我们用E来接收的参数的类型必须为整型,不能再传入其他类型,如:字符串、字符型......:
这里就会帮我们进行自动类型检查,如果不是对应的类型,就会报错。
我们接收数据时也不需要强制类型转换,会进行自动类型转换:
注意!注意!注意!!!
实例化对象时,泛型中传入的类型只能为类类型,不能为基本数据类型!!!
泛型代码:
class myArray<E> {
public Object[] array = new Object[10];
public void setValue(int pos,E val) {
array[pos] = val;
}
public E getValue(int pos) {
return (E)array[pos];
}
}
public class Test {
public static void main(String[] args) {
myArray<Integer> Array = new myArray<>();
Array.setValue(0,10);//自动类型检查
Array.setValue(1,100);//自动类型检查
//Array.setValue(2,"dings");自动类型检查 发现错误
Integer ret1 = Array.getValue(0);//自动类型转换
System.out.println(ret1);
}
}
2.4 裸类型(Raw Type) (了解)
2.5 泛型是如何进行编译的?
2.5.1 擦除机制
泛型是编译时期的一种机制,在运行的时候没有泛型的概念,也就是说,JVM当中没有泛型的概念。
在编译完成后,我们定义的<>中的T、E......等等,都会被擦除并且替换为Object,编译器生成的字节码在运行期间并不包含泛型的类型信息,这就是擦除机制。
2.5 泛型的上界
2.5.1 语法
2.5.2 示例一(以类为边界)
语法:
我们对泛型类定义了上界为Number ,那传入的类型只能是Number或者是Number的子类。
代码示例:
ps:没有指定类型边界 E,可以视为 E extends Object
2.5.3 示例二(以接口为边界)
例如:
那传入的类类型只能是实现了Comparable接口的类。
为什么要这样规定呢?
因为,我们使用的是泛型类,一旦我们要在类中进行数据的比较,那我们比较方法是未被定义的,就是说我们在写下这段代码时,还不知道传入的E会是什么类型,在方法中不能直接使用 >或者<来直接进行比较。于是,我们规定传入的类必须实现了Comparable接口,那就可以直接调用CompareTo方法来进行数据的比较了。
代码示例:
class A<E extends Comparable<E>> {//要求传入的类必须实现了Comparable接口,下面用来数据之间的比较
public E findMax(E[] array) {//利用compareTo进行比较,找到数组中的最大值
int max = 0;
for (int i = 0; i < array.length; i++) {
if (array[max].compareTo(array[i]) < 0) {
max = i;
}
}
return array[max];
}
}
public class Student implements Comparable<Student>{//实现了Comparable接口
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override//重写compareTo方法,规范比较行为
public int compareTo(Student o) {
return this.age - o.age;
}
public static void main(String[] args) {
Student[] students = new Student[]{
new Student("dinsg",10),
new Student("fdd",100),
new Student("kasg",21),
new Student("hau",1)
};
A<Student> aaa = new A<>();
Student maxStudent = aaa.findMax(students);
System.out.println(maxStudent);
}
}
2.6 泛型方法
我也可以不定义泛型类,仅仅通过对方法传入类型来定义一个泛型方法。
2.6.1 语法
2.6.2 示例
class A {
public<E extends Comparable<E>> E findMax(E[] array) {//非静态泛型方法
E max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i].compareTo(max) > 0) {
max = array[i];
}
}
return max;
}
}
public class Test {
public static void main(String[] args) {
Integer[] arr = new Integer[]{1,2,3,4,5,100,234,23,12,4,56};
A a = new A();
int ret = a.findMax(arr);//通过传入的参数会自动推导出泛型方法中E类型
//等价于int ret = a.<Integer>findMax(arr);
System.out.println(ret);
}
}
注意,调用泛型方法时有两种写法,这两种写法都是可以的:
①就是像调用普通方法一样调用泛型方法,Java会通过传入的数据(参数)自动推导出所要传入的类型
②在方法名前手动加上所要传入的类型参数
上面举例为普通的泛型方法,若要写静态的泛型方法,在修饰访问修饰限定符后加上static即可:
2.7 通配符
泛型的通配符: ?(表示不确定的类型)
我们在定义类、方法、接口时,如果我们不确定类型,我们就可以定义泛型类、泛型方法、泛型接口。泛型默认为Object,可以接收所有的类型。
但是我们一旦知道,只能是某个继承体系中的类型时,我们就可以使用通配符来限定类型的范围。
2.8 泛型的特点
泛型不具备继承性,但是数据具备继承性。
泛型不具备继承性的意思是:当我们将一个具有泛型的类作为一个方法形参,那么调用这个方法传入的实参的泛型也必须为这个类型,即使具有父子类关系也不可以:
数据具备继承性的意思是:我们可以在泛型类中 使用泛型接收泛型的子类类型的数据:
OK~本次博客到这里就结束了,
感谢大家的阅读~欢迎大家在评论区交流问题~
如果博客出现错误可以提在评论区~
创作不易,请大家多多支持~