⭐️小剧场⭐️
下面输出为true的有几个?(答案及解析在结尾)
public class Demo2 { public static void main(String[] args) { Integer a=10; Integer b=10; Integer c=321; Integer d=321; Integer e=new Integer(10); Integer f=new Integer(321); System.out.println(a==b); System.out.println(c==d); System.out.println(a==e); System.out.println(a.equals(e)); System.out.println(d.equals(f)); } }
🌞 1.为什么需要包装类?
众所周知,我们Java语言的特性是面向对象。所以在Java中任何东西都是以类而存在的,但是我们的基本数据类型,却不属于类。在一些应用的场景下,它限制了我们的使用。比如我们无法使用Object里的方法呀(因为任何类都继承自Object)。无法参与转型,泛型,反射等使用。Java的开发人员为了弥补这个缺陷,于是为我们的八大数据类型各种设计了一个包装类,将基本数据类型和一些方法包封装起来成为保证类,下面是八大基本数据类型对应的包装类。除了int和char其他都是将头字母变为大写即可,其中我们用的最多的是Integer。
基本数据类型 |
对应的包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
flort | Flort |
double | Double |
char | Character |
boolean | Boolean |
🌞 2.什么是装箱与拆箱?
在JavaSE5版本前,想要生成一个Integer对象,我们需要像大部分对象通过new调用构造方法来创建(手动实例化方法)
Integer a=new Integer(10);
之后为了简化我们的书写,包装类对象的声明可以像基本数据类型一样进行直接的赋值使用,像下面即可(自动装箱方法)
Integer a=10;
在我们传入对应的基本数据类型后得到一个包装类对象,这个过程就是装箱,所以装箱简略而言就是将基本数据类型转化成引用类型。那什么是拆箱呢?和装箱相对而言,那拆箱就是将引用类型转化成基本数据类型
Integer a=10;//装箱 int x=a;//拆箱
装箱:基本数据类型转化为引用类型的过程
拆箱:引用类型转化为基本数据类型的过程
🌞 3.包装类的使用场景(重点)
光明白包装类的拆箱与装箱却不了解使用场景,那我们学习了就毫无意义。下面为大家介绍一些常见的使用场景
🐱 1.字符串与基本数据类型的转换
在我们学习过程中,肯定遇到过这样一种情况,有一个纯数字的字符串,想将它变为int类型,却发现是不支持的,就像这样
String a="1234567"; int b=a;(错误的,Java是不支持的)
但是Integer提供却提供了将字符串转化为Integer的方法valueof()。在这个过程中是先将String转化为Integer,然后再拆箱成int类型,就像这样
String a="1234567"; int b= Integer.valueOf(a);
但其实我们将String转化为int时更常用的是parseInt()方法,它是将String当做一个十进制的整数解析。
String a="1234567"; int b= Integer.parseInt(a);
🌼总结:之所以要先讲valueof()是因为它先返回的是我们的包装类对象再变为基本数据类型,更加直观地体现出拆箱的过程。平时使用时还是推荐大家用parseInt()方法。这里只是以Integer为例。String也可以转换为其他基本数据类型,希望大家去了解和尝试。说不定哪天就遇上了需要转化的场景。
🐱 2.简化对基本数据类型的操作(进制转换)
我们都知道我们平时的整数都是十进制的,在某些情况下,比如做算法时,它可能需要我们转化为二进制、八进制、十六进制等等。有的同学居然去写一个非常复杂方法进行转换,这个问题Integer早就为我们想到了,提供了非常方便的方法。
public class Demo2 { public static void main(String[] args) { int a=100; System.out.println(Integer.toBinaryString(a));//1100100 二进制 System.out.println(Integer.toOctalString(a));//144 八进制 System.out.println(Integer.toHexString(a));//64 十六进制 } }
这时候肯定有人不服,你这些二八十六都是常见的进制,万一它要我转化什么三进制四进制五进制咋办?你还真别说,Integer还真不怕你杠,在Integer中重写了一个两参的toString方法。
参数i是你需要转化的数,参数radix是你需要转化的进制。我们直接来尝试使用一下
public static void main(String[] args) { int a=100; System.out.println(Integer.toString(a, 3)); //10201 将a转化为三进制 System.out.println(Integer.toString(a,4));//1210 将a转化为四进制 System.out.println(Integer.toString(a,5));//400 将a转化为五进制 }
🌼总结:到这你肯定已经感受到了包装类的强大了吧,它其实还有更多强大的功能,大家可以自己去jdk的文档了解。其他包装类的API使用较少,如果大家感兴趣的话也可以去了解一下。如果哪天能用上,那它一定能帮你解决不少的麻烦。
🌞 4.Integer的内存复用(常量池)
☁️1.自动装箱的情况
我们都知道有一个字符串常量池这个东西,它以前在我们的方法区里,后面搬家去了堆上。Integer其实也有一个缓存常量池(其实包装类都有),但是这个常量池在哪我也不知道哈哈哈。在Integer类加载时,这个常量池就会先将-128~127之间的数据缓存进常量池中。当你对Integer自动装箱赋值(还有手动实例的情况)的值在这个区间时,它会自动从区间取出来引用给你。当你不在这个区间时,它会去堆上开辟新的空间来实例化对象。同样以例子说明
public static void main(String[] args) { Integer a=10; Integer b=10; Integer c=129; Integer d=129; System.out.println(a==b);//true System.out.println(c==d);//false }
答案为true的解析:这样的答案在我们的意料之中,当你使用自动装箱的方式创建Integer对象时,会先判断你的值会否在-128~127的区间,如果在的话会直接将地址的值传回给你。这里a和b的值都在这个区间且相等,所以收到的地址是相等的,而==刚好是判断地址的所以第一个答案为true。
答案为false的解析:我们以同样的步骤分析,我们判断到c和d的值不在缓冲常量池的区间,这时候c和d就都要去堆上通过new实例化对象了。一人一片地址,==比较地址,两者地址不同当然返回false啦。
☁️2.手动实例化情况
之所以还要写这个是怕很多小伙伴忘记了或者分不清自动装箱和手动实例化的情况:
public static void main(String[] args) { Integer a=10; //自动装箱得到Integer对象 Integer b=new Integer(10); //手动实例化Integer对象 }
如果我们结合上面两种答案的分析我们就能知道,当你自动装箱得到Integer时,它会去判断你的值是否在常量池中。在?ok那我把常量池对应的值地址给你,不在?那你老老实实去堆上new对象吧。当你直接通过new实例化对象时,那它可不管你的值是多少了,只要你敢new那就一定会在堆上重新开辟一片空间(压根不管你常量池)。
☁️3.Integer的正确比较方式
其实Integer的比较有点类似于字符串。首先我们要知道==是比较地址,equals是比较字面值是否相等(别忘了每个类都有equals方法)。所以当你只是想比较值两个Integer的值是否相等时,那你就应该使用equals,就像下面这个例子一样
public static void main(String[] args) { Integer a=10; Integer b=new Integer(10); System.out.println(a==b);//false System.out.println(a.equals(b));//true }
🌲警告: 我们一般对Integer比较时都是比较值,很少有比地址的情况,但大多数我们都习惯写==,有时候可能因为这个原因获得错误的结果从而让你所有的代码都走向一个错误的结果。所以记住:对包装类(主要是Integer)的比较我们通常都使用equals而不是==。
🌞 5.小剧场答案解析
public class Demo2 { public static void main(String[] args) { Integer a=10; //自动装箱情况 Integer b=10; //自动装箱情况 Integer c=321; //手动初始化情况 Integer d=321; //手动初始化情况 Integer e=new Integer(10); Integer f=new Integer(321); System.out.println(a==b); //a和b都在-128~127区间且值相等,则a和b指向常量池同一个地址 System.out.println(c==d); //a和b不在-128~127区间,虽然值相等,但还是各自new了个地址 System.out.println(a==e); //a指向了常量池区间,e去堆上自己new了空间,两个地址不相等 System.out.println(a.equals(e)); //上面说了两个a和e地址不等,但值是相等的 System.out.println(d.equals(f)); //同理于上,d和f地址不等但值相等 } }
轻松判断出1,4,5是返回的true,则返回true的结果有三个。