看到这句话的时候证明:此刻你我都在努力
加油陌生人
前言
好久没有更新文章了,大概断更了20天,想着今天就写一下文章吧!最近也是又温习了一下数据结构,其实之前我写过关于数据结构的一个专栏那个专栏是写了顺序表,链表,栈和队列,但是那时是用C语言实现的,虽然数据结构不局限于语言,但是总归在语言的使用上有所不同,毕竟面向不同,一个是面向过程的C语言,一个是面向对象的Java。所以这次我打算起一个数据结构的Java专栏,当然由于之前已经写过有些地方会写得简洁一点,模糊的话大家可以去看一下前面得文章哦。今天是关于泛型的哦!只是简单的认识一下基础,为了更好的理解后面Java使用数据结构的代码。
认识包装类
Java中,包装类(Wrapper Classes)是用来包装原始数据类型的类。Java是一种面向对象的语言,所有的对象都是类的实例,包括基本数据类型。但是基本数据类型并不是对象,它们是Java语言的一部分,而不是Java类。为了将基本数据类型当作对象来处理,Java提供了对应的包装类。
以下是Java中的基本数据类型及其对应的包装类:boolean - Boolean
byte - Byte
short - Short
int - Integer
long - Long
float - Floa
double - Double
char - Characte
很容易看出来除了int和char的包装类有些特殊其它基本数据的包装类就是大写其第一个字幕。
包装类的主要作用包括:
自动装箱和拆箱:Java 5 引入了自动装箱(Autoboxing)和拆箱(Unboxing)的概念,允许自动将基本数据类型转换为对应的包装类对象,反之亦然。
使用对象的方法:包装类提供了一些有用的方法,比如toString()、equals()、hashCode()等,这些在基本数据类型中是不可用的。
集合框架:Java的集合框架只能存储对象,不能直接存储基本数据类型。通过包装类,可以将基本数据类型作为对象存储在集合中。
方法参数:在定义方法时,如果需要一个可变参数,可以使用包装类,因为基本数据类型是不可变的。
在数据结构中我们主要运用第三点集合框架,集合框架,后面的文章会给大家讲到。
自动装箱和拆箱的使用
以下就是自动装箱和拆箱的使用方法:
如果将num1和num2打印出来,都是一样的值----5。
public class Test { public static void main(String[] args) { Integer num1 = 5; // 自动装箱 int num2 = num1; // 自动拆箱 System.out.println(num1); System.out.println(num2); } }
包装类还提供了一些静态方法,比如Integer.valueOf(int i)用于将基本类型转换为包装类对象,
Integer.parseInt(String s)用于将字符串转换为基本类型。这些方法在处理数值与字符串之间的转换时非常有用。将字符串转化为整形其实我们前面在讲字符串时也是讲过的。
了解完包装类接下来就是我们的泛型学习了。
泛型的概念
泛型是Java中一种强大的特性,它允许程序员在编写代码时指定类型参数,从而使得代码更加灵活和可重用。泛型提供了一种方式,使得编译器可以在编译时检查类型安全,避免了类型转换的错误和运行时的类型检查。
当当看概念可能比较抽象,下面我们引出一个情境:
我们以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10]; String[] strs = new
String[10];
那么如果我想要一个什么类型都能储存的数组可以吗?
其实这是可以实现的,那么我们这里就要提到object类了。
所有类的父类,默认为Object类。数组是否可以创建为Object?
答案是可以的,如下代码,在自己定义了一个MyArray后,我们就可以在里面储存各种类型的数据了。
class MyArray { Object[] array = new Object[10]; public void setVal(int pos, Object val) { array[pos] = val; } public Object getVal(int pos) { return array[pos]; } } public class Test { public static void main(String[] args) { MyArray myArray=new MyArray(); myArray.setVal(0,2); myArray.setVal(1,"hello world"); int ret1=(int)myArray.getVal(0); //注意这里必须进行强制类型转换 String ret2=(String)myArray.getVal(1); //注意这里必须进行强制类型转换 System.out.println(ret1); System.out.println(ret2); } }
但是数组的定义本来就是用来储存同种类型数据的一种集合。像上面这种却又不像是数组呢。
下面我们在引入一个情境:
我在定义一个容器时暂时不知道用什么数据类型怎么办,我在用到时才知道这个容器要用什么类型的数据,这时我们该怎么办呢?这时我们的主角泛型就该出场了。
泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译 器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
那么下面就来细看泛型的使用
语法:
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
那么类型参数列表是什么呢?
其实我们可以理解为一个占位符,我们用一个字母来代表他是一个类型的数据,至于这具体是什么我们定义这个类时是不知道的,但是这时我们可以在这个类中先使用这个未知的类型进行一系列操作,当我们在实例化new一个对象出来时才可以传入自己想要使用的参数类型。
泛型的使用如下代码:
class Box<T> { private T t; public void set(T t) { this.t = t; } public T get() { return t; } } public class Test { public static void main(String[] args) { Box<Integer> box=new Box<>(); box.set(6); System.out.println(box.get()); } }
在上面代码中我们定义一个泛型类Box,在实例化时我们需要如上 Box box=new Box<>();那么这个类中的T,那么全部转化为Integer了,所以在传入类型实参时通常是一个类,即:这里的Integer不能是int,这就是我们为什么要先学习包装类。
这个泛型类就是我们数据结构最常用的泛型知识点。
泛型的上界
首先我们先了解什么是泛型上界
泛型上界是泛型编程中的一个重要概念,它用于指定泛型参数可以继承或实现的类或接口的类型范围。在Java等支持泛型的语言中,上界允许你限制泛型参数必须是某个类或接口的子类或实现。
其语法为:
class 泛型类名称<类型形参 extends 类型边界> {
······
}代码例子
public class MyArray<E extends Number> { ... }
那么这时我们的传入的类型变量就必须为Number的子类
MyArray n1; // 正常,因为 Integer 是 Number 的子类型
MyArray n2; // 编译错误,因为 String 不是 Number 的子类型
泛型方法
语法形式:
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) {
…
}
代码示例:
public class Util { //静态的泛型方法 需要在static后用<>声明泛型类型参数 public static<E> void swap(E[] array, int i, int j) { E t = array[i]; array[i] = array[j]; array[j] = t; } }
那么这时我们使用泛型方法时通常有两种方式:
一、可以自动进行类型推导:
Integer[] a = { ... }; swap(a, 0, 9); String[] b = { ... }; swap(b, 0, 9);
二、 不使用类型推导
Integer[] a = { ... }; Util.<Integer>swap(a, 0, 9); String[] b = { ... }; Util.<String>swap(b, 0, 9)
以上就是泛型的简单介绍,泛型的知识还有很多,但是我们这次只学习一些基础唔。主要是为了后面数据类型的打基础铺垫。