ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java 包装类、泛型与类型擦除

Java 包装类、泛型与类型擦除 文章目录1. 包装类1.1 基本数据类型和其对应的包装类1.2 装箱与拆箱1.3 Integer 缓存机制2.泛型2.1 使用语法2.2 泛型的”本质机制“2.2.1 泛型只存在编译期2.2.2 类型擦除2.3 泛型的上界1. 包装类1.1 基本数据类型和其对应的包装类Java 为了把基本数据类型当作“对象”来使用专门提供的一组类。基本类型包装类byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublecharCharacterbooleanBoolean1.2 装箱与拆箱装箱装包把基本数据类型变成包装类类型的过程叫做装箱装包。装箱又分为自动装箱和手动装箱实际自动装箱底层逻辑还是手动装箱publicclassDemo{publicstaticvoidmain(String[]args){inta100;//手动装箱IntegeriaInteger.valueOf(a);DoubledaDouble.valueOf(a);System.out.println(ia);System.out.println(da);//自动装箱Integeriiaa;Doubledda(double)a;System.out.println(iia);System.out.println(dda);}}拆箱拆包把包装类类型变成基本数据类型的过程就叫做拆箱拆包。拆箱也分为自动拆箱和手动拆箱实际自动拆箱底层逻辑还是手动拆箱publicclassDemo{publicstaticvoidmain(String[]args){Integeria100;//手动拆箱intaia.intValue();doubledia.doubleValue();System.out.println(a);System.out.println(d);//自动拆箱intiiaia;doubledaia.doubleValue();System.out.println(iia);System.out.println(da);}}1.3 Integer 缓存机制publicclassDemo{publicstaticvoidmain(String[]args){Integera100;Integerb100;System.out.println(ab);//trueIntegerc200;Integerd200;System.out.println(cd);//false}}以上代码中涉及的是自动装包结果的原因Integer.valueOf()会缓存 [-128,127]在这个范围内返回同一个对象超出范围新对象我们可以查看 Integer 源码中的valueOf()方法我们可以看出自动装包输入的 i 在其中是个数组的范围我们可以查看到 IntegerCache 这样一个类中设定了low -128,而high最终被h 127所赋予因此得到该缓存数组范围[-128,127]我们再思考返回的数组内容IntegerCache.cache[i (-IntegerCache.low)]我们就拿i 100代入而我们通常比较“值”的方法是使用a.equals(b)2.泛型泛型就是把“类型”当作参数在编译期确定下来的一种机制。其作用是 1、解决“类型不安全”问题2、消除强制类型转换向下转型3、提高代码复用能力2.1 使用语法泛型类列举样例class泛型类名称{//可使用类型参数}classClassNameT1,T2,...,Tn{}class泛型类名称类型形参列表extends继承类/*此处可使用类型参数*/{}classClassNameT1,T2,...,TnextendsParentClassT1{}举例classBoxT{privateTvalue;publicvoidset(Tvalue){this.valuevalue;}publicTget(){returnvalue;}}使用如下BoxStringboxnewBox();box.set(hello);Stringsbox.get();说明T在定义类时只是占位在创建对象时才是真正确定类型泛型方法方法限定符类型形参列表返回值类型 方法名称(形参列表){...}publicstaticTTgetFirst(T[]arr){returnarr[0];}解释T位于返回值之前表示该方法定义了一个泛型类型参数TT表示这个方法返回值的类型是T使用如下IntegerigetFirst(newInteger[]{1,2});StringsgetFirst(newString[]{a,b});泛型接口interfaceMapperT{Tmap(Tinput);}实现方式一实现时确定类型classStringMapperimplementsMapperString{publicStringmap(Stringinput){returninput.toUpperCase();}}实现方式二实现时仍保留泛型classDefaultMapperTimplementsMapperT{publicTmap(Tinput){returninput;}}2.2 泛型的”本质机制“2.2.1 泛型只存在编译期Java 的泛型是”伪泛型“比如ArrayListStringArrayListInteger在 JVM 看来都是ArrayList2.2.2 类型擦除编译过程中将所有的 T 替换为 Object 这种机制称为擦除机制java 的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。ArrayListStringlistnewArrayList();编译后等价于ArrayListlistnewArrayList();只是编译器偷偷加了强制类型检查和转换。因此对于两个问题1、那为什么T[] ts new T[5];是不对的编译的时候替换为Object不是相当于Object[] ts new Object[5]吗2、类型擦除一定是把T变成Object吗问题 一首先 java 编译器不允许这样写原因不是语法而是在于类型安全。因为数组是运行期类型检查的数组在运行期知道自己真实的元素类型。另外泛型是编译期类型检查的在运行期就已经被删除了所以 JVM 运行期并不知道元素类型举例假如T[] ts new T[5];允许classBoxT{T[]tsnewT[5];// 假设合法}BoxStringboxnewBox();Object[]arrbox.ts;// 数组协变合法arr[0]100;// 编译通过首先arr是Object[]放Integer是合理的但box.ts逻辑上应该是String[]而且编译器也完全无法阻止这个错误对于问题二不一定。无边界泛型 - 擦除成 Object因为没有任何约束最安全的上界就是Object如果有上界泛型 - 擦除成”上界类型“方法级的泛型同理举例如下publicTextendsComparableTTmax(Ta,Tb){returna.compareTo(b)0?a:b;}擦除后publicComparablemax(Comparablea,Comparableb){returna.compareTo(b)0?a:b;}另外对于类型擦除还有许多可以了解的地方1.泛型信息不会进入 JVM 的方法签名中也就是说不存在重载2.数组协变泛型不变2.3 泛型的上界泛型上界限制泛型参数”最多能是什么类型“并向编译器承诺他至少具备某个父类或接口的能力。使用语法class泛型类名称类型参数extends类型边界{}这里的extends既表示继承也表示实现接口。举个例子classBoxTextendsNumber{Tvalue;voidprint(){value.intValue();}}如果没有extends Number那么编译器只知道T是ObjectObject没有intValue().但是加上之后就相当于上界告诉了编译器T至少是NumberNumber定义了intValue()以上是我关于Java的笔记分享感谢你读到这里这也是我学习路上的一个小小记录。希望以后回头看时能看到自己的成长~
返回列表