当前位置: 首页 > 科技观察

为什么Java要设计封装类

时间:2023-03-23 11:00:36 科技观察

?转载本文请联系飞天小牛公众号。全文思维导图如下:1.为什么需要包装类?在Java中,万物皆对象,所有的操作都要求以对象的形式来描述。但是除了对象(引用类型)之外,Java中还有八种基本类型都不是对象。那么,要将基本类型转化为对象,最简单的方法就是“将基本类型保存为类的属性”,即包装基本数据类型,这就是包装类的由来。这样,我们先自己实现一个简单的包装类,以包装基本类型int为例://packageclassMyIntpublicclassMyInt{privateintnumber;//基本数据类型publicInt(intnumber){//构造函数,传入基本类型数据类型这个。number=number;}publicintintValue(){//获取包装类中的数据returnthis.number;}}测试这个包装类:publicstaticvoidmain(String[]args){MyInttemp=newInt(100);//100是基本的data类型,将基本数据类型包装成一个对象intresult=temp.intValue();//从对象中获取基本数据类型System.out.println(result);}当然,我们实现的包装类很简单,Java为我们提供了更完善的内置包装类:基本类型对应的包装类(在java.lang包中)byteByteshortShortintIntegerlongLongfloatFloatdoublecharCharacterbooleanBoolean派生出前6个类来自公共超类Number,而Character和Boolean是Object的直接子类。让我们看一下包装类的声明。以Integer为例:它被final修饰,这意味着Java内置的“包装类不能被继承”。2.装箱和拆箱OK,现在我们知道了基本数据类型及其对应的包装类,那么,它们之间的转换操作就称为装箱和拆箱:装箱:将基本数据类型转换为包装类(的构造函数每个包装类都可以接收各自数据类型的变量)拆箱:从包装类中取出包装好的基本类型数据(使用包装类的xxxValue方法)下面以Integer为例,来看看Java是如何实现的built-inpackagingclassunboxes:Integerobj=newInteger(10);//自动装箱inttemp=obj.intValue();//自动拆箱可见一斑,和我们上面自己写的包装类的用法基本一致。其实Integer中这两个方法的底层实现和我们上面写的代码差不多。不知道大家有没有注意到,这个值被声明为final,意思是“一旦构造了wrapper,包裹在里面的值就不允许被改变”。另外需要注意的是,这种形式的代码是“JDK1.5之前”的!!!“JDK1.5之后”,Java设计者为了开发的方便,提供了“自动装箱”和“自动拆箱”的机制。并且可以直接使用封装类的对象进行数学计算。还是以Integer为例,看一下自动拆箱的过程:Integerobj=10;//自动装箱.基本数据类型int->packagingclassIntegerinttemp=obj;//自动拆箱.Integer->intobj++;//直接使用包装类的对象进行数学计算System.out.println(temp*obj);看看你不会,从基本数据类型到包装类的转换不需要像上面那样使用构造函数,直接用=就可以完成;同样,从包装类到基本数据类型的转换不需要我们手动调用包装类的xxxValue方法,直接通过=即可完成拆箱。这就是为什么它们被称为自动的。我们来看看这段代码的反编译文件。底层原理是什么:Integerobj=Integer.valueOf(10);inttemp=obj.intValue();可以看到,自动装箱的底层原理是调用包装类的valueOf方法,自动拆箱底层调用包装类的intValue()方法。3.不简单Integer.valueOf我们已经看到上面自动拆箱的intValue方法的源码了,非常简单。接下来,让我们看一下用于自动装箱的valueOf。其他封装类没什么好说的,但是Integer中的这个方法还是有讲究的:WhatisIntegerCache?点击查看:IntegerCache是??Integer类中的一个静态对象。内部类,结合这两段代码,我们大概可以知道IntegerCache其实是一个“缓存”,它定义了一个buffercache,用于存储Integer类型的数据,“缓存范围是[-128,127]”。回到valueOf的源码:首先会判断int类型的实参i是否在可缓存范围内,如果在,则直接从缓存的IntegerCache中获取对应的Integer对象;如果它不在缓存范围内,它将创建一个新的Integer对象。结合这个特点,我们来看一个题目,两段相似的代码逻辑,却得到完全相反的结果。:publicstaticvoidmain(Stringargs[]){Integera1=127;Integera2=127;System.out.println(a1==a2);//trueIntegerb1=128;Integerb2=128;System.out.println(b1==b2);//false}我们知道==有两种应用场景:对于引用类型,判断内存地址是否相等;对于基本类型,它判断值是否相等。从a1开始,因为它的值在InterCache缓存范围内,所以这个Integer对象会被存入缓存。在创建a2时,由于它的值等于a1,所以直接从缓存中取出值为127的Integer对象供a2使用,也就是说a1和a2的两个Integer对象引用都指向了同一个地址。对于b1和b2来说,由于128不在IntegerCache的缓存范围内,只能老老实实开辟空间,所以b1和b2指向不同的内存地址。显然,由于InterCache缓存机制的存在,在编程的时候可能会让我们感到困惑,所以最好使用.equals方法来比较Integer值是否相等。Integer重写了.equals方法:当然,其他包装类虽然没有缓存机制,但是也会重载.equals方法,根据值判断是否相等。因此得出结论是“使用equals方法比较两个包装类对象的值”。4.Object类可以接收所有数据类型综上所述,有了自动拆箱机制,基本数据类型可以自动转换为包装类,而Object是所有类的父类,也就是说“Object可以接收所有数据类型”(引用类型、基本类型)!!!如果你不相信我,你可以试试。直接用Object类接收一个基本数据类型int是完全可以的。Objectobj=10;inttemp=(整数)obj;解释上面代码中发生了什么。下图很重要。请仔细看一下:5.包装类在集合中的广泛使用其实包装类最常见的用途是在集合中,因为集合不允许存储原始类型的数据,只能存储引用类型的数据。那么如果我们要存储1、2、3等基本类型的数据呢?例如,我们可以声明一个Integer对象的数组列表,如下所示:ArrayListlist=newArrayList<>();添加int类型数据:list.add(3);上面的调用会在底层自动装箱:intn=list.get(i);基本数据类型int将转换为Integer对象并存储在集合中。我们根据一个下标i从这个集合中得到对应的Integer对象,并以基本数据类型int接收:intn=list.get(i);上面的调用会自动拆箱底层:intn=list.get(i).intValue();6.数据类型转换另外,包装类除了在集合中被广泛使用外,还有一个重要的功能,就是提供将String数据转换为基本数据类型的方法。Use几个有代表性的类进行解释:Integer:Double:Boolean:这些方法都被标记为static,也就是说由所有对应的对象共同维护,通过类名可以直接访问方法。例如:Stringstr="10";inttemp=Integer.parseInt(str);//String->intSystem.out.println(temp*2);//20需要特别注意的是:没有将字符串转换为字符的方法,因为String类中已经有charAt()方法可以根据索引获取字符内容。

最新推荐
猜你喜欢