包装类
基本类型对应的包装类
Java是一个面向对象的编程语言,但其基本数据类型(如int、char、boolean等)并不直接支持面向对象的特性。为了弥补这一不足,Java为每种基本数据类型设计了一个对应的类,这些类统称为包装类(Wrapper Class)。包装类均位于java.lang
包中。
装箱和拆箱
装箱(Boxing)和拆箱(Unboxing)是Java语言中关于基本数据类型(primitive types)和它们对应的包装类(wrapper classes)之间转换的两个重要概念。
装箱
- 装箱是指将基本数据类型转换为对应的包装类对象的过程。
- 例如,将
int
类型转换为Integer
类型。
int a = 10;
Integer ii = Integer.valueOf(a); //显示装箱 or 手动装箱
Integer jj = a; //隐式装箱 or 自动装箱
我们查看这段代码的汇编代码可以发现,两种装箱底层都是调用的 valueOf()
方法
拆箱
- 拆箱是指将包装类对象转换为对应的基本数据类型的过程。
- 例如,将
Integer
类型转换为int
类型。
Integer ii = 10;
int a = ii; //自动拆箱
int b = ii.intValue(a); //手动拆箱
我们查看这段代码的汇编代码可以发现,两种拆箱底层都是调用的 intValue()
方法
阿里笔试题【Integer的比较】
//判断两次输出分别是什么
Integer a = 100;
Integer b = 100;
System.out.println(a==b);
Integer c = 200;
Integer d = 200;
System.out.println(c==d)
答案:true false
解析:
- 这些赋值的过程都是在进行装箱,所以都调用了
valueOf()
方法
- 由
if
判断条件可知,i
如果是在[-128, 127]
之间,就是返回一个数组的值- 相反,若不在这个范围,就会随机实例化一个对象进行返回
- 看题可知,100在范围之内,所以 a 和 b 都是返回
IntegerCache.cache[100-(-128)] = IntegerCache.cache[228]
,故a == b
为true
- 而200不在范围内,所以 c 和 d 均随机实例化两个不同的对象,会占用内存中不同的位置,一定不会相等,故
c == d
为false
泛型
- 属于一个语法
- 简单来说就是适用于许多类型
- 主要功能是把类型参数化,意味着可以传指定的类型参数
为什么要有泛型
举例:
- 实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中控某个下标的值
- 实现这个类,我们先定义一个
Object[]
数组,Object 使得这个数组可以放下任何类型的数据- 然后创建一个
setArray(int pos, Object obj)
方法,用来在第pos
位上存放数据obj
- 再创建一个
Object getArray(int pos)
方法,用来返回第pos
位的值- 可是在当你在
pos = 1
的地方,传入一个obj = 10
之后,当准备创建一个int a
来接收array[1]
这个值的时候,程序确保错了
- 这是由于
getArray()
方法为了可以让数组能返回任意类型的数据,返回类型设置成了Object
- 而在要以
int
类型取出这个数据的时候,由于类型不匹配,导致报错- 若要成功接收,就需要强制类型转换,将
Object
的类型转化为int
- 因为这样操作下来非常麻烦,所以泛型就出现了
- 上面例子的代码为:
public class MyArray {
public Object[] array = new Object[10];
public void setArray(int pos, Object obj) {
array[pos] = obj;
}
public Object getArray(int pos) {
return array[pos];
}
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setArray(1,3);
int a = (int) myArray.getArray(1);
}
}
泛型语法
class 泛型类名称<类型形参列表> { // 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> { }
class 泛型类名称<类型形参列表> extends 继承类 { }
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> { }
- 上面例子用泛型来写:
public class MyArray<T> {
public Object[] array = new Object[10];
public void setArray(int pos, T obj) {
array[pos] = obj;
}
public T getArray(int pos) {
return (T) array[pos];
}
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();
myArray.setArray(1,3);
Integer a = myArray.getArray(1);
}
}
- 这里面存放数据的时候都是按照
T类型
来存放的,最后取出的时候是按照Integer类型
来检查的,若索取出的值和实例化中<>
内的类型一直,则可取出,反之报错
< >:
- 八种基本类型不能写在里面
- 只能写包装类类型或者类类型,包装类可以,自己定义的类也可以
- 小结:
- 泛型是讲数据类型参数化,进行传递
- 使用 表示当前类是一个泛型
- 泛型目前为止的优点:数据类型参数化、编译时自动进行类型检查和转换
泛型的上界
- 在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束
- 在泛型编程中,上界通过
extends
关键字(在Java中)来指定,用于声明一个类型参数必须是某个特定类或接口(或其子类/实现类)的实例。这允许在编译时进行更严格的类型检查,防止类型不匹配的错误
public class MyArray<E extends Number> { //只接受Number的子类型或Number本身作为E的类型实参
...
}
public class MyArray<E extends Comparable<E>> { //E必须是实现了Comparable接口的
...
}
泛型方法
- 泛型方法,顾名思义,就是在定义方法时引入了类型参数的方法。这些类型参数在调用方法时会被具体的类型所实例化,从而允许同一个方法接受不同类型的参数并返回相应类型的结果。泛型方法的类型参数通常放在方法返回类型之前,并使用尖括号
<>
包围。
public <T extends Comparable<T>> T findMax(T[] array){
T max = array[0];
for(int i = 1; i < array.length; i++){
if(array[i].compareTo(max) > 0){
max = array[i];
}
}
return max;
}