Java学习【深入探索包装类和泛型】
- 🚀包装类
- 获取包装类对象的方式
- 使用valueOf()创建
- 直接赋值
- Integer成员方法
- 🚀泛型
- 引出泛型
- 泛型类
- 泛型方法
- 泛型接口
- 泛型的继承和通配符
- 泛型的上界
在Java的学习中,包装类和泛型是两个重要的概念,它们不仅丰富了Java的数据类型,还提高了代码的可读性和安全性。下面,我们将深入探讨这两个主题。
🚀包装类
包装类是Java提供的一种特殊类,它们将Java的基本数据类型(如int、double、char等)封装成对象。这样做的好处是可以将基本数据类型作为对象来处理,使用对象所特有的属性和方法。
Java提供了8种基本数据类型的包装类:
Integer → int
Double → double
Byte → byte
Short → short
Long → long
Float → float
Character → char
Boolean → boolean
获取包装类对象的方式
使用valueOf()创建
以Integer为例,直接通过调用valueOf方法,把值传入到方法中
Integer i1 = Integer.valueOf(127);
下面看一个特殊的例子:
Integer i1 = Integer.valueOf(127);
Integer i2 = Integer.valueOf(127);
System.out.println(i1 == i2); // true
Integer i3 = Integer.valueOf(128);
Integer i4 = Integer.valueOf(128);
System.out.println(i3 == i4); // false
对于引用数据类型,因为" == " 是比较地址值的,i3 , i4不是同一个对象可以理解,但是为什么 i1 和 i2 是一样的呢?
在实际开发中,-128~127之间的数字用的比较多,为了节省内存,Java提前创建好了这些对象,用的时候直接调取,不会再创建新的对象
直接赋值
自动装箱和自动拆箱
当使用包装类的时候,该怎么去进行计算呢,首先要把对象转化为基本数据类型,再进行计算,再转化为引用数据类型,这样手动的去转化就非常麻烦,所以在JDK5的时候就出现了自动装箱和自动拆箱的机制
自动装箱:把基本数据类型变为对应的包装类
自动拆箱:把包装类转化为对应的基本数据类型
//自动拆箱
Integer i = 1;
//自动装箱
int j = i;
System.out.println(i);
System.out.println(j);
System.out.println(i + j);
Integer成员方法
返回值为String类型
//进制转换
System.out.println(Integer.toBinaryString(10));//1010
System.out.println(Integer.toOctalString(10));//12
System.out.println(Integer.toHexString(10));//a
类型转换:
Integer i5 = Integer.parseInt("123");
System.out.println(i5 + 1);
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();//键盘录入一行,遇到空格不会停止
System.out.println(s);
System.out.println(Integer.parseInt(s) + 1);
除了 Character,都有对应的转换方法
🚀泛型
在Java编程中,泛型是一个强大的工具,它允许我们在编写代码时定义灵活的、可重用的结构,而无需关心具体的数据类型。
引出泛型
问题:实现一个类,类中包含一个数组成员,使得数组中可以存放任意类型的数据,也可以根据成员方法访问返回数组中下标的值
如果是任意类型的话,可以考虑Object,因为它是所有类型的父类,接着试着实现一下这个问题
class MyArray{
private Object[] arr = new Object[10];
public void set(int index,Object value){
arr[index] = value;
}
public Object get(int index){
return arr[index];
}
}
public class Demo1 {
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.set(0,"AA");
myArray.set(1,1);
String s = (String) myArray.get(0);//因为返回的是Object类型,所以需要强制类型转换
int i = (int)myArray.get(1);
System.out.println(s+" "+i);
}
}
上面的代码就实现了这个要求,但是此时发现一个弊端,如果数据过多的话就显得特别杂乱,各种类型都有,都需要强制类型转换
泛型的主要目的就是:指定当前容器要持有什么类型的对象,接着让编译器去检查类型,此时就是把类型作为参数传递,需要什么类型就传入什么类型
格式: <数据类型>
注意: 泛型只能支持引用数据类型
泛型的擦除:
类型擦除是Java编译器在编译泛型代码时的一个步骤。在编译过程中,编译器会将泛型信息从代码中擦除,并在需要的地方插入类型转换和类型检查代码。这样,运行时的字节码不包含任何泛型类型信息,只包含原始类型和必要的类型转换。
ArrayList<Integer> arrayList = new ArrayList<>();
例如在创建集合时,当数据真正的添加到集合里边的时候,集合还是会把它们当作Object类处理,只不过往外获取时会进行相应的强制类型转换
泛型类
当一个类中,某个变量的类型不确定,就可以定义带有泛型的类
格式:
修饰符 class 类名 <类型>{
}
class MyArray <E>{
private Object[] arr = new Object[10];
public void set(int index,E value){
arr[index] = value;
}
public E get(int index){
return (E) arr[index];//
}
}
public class Demo1 {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>(); //已经确定好类型了
//myArray.set(0,"Str"); 自动类型检查,要与上面的类型一致
myArray.set(0,1);
int i = myArray.get(0);
System.out.println(i);
//创建String类型
MyArray<String> myArray2 = new MyArray<>();
myArray2.set(0,"hello");
String str = myArray2.get(0);
System.out.println(str);
}
}
上面的MyArray就是一个泛型类,通过使用泛型,就对传入的数据类型进行了约束,同时,也实现了可以传入不同的类型参数
泛型方法
当一个类中只有一个方法中要用到不确定的类型,就只需要把这个方法定义为泛型方法即可
格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名){
}
class ListUtil{
public void show(){
System.out.println("其他方法···");
}
public static <E> void addAll(ArrayList<E> list,E e1,E e2,E e3){
list.add(e1);
list.add(e2);
list.add(e3);
}
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
addAll(list,"a","b","c");
System.out.println(list);
ArrayList<Integer> list2 = new ArrayList<>();
addAll(list2,1,2,3);
System.out.println(list2);
}
}
泛型接口
格式:
修饰符 interface 接口名<类型>{
}
例如Java中的List接口就是一个泛型接口:
泛型接口的使用方法:
1.实现类给出具体类型
2.实现类延续泛型,创建对象时再确定类型
泛型的继承和通配符
泛型不具备继承性,但数据具备继承性
什么意思呢
首先定义了两个具有继承关系的类,method方法里边所限定的类型是Fu 类型,它的子类型所创建的对象并不能使用该方法,如果想要子类型也能使用,就需要把方法定义为泛型方法,但是如果是其他类型也可以使用,怎么去限定只有这种具有继承关系的类才能使用
这时就可以使用通配符来实现
通配符:?
也表示不确定类型,但是可以限定类型,
? extend E: 表示可以传递E 或 E所有的子类类型
? super E: 表示可以传递E 或 E 所有的父类类型
泛型的上界
上面介绍的通过extend进行类型限制就是指定了泛型的上界,下面还有一种复杂的示例
例如:
写一个泛型类,定义一个方法,可以求数组的最大值
这时候就需要用到compareTo方法,就要实现comparable接口
public class Alg<E extends Comparable<E>> { //对泛型进行了限制,必须实现Comparable接口的类型才能传入
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 static void main(String[] args) {
Integer[] arr = {1,2,3,4,5};
Alg<Integer> alg = new Alg<>();
int a = alg.findMax(arr);
System.out.println(a); //5
}
}
比如再定义一个Person类,因为它没有实现Comparable接口,所以泛型里边不能传入Student