
1.包装类
1.1 包装类
在java中,由于基本数据类型不是继承object类,为了在泛型代码中可以支持基本数据类型,java给每个基本数据类型各自提供了·一个包装类。
如下图

除了char和int基本数据类型的包装类型有点特别,其他的都是首字母大写
1.2 装箱与拆箱
1. 装箱
装箱就是将基本数据类型的数据转化成包装类,装箱分为自动装箱和显示拆箱。
public static void main(string[] args) {
int a=10;
integer b=integer.valueof(a);//显示装箱
integer c=a;//自动装箱
}其实自动拆箱和显示拆箱的底层原理是一样的,都是调用了integer.valueof()方法。
2. 拆箱
拆箱就是将包装类的数据类型转换转换成基本数据类型,拆箱也分为自动拆箱和显示拆箱。
public static void main1(string[] args) {
integer a=10;
int b=a.intvalue();//显示拆箱
int c=a;//自动拆箱
}2.面试题
了解装箱与拆箱,我们来看一道面试题
public static void main(string[] args) {
integer a=100;
integer b=100;
system.out.println(a == b);//打印true
integer c=200;
integer d=200;
system.out.println(c == d);//打印false
}为什么会打印不同的结果呢?
我们来看integer.valueof()方法的原码

我们发现,在进行装包操作的时候,会根据装包的数据的大小来返回不同类型的数据。
当要装包的数据的范围在 [-127~128] 之间时,valueof 方法就会返回数组中的一个数据(整数)。
如下图

当数据不在上面的范围时,就会返回一个新的实例化的对象。
所以,由于100在【128~127】这个范围内,所以两者的比较是两个整数之间的比较。
由于200超出了以上范围,所以c和d的比较实际上是两个对象之间的比较,有==来比较两个对象,返回值当然是false。
2. 泛型
2.1 泛型的概念
一般类和方法,只能使用具体的数据类型,要么是基本数据类型或者是引用数据类型。当我们要编写设计多种数据类型的程序时,一旦我们将数据类型固定,那么对于后续的编程的限制会很大。所以在java中提出了泛型的概念,所谓泛型,就是将数据类型参数化。
2.2 引出泛型
我们先来看一道题:实现一个类,该类中有一个可以存储任何数据类型的数组,并且可以通过方法来设置数组中的值和获取数组中对应的内容。
代码如下图

在类中,我们创建了一个object类型的数组,这样就可以存储任何数据类型了。但是,后面我们发现,我们可以通过方法来直接设置数组中的内容,但是我们通过方法来获取数据类型的时候,却发现会报错,这是因为发生了向下转型,我们需要进行强制类型转换才能正确获得数组中的任何数据。这样想想就很奇怪了,我明明可以存储任何数据类型,但却不能直接1获取数组中的数据。为了解决这个问题,我们就可以使用泛型。
如以下代码
class database<e>{
object[] array=new object[10];
public void setarray(int pos,e obj){
array[pos]=obj;
}
public e getarray(int pos){
return (e)array[pos];
}
}
public class test {
public static void main(string[] args) {
database<integer> database=new database<>();
database.setarray(0,10);
integer a=database.getarray(0);
database<string> database1=new database<string>();
database1.setarray(1,"man");
string str=database1.getarray(1);
}
}
< e >就是泛型的用法 ,可以发现,当我们使用泛型之后,我们就可以直接来获取数据了,不用进行强制转换了。
简单来说,使用泛型,就行我们在创建类的时候,通过泛型,我们可以将数据类型转换为参数来进行类的创建。如上图,我们在实例化database对象的时候,我们将integer的数据类型作为参数传过去,所以此时,对于字母e就代表integer数据类型,实例化database1的时候,我们将string数据类型作为参数传过去,此时,对于database1来说,字母e就代表string类型。
注意事项:<e>可以理解为一个标识符,代表该类为泛型类。
2.3 泛型的语法
class 类名<t>{
}类名后的<t>是一个标识符,表示当前类为泛型类。 其中< >里面也可以是其他字母,常见的有t和e。
3.泛型类的使用
3.1 语法格式
泛型类名<类型实参> 变量名=new 泛型类名<类型实参>();
举例
class myfunc<t>{
}
public class test {
public static void main(string[] args) {
myfunc<integer> myfunc=new myfunc<integer>();
myfunc<string> myfunc1=new myfunc<>();
}
}在创建泛型类对象时,后面的new< >里面的包装类可以不写,编译器会根据前面的包装类来推导后面的包装类类型。
注意事项:泛型只能接受类,所有的基本数据类型必须使用包装类。
4. 裸类型(了解)
裸类型是一种不带参数的泛型类型,它是为了兼容以前老版本jdk没有泛型的版本。例如myarraylist就是一个裸类型。
5. 擦除机制
泛型是如何进行编译的呢?
泛型是编译时期的机制,代码在运行的时候没有泛型的概念。
这就涉及到擦处机制:在编译完成后,所有的传给泛型类的数据类型,最终都会被擦除为object类。所以,编译之后的字节码无泛型,只有object类。
6. 泛型的上界
在定义泛型类时,有时需要对传入的类型变量进行限制,这时候可以通过类型边界来限制。
6.1 语法
class 泛型类名称<类型形参 extends 类型边界> {
...
}举例

这时定义的泛型类时传入的类型参数的上界为number,传如的类型参数必须是number的子类,由于string类不是number的子类,所以会报错。
注意事项:没有泛型边界的,默认边界为object。
6.2 复杂例子
题目要求:创建一个类,里面有一个方法来获取数组中的最大值。
我们会很直接的写下以下代码

当我们写出上图的代码之后,我们发现会报错。这是因为泛型类的e代表很多数据类型,我们不能用平常的数学思维去比较。这时候我们可以对传入的类型参数实现comparable接口来解决问题。
class alg<e extends comparable<e>>{
public e findmax(e[] array){
e max=array[0];
for(int i=1;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[] array={1,2,3,4,5};
alg alg=new alg();
int ret=alg.findmax(array);
}
}这时,由于我们在定义泛型类的时候使用了comparable接口,所以,此时,传入的类型参数必须实现comparable接口,否则会报错。
7. 泛型方法
7.1 语法格式
方法修饰限定符 <类型参数> 返回值类型 方法名(){
}举例
public static<e extends comparable<e>> e findmax(e[] array)
以上题目,获取最大值的另一种代码形式
class alg{
public static<e extends comparable<e>> e findmax(e[] array){
e max=array[0];
for(int i=1;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[] array={1,2,3,4,5};
alg alg=new alg();
integer ret=alg.findmax(array);//使用类型推导
integer ret2=alg.<integer>findmax(array);//不使用类型推导
}
}感谢观看。
到此这篇关于java包装类与泛型超详细教程的文章就介绍到这了,更多相关java包装类与泛型内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论