数据结构前置知识(下)

[复制链接]
发表于 2026-1-12 06:32:54 | 显示全部楼层 |阅读模式
1. 包装类

        Java为了让根本数据范例也可以大概继续Object,因此给每个根本数据范例提供了包装类,
如许就可以平静常的引用数据范例一样使用了,而且也可以应用在泛型上(后续讲)
根本数据范例包装类
byteByte
shortShort
intInterger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
除了 Integer 和 Character , 别的根本范例的包装类都是首字母大写.
        1.1 装箱和拆箱

        装包,装箱: 把根本数据范例给到引用数据范例
  1. // 装包: 把基本数据类型给到引用数据类型
  2. public static void main(String[] args) {
  3.         Integer a = 10;
  4.         int i = 99;
  5.         Integer b = i;//这俩个都是自动装包,隐式装包
  6.         System.out.println(a);
  7.         System.out.println(b);
  8.         Integer aa = Integer.valueOf(10);//这个是显示装包,装箱      
  9.     }
  10. //
  11. 10
  12. 99
复制代码
 Interger a = 10;  a是Interger引用数据范例
 int i = 99;  i是根本数据范例
Integer b = i;  我们把根本数据范例变量放在引用数据范例变量内里
以上完成了隐式装包使用
Integer aa = Integer.valueOf(10); 这个是表现装包
总结: 
引用数据范例 变量名 = 根本数据范例变量/传数值; 隐式装包
引用数据范例 变量名 = 引用数据范例.valueOf(根本数据范例变量/传数值); 表现装包

       拆箱,拆包: 把引用数据范例给到根本数据范例
  1.     public static void main(String[] args) {
  2.         //拆箱
  3.         Integer a = 10;//隐式拆箱
  4.         int i = a ;//把引用数据类型->基本数据类型(一般俩种都用到valueOf方法)
  5.         System.out.println(i);
  6.         //显示拆箱
  7.         int aa = a.intValue();
  8.         double d = a.doubleValue();//显示拆箱
  9.         System.out.println(aa);
  10.         System.out.println(d);
  11.     }
  12. //
  13. 10
  14. 10
  15. 10.0
复制代码
Integer a = 10; a是引用数据范例
int i = a ; 这个是引用数据范例变量赋值给根本数据范例,完成了拆包过程
上面是隐式拆包
int aa = a.intValue(); 这个是表现拆包
总:
根本数据范例 变量名 = 引用数据范例变量名; 隐式拆包
根本数据范例 变量名 = 引用数据范例变量名.根本数据范例Value() 表现拆包
 再来看看这个代码
  1.     public static void main(String[] args) {
  2.         Integer a = 100;
  3.         Integer b = 100;
  4.         System.out.println(a == b);//[-128,127]
  5.         Integer a1 = 200;
  6.         Integer b1 = 200;//源码里面,如果超过127则会new一个新的对象
  7.         System.out.println(a1 == b1);
  8.     }
  9. //
  10. true
  11. false
复制代码
从这个代码我们可以看出,引用数据范例一旦凌驾了它所表现的范围就会new一个新的对象,比如上面这个例子Integer的范围是[-128,127],因此200凌驾了它的范围,会new出一个新的对象

2. 泛型

        所谓泛型就是实用于很多范例,从代码来说就是对范例实现了参数化
        2.1 引出泛型

        我们来看这么一个例子:实现一个类,类中包罗一个数构成员,使得数组中可以存放任何范例的数据,也可以根据成员方法返回数组中某个下标的值?
  1. class Myarray1 {
  2.     //TODO 这个是尝试的法一
  3.     public Object[] array = new Object[10];
  4.     //存放数据
  5.     public void setValue(int pos,Object val) {
  6.         array[pos] = val;
  7.     }
  8.     //获取数据
  9.     public Object getValue(int pos) {
  10.         return array[pos];
  11.     }
  12. }
复制代码
既然这个数组可以存放任何范例,那么我们就可以New一个Object范例的数组,我们又须要存放和得到数组内里的 数据,那么我们就要为数组提供set和get方法.如许写固然感觉没标题,但是当我们在main内里创建这个对象而且调用set和get方法的时间就出现了标题

