integer判断是否为空_String和Integer分配内存的分析

对于字符串String的学习一直是迷迷糊糊,但这块知识却也是重点,要想真正的搞懂不仅要清楚的知道Java中的内存模型,也要能准确区分各种创建好的对象的存储位置。在自己看了深入理解Java虚拟机这本书后,结合多篇博客,才感觉稍微入门,用这篇博客总结记录一下自己理解下的字符串常量池。

String

一、关于String类

之前写过关于String类的简单认识—>字符串String类可供参考

二、常量池

关于Java内存模型参照—>Java虚拟机中的内存区域

这里再着重说一下常量池的概念。运行时常量池和字符串常量池之间有什么关系呢?

在Java虚拟机中常见的几种常量池:class文件常量池、运行时常量池、字符串常量池

Class文件常量池:主要运用编译时期产生的字面量和符号引用

其中,字面量类似常量包括String类型的值和final修饰的变量。

运行时常量池:Class文件会在类加载后进入方法区,就称之为运行时常量池

字符串常量池:在jdk1.6时,字符串常量池就存在与方法区中,在jdk1.7版本后,字符串常量被移到了堆中。因此1.7版本后,字符串常量池就没有存在于运行常量池中了。

接下来,让我们看一段代码:

三、代码分析

让我们看看下面一段代码中的问题:

问题一:

16f1b62aabc164750a4eef0149b68b78.png

这里是否相等相信困扰着许多人。在我们判断时,重点是要理解上面的例子中每一种方式对象创建的位置。

直接创建时(例如创建s1)时,如同上面介绍的常量池的概念,将会在编译期间就生成出

“abc”这个字面常量。每次使用直接创建时,至少会产生0个对象,至多产生1个对象。原因是:如果在字符串常量池中已经有了相同的字面量的对象,那么再次创建时将直接引用该对象,反之如果常量池中不存在,就会在常量池中创建一个对象。

第二就是使用String类的new()方法创建(例如s2),此种方式下,至少会产生1个至多会产生2个对象。原因是:使用new方法是一定会在堆上创建一个对象的,另外由于new()的字面量也会在编译时在常量池中创建一份,但如果在创建时,常量池中已经有了相同的字面量,就不会再次创建。

例如以下四种:

466acb9fa0f18fc6df5a78f49aeeab22.png

第三种是字面量直接相加(例如s4)这种情况下,编译器会直接进行优化,将两个字面量相加后的结果作为一个整体进行创建,此时就类似s1的直接创建,只在常量池中寻找是否已经存在相同的字面量,有则引用,没有则创建。

第四种仍然是相加,只是相加的是两个new()出来的String类对象(例如s5、s8其实均是此种方式),在这种方式下,采用的是动态调用,会类似s2的创建重新new一个对象出来,而最终的结果不作为字面常量。

第五种就是使用已有String对象的intern()方法(例如s9),在这种情况下,首先会在常量池中搜索是否已经有于该字面量相同的对象,如果有,则返回该对象,如果没有,则在常量池中重新创建。

接下来,我们针对问题进行分析:

843dafeb24eade54126fe6c88271a50f.png

好,这个明白了那再来看下一个问题:

问题二:

3629b4e39acb7bd9654931f3180ca5ce.png

这个问题事实上还是上面问题的延申,现在来分析一下:

为什么s10和s11不相等呢?intern方法起作用了吗?

首先第一句代码执行,在堆上创建一个对象,s10指向堆上的对象。接着直接在常量池创建s11返回地址给s11。此时对s10调用intern方法,发现常量池中已经有了该字面量,就不再创建,而是返回s10在常量池中的引用地址也即返回s11的引用,但没有接收的变量,如果使用变量接受再比较接受结果的变量和s11,返回值就是true。但在当前情况下s10在堆,s11在常量池,自然不相等。

那么类比下面的代码呢?

6fcd3225071df44fd20b8277f161ea9c.png

为什么此时返回的就是true呢?

原因是,在s12创建好后,常量池中存在“hello”“world”但并不存在“hello world”字面量,在执行intern方法后在常量池中查找,找到返回引用,找不到就创建,jdk1.7以后在常量池中创建后,常量池中保存的是s12的引用,因此当创建s13时,在常量池中查找后,返回的是s12这个引用。两个相同的引用一定是返回true的。故此时intern方法对代码块有影响。

只是交换了intern方法的位置就可能会产生不同的结果。

Integer

像Integer这种包装类,只要创建就会消耗资源,因此,Java对各种包装类在[-128 - 127]之间是会放到常量池中,而超过这个范围就会创建对象。

但是Float和Double类型没有实现常量池技术。

包装类的“==”运行符在不遇到算术运算的情况下不会自动拆箱,以及他们的equals()方法不处理数据类型的关系

Long的equals方法会先判断是否是Long类型

无论是Integer还是Long,他们的equals方法比较的是数值

通过例子来看:

问题三:

be1eb27905783d73f8701d3794fca57a.png

现在逐一来分析:

c==d

因为c、d均在[-128-127]范围内,故在均常量池中。此时没有遇到运算符,不自动拆箱比较的是引用是否是指向同一个对象,而不是数值。

e==f

e、f 均不在[-128-127]范围内,因此在堆上创建对象,此时无论对象的值是否相等,由于不是同一个引用故返回false

c == (a+b)

由于包装类遇到了运算符,就进行了自动拆箱,此时比较的就是对象里面的value属性值是否相等,故返回true

c.equals(a+b)

无论是Integer还是Long,equals方法比较的是数值是否相等,故返回true

f == g

int和Integer相比较也会对包装类自动拆箱,因此比较的还是值的大小,故返回true

h == (a + b)

由于包装类遇到了运算符,就进行了自动拆箱,此时比较的就是对象里面的value属性值是否相等,故返回true

h.equals(a + b)

Long类型的equal在比较时,会先判断a+b是否为Long类型,显然a+b不是,因此false

————————————————

原文链接:https://blog.csdn.net/Moo_Lavender/article/details/103198617

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值