包装类
包装类的定义:是每一种基本数据类型所对应的类 类型,叫做包装类。有了包装类就是面向对象编程了。
装箱和拆箱:
装箱:装箱的定义就是把基本数据类型变成引用数据类型(或者叫包装类型),这个操作就叫做装箱。
如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/88df580b93f42d2679ab0bf4dcdd89cc.png)
a原来是int基本数据类型,把a赋值给Integer之后就变成了包装类型,这中间就涉及到了装箱操作,这样装箱也叫做自动装箱,还有一种是显示装箱,就是显而易见的可以看见的操作,我们可以看一下Java的汇编语言,这个时候它调用的是valueof这个方法,因为包装类,既然是一个类,其中就会有很多的方法,我们可以点进去看valueOf这个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/a576db7fb3cea73726885de0d084630a.png)
它传进来是一个int类型,返回值是new了一个Ingeter对象,然后把i变成了包装类型,所以在自动装包的过程中,调用到了valueOf这个方法我们就可以写如下代码:
![](https://i-blog.csdnimg.cn/blog_migrate/85856ad13b7773f8fe0e9b157d3d6b90.png)
如上图所示:在代码上去调用valueOf方法就是显示装箱的操作。
拆箱:拆箱的定义是将引用数据类型(包装类型)变成基本数据类型就叫做拆箱,同样的,拆箱也可以自动拆箱和显示拆箱。如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/914a54c74e4013254fbb4f331610045c.png)
这个就是自动拆箱,因为具体实现的包装类中哪一个方法在代码层次上没有显示出来。同样的,我们可以看一下Java汇编中在拆箱的操作是调用包装类中的哪一个方法的,是intValue方法,如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/a9b300535e817daf9ed8bfbc94ad52cd.png)
在包装类中,可以看到有很多的方法,intValue,longValue....所以我们可以把要拆的数据拆包成别的类型,如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/0c0c8ce6f2c0cf126f9274a8a4533b5b.png)
我们可以把Ingter包装类显示拆箱,也可以显示拆箱成别的类型。
泛型
泛型:就是把类型作为参数传递,可以存放指定类型的数据,首先,不看泛型,可以想一下,是否有这样的一个数组,这个数组可以存放各种类型的数据,int,double,String等等,在获取数据的时候给定一个下标,就可以返回这个值,其实是不可以的,如果我们给定一个object类型的数组,虽然object类是所有类的父类,这个时候虽然存储数据是可以搞定的,但是当我们获取数据的时候,它不知道我们要获取的数据 是什么类型,就需要我们在这个数组中查找一下看这个存放的数据是什么类型的,然后再进行强转用对应的类型去接受这个数据,但是显然这样做不到是自动的获取。
![](https://i-blog.csdnimg.cn/blog_migrate/0de1eba49c145643e4ef6b523f07ac27.png)
<T>表示当前类是一个泛型类,T是一个占位符。
定义一个泛型就是再类名后边加一个<>,然后再实例化对象的时候可以指定这个数组中可以存放某种类型,<>中只能是引用类型,不能是基本数据类型。如上图,这个时候就可以放指定的Integer类型的数据。(后边<>中可以不写参数类型)
![](https://i-blog.csdnimg.cn/blog_migrate/408f2a36869a03aa3e2a51ee2178c62a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/947a978cd3bd64de0bd9b3a4f09aa0f8.png)
我们可以看到,当指定这个数组的时候,就可以相对应的数据类型,(在用int接受的时候涉及到自动给拆箱) 同时在获取数据的时候自动帮我们进行数据转换,(因为指定了这个数组存放的是什么类型,这个时候就不用强制类型转换)
![](https://i-blog.csdnimg.cn/blog_migrate/df5913c2bc787d4cd9148fc157e2fbfd.png)
同样的编译器会帮我们检查这个类型是否匹配,如上图,这个报错就是因为类型不匹配。
所以泛型的作用:在存储数据的时候可以帮助我们进行类型的检查,在获取元素的时候,可以帮我峨嵋你进行类型转换。(是相对于编译时期的,在运行时期没有泛型这个概念)
规定:泛型中,不能实例化一个数组。
Java是如何编译泛型的?
擦除机制:这里是指在运行的时候将<>中的参数类型擦除成object类,既然泛型是在编译时期才有的概念,那它存在的意义就是检查我们存放的数据类型是否能一致并且自自动进行类型转换。如果运行起来,运行是在jvm中运行的,也就是说在jvm上是没有泛型机制的,它会将参数的类型全部擦除为object类。
![](https://i-blog.csdnimg.cn/blog_migrate/833cdff3758ec66016a9496392c8211f.png)
其实public T[] obj = (T[])new Object[3];这种写法不是准确的,正确的应该是new一个object类型的数组,因为java不允许new一个具体类型的数组,所以只能设定一个object类型的数组然后当在方法中用到的时候将object类型强转成<>中的类型。
泛型的上界
![](https://i-blog.csdnimg.cn/blog_migrate/77e160c64cf6fab470cfac70d6b84763.png)
首先上述代码是问题的提出:如果要去比较一个E类型的数组,为什么那里会报错。
因为E类型是一个引用类型,引用类型是不能用大于号小于号去比较的,那应该用comparTo或者compare来比较,但是这个地方没有实现comparable接口,就不能去比较。
![](https://i-blog.csdnimg.cn/blog_migrate/0801094ae8ec884bbadac8ac0631231b.png)
此时,上图代码就是E的上界,此时的E的类型只能是实现了这个接口的类型。
![](https://i-blog.csdnimg.cn/blog_migrate/fb0dd3ffeb60d031d24640315a841a55.png)
如上图,此时再用泛型E一定是有边界的,如果没有边界就会被擦除成object类,当再指定<>中的类型的时候,这个引用类型一定得是实现了comparable这个接口的类型,这个就是泛型的上界,如果它是一个接口,下面指定类型的时候一定得是实现了这个接口的,如果是一个类,(例如:Number)一定是Number本身或者是Number的子类。如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/76032dc27479804b199059299a6ea866.png)
泛型方法
![](https://i-blog.csdnimg.cn/blog_migrate/48df919af84ac43debcf66dc550a1b65.png)
有泛型类,也有泛型方法,在方法名之前要指定返回的类型,在下面调用的时候也是在方法之前写返回的类型但是这个返回类型可以不写,在方法传参的时候方法会根据传的参数的类型去判断是什么类型的参数。现在的这个方法每次调用都需要去实例化一个对象。
所以可以写成静态的泛型方法:
![](https://i-blog.csdnimg.cn/blog_migrate/305bfba462b982928678e7876a32cbfb.png)
再去调用方法的时候就是类名.方法 的方式来调用泛型方法。