由于getValue返回的范例是个Object的范例,我们不能直接把父类范例放在子类范例内里,因此我们须要向下转型:
  1. String str = (String) myarray.getValue(1);
复制代码
 但是,假如每次放进去什么数据,拿出来的时间我们还须要对它举行对应范例的向下转型就太过于贫苦了.以是我们就引出了泛型,把数据范例当成参数传已往,就完成了数据范例不匹配的标题.我们来看以下代码:
  1. class Myarray<T> {//<T>表示当前的类是个泛型类,它只是一个占位符
  2.     //TODO 这个是法二,用的泛型
  3.     //泛型表示我可以放进去的类型是我自己指定的类型
  4.     public Object[] array = new Object[10];
  5. //    public T[] a = new T[10];//泛型是编译时期存在的,当程序运行起来到JVM之后,就没有泛型的概念了,而对于数组而言,创建出来就需要是一个确定的类型
  6.     //而且,我们无法确保Object类型有没有无参的构造方法
  7.     //泛型在编译的时候是通过擦除机制,擦成了Object
  8.     //,2:54:14
  9.     public void setValue(int pos,T val) {
  10.         array[pos] = val;
  11.     }
  12.     //获取数据
  13.     public T getValue(int pos) {
  14.         return (T)array[pos];//把返回的类型,强转为指定类型
  15.     }
  16. }
复制代码

我们来分析以下这个代码
class Myarray<T> 这内里的<T>表现当前的类是个泛型类,它只是一个占位符
然后内里全部扳连到值的范例的时间,我们都用这个占位符表现
return (T)array[pos];这个代码就是把Object范例的数组范例的强转为指定范例的值,指定范例就是在main函数内里 <Integer>这内里的范例.然后我们可以看出,在后续我们getValue的时间就不须要强转了,就相称于在创建这个对象的时间我们已经从<Integer>这里晓得了这个就是个Integer范例的对象.我们须要传入什么数据范例的值,我们就创建对象的时间<>在这内里告诉编译器,我们要的是什么范例的数据,后续假如放入其他范例就会报错了.

以是,泛型的告急目的:就是指定当前的容器,要持有什么范例的对象。让编译器去做查抄。此时,就须要把范例,作为参数通报。须要什么范例,就传入什么范例
 
        2.2 泛型的语法

        class 泛型名称 <范例参数列表> {   }
        class 泛型名称<T1,T2,T3...>
        class 泛型名称<范例参数列表>extends 继续类 {  }
        class 泛型名称<T1,T2,T3...>extends parentClass<T>{}
此中,<T>代表占位符,表现当前的类是一个泛型类
 【规范】范例形参一样平常使用一个大写字母表现,常用的名称有相识即可)
E 表现 Element
K 表现 Key
V 表现 Value
N 表现 Number T 表现 Type
S, U, V 等等 - 第二、第三、第四个范例
 泛型类的使用:
泛型类名<引用数据范例> 对象名 = new 泛型类名<引用数据范例>();
         2.3 泛型的上界

我们通过一个例子来阐明一下
  1. class Person {
  2. }
  3. class Stundent extends Person  {
  4. }
  5. //TODO T 一定是Number 或者Number的子类
  6. class  TestGneric <T extends Number> {
  7. }
  8. //一定是Person或者Person的子类
  9. class TestGneric2 <T extends Person> {
  10. }
  11. public class Test2 {
  12.     public static void main(String[] args) {
  13.         //TODO 泛型的上界
  14.         TestGneric<Number> testGneric = new TestGneric<>();
  15.         TestGneric<Integer> testGneric1 = new TestGneric<>();
  16. //        TestGneric<String> testGneric2 = new TestGneric<String>();
  17.         TestGneric2<Stundent> testGneric2 = new TestGneric2<>();
  18. //一定是Person或者Person的子类
  19.         TestGneric2<Person> testGneric21 = new TestGneric2<>();
  20.     }
  21. }
复制代码
class TestGneric <T extends Number>{}这个类表现的泛型类, T肯定是Number大概Number的子类
这个就阐明白我们可以通过extends关键字,限定我们使用的泛型的范例,我们在main内里创建它的对象,我们在<>内里的的范例只能是Number大概Number的子类,我们不能放其他引用范例.

然后我们自己实现一个父子类关系来实行,Student继续自Person类,我们限定TestGneric2的泛型范例只能是Person大概是Person的子类 class TestGneric2 <T extends Person>;
否则就会报错.
           2.4 泛型方法

既然我们学会了写泛型类,那么我们也要学会写泛型方法
        我们先看语法:
方法限定符 <范例参数列表> 返回值范例 方法名称(形参列表) {...}
看个例子:
  1. 写一个泛型类,求一个数组中的最大值
复制代码

关于这个代码为什么报错,可以去看看我java分类,类和对象抽象类那一块,我简朴表明一下,我们引用数据范例是必须是实现comparable接谈锋可以大概指定对什么举行比力,不可以大概直接举行比力.
因此我们须要举行修改
  1. class Alg2<T extends Comparable<T>> {//把指定的类型放在类上
  2.     public T findMaxValue(T[] array) {
  3.         T max = array[0];
  4.         for (int i = 0; i < array.length; i++) {
  5.             if (max.compareTo(array[i]) < 0) {
  6.                 max = array[i];
  7.             }
  8.         }
  9.         return max;
  10.     }
  11. }
复制代码
在这个代码中,我们把Alg2限定为一个实现了Compareable接口的类大概其子类上

我们来观察一下Integer的源码:
 它是实现了Comparable接口的
Comparable自己是个泛型接口
因此上面代码可以找出最大值:8
但是我们任然用的是泛型类,下面我们来用法二
  1. class Alg3 {
  2.     public<T extends Comparable<T>> T findMaxValue(T[] array) {//把指定的类型放在方法上
  3.         T max = array[0];
  4.         for (int i = 0; i < array.length; i++) {
  5.             if (max.compareTo(array[i]) < 0) {
  6.                 max = array[i];
  7.             }
  8.         }
  9.         return max;
  10.     }
  11. }
复制代码
这个玩意就是我们在平凡类内里写一个泛型方法我们限定T是要实现Comparable 接口的范例
访问修饰限定符<T extends 接口<T>> T 方法名 (T 参数) {
}  
 

我们在main内里创建 这个对象,而且创建你想比力的实现了Comparable接口的引用范例数组.然后我们把数组当作参数传入方法内里举行盘算.我们根据传进去实参的传值来推导此时的范例,缩写:System.out.println(alg3.findMaxValue(integers));//这个直接根据你传进去的参数来预判出是什么范例,完备写法System.out.println(alg3.<Integer>findMaxValue(integers));
我们写一个不须要实例化对象的静态方法
  1. class Alg4 {
  2.     public static <T extends Comparable<T>> T findMaxValue(T[] array) {//把指定的类型放在方法上
  3.         T max = array[0];
  4.         for (int i = 0; i < array.length; i++) {
  5.             if (max.compareTo(array[i]) < 0) {
  6.                 max = array[i];
  7.             }
  8.         }
  9.         return max;
  10.     }
  11. }
  12.    public class Test {
  13.         public static void main(String[] args) {
  14.             Integer[] integers = {1,2,3,4,6,8};
  15.             System.out.println(Alg4.findMaxValue(integers));
  16.             System.out.println(Alg4.<Integer>findMaxValue(integers));
  17.         }
  18. }
复制代码
静态的方法,我们直接用对象名来举行调用即可,其他用法和上述一样.
                2.5 擦除机制

我们学了这么多泛型的东西,那么我们来聊一下它底层是怎么实现的,
通过下令:javap -c 检察字节码文件,全部的T都是Object

在编译过程中,我们把全部的T更换成Object,这种机制就是擦除机制
然后,我们提出一个标题,我们在创建泛型的时间我们创建的范例为什么是Object范例的,为什么不能直接用泛型创建数组

由于泛型是通过范例擦除实现的,这意味着在运行时间泛型的详细范例会被擦除,编译器无法确定T的详细数据范例,而创建数组,我们是须要知道详细创建范例是什么的.详细硬是要实例化泛型数组就要用到反射机制了(自行相识)


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表