聊一聊让我蒙蔽一晚上的各种常量池
在写之前我们先来看几个问题,假如你对这些问题已经很懂了的话,那大可不用看这篇文章,如果不大懂的话,那么可以看看我的想法。
问题1:
public static void main(String[] args){String t1 = new String("1");System.out.println(t1 == t2);t1.intern();String t2 = "1";String t3 = new String("2") + new String("2");t3.intern();String t4 = "22";System.out.println(t3 == t4);
}
答案输出:
JDK1.6是 false false
JDK1.7是 false true;
问题2(把问题1的语句调换一下位置)
public static void main(String[] args){String t1 = new String("2");System.out.println(t1 == t2);String t2 = "2";t1.intern();String t4 = "22";String t3 = new String("2") + new String("2");t3.intern();
}System.out.println(t3 == t4);
答案输出:
false false
对于这两个问题,看了几个人的博客,可谓百花齐放,越看越懵逼
问题3
public static void main(String[] args){Integer a = 1;Integer b = 2;Integer e = 321;Integer c = 3;Integer d = 3;System.out.println(c == d);Integer f = 321;Long g = 3L;System.out.println(c == (a + b));System.out.Println(e == f);System.out.println(g == (a + b));System.out.println(c.equals(a+b));
}System.out.println(g.equals(a + b));
答案输出:
truefalsetruetruetruefalse
问题4:
运行时常量池与字符串常量池是什么关系?包含?
在解决问题之前,我们先来简单了解一些常量池的一些知识点(大部分来源于周志明的深入Java虚拟机这本书)。
JVM中的几种常量池
1.class文件常量池
在Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。
这里简单解释下字面量和符号引用
字面量
字面量类似与我们平常说的常量,主要包括:
● 文本字符串:就是我们在代码中能够看到的字符串,例如String a = “aa”。其中”aa”就是字面量。
● 被final修饰的变量。
符号引用
主要包括以下常量:
● 类和接口和全限定名:例如对于String这个类,它的全限定名就是java/lang/String。
● 字段的名称和描述符:所谓字段就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量。
● 方法的名称和描述符。所谓描述符就相当于方法的参数类型+返回值类型。
2.运行时常量池
我们上面说的class文件中的常量池,它会在类加载后进入方法区中的运行时常量池。并且需要的注意的是,运行时常量池是全局共享的,多个类共用一个运行时常量池。并且class文件中常量池多个相同的字符串在运行时常量池只会存在一份。
注意运行时常量池存在于方法区中。
3.字符串常量池
看名字我们就可以知道字符串常量池会用来存放字符串,也就是说常量池中的文本字符串会在类加载时进入字符串常量池。
那字符串常量池和运行时常量池是什么关系呢?上面我们说常量池中的字面量会在类加载后进入运行时常量池,其中字面量中有包括文本字符串,显然从这段文字我们可以知道字符串常量池存在于运行时常量池中。也就存在于方法区中。
不过在周志明那本深入java虚拟机中有说到,到了JDK1.7时,字符串常量池就被移出了方法区,转移到了堆里了。
那么我们可以推断,到了JDK1.7以及之后的版本中,运行时常量池并没有包含字符串常量池,运行时常量池存在于方法区中,而字符串常量池存在于堆中。
说了这么多,现在我们开始来解决上面提出了问题。
解决问题
问题1:
public static void main(String[] args){String t1 = new String("1");System.out.println(t1 == t2);t1.intern();String t2 = "1";String t3 = new String("2") + new String("2");t3.intern();String t4 = "22";System.out.println(t3 == t4);
}
答案输出:
JDK1.6是 false false。JDK1.7是 false true;
在解决这个问题之前,我们先来看另外一道面试中经常会问到的问题。
String t = new String("tt");
假如程序中只有这样一行代码,那么这行代码创建了几个对象?
我们上面说过,”tt”属于字面量,那么它会在类加载之后存在于字符串常量池中,也就是说,在 String t = new String(“tt”)这句代码执行之前,字符串常量池就已经创建了”tt”这个字符串对象了,我们都知道,new这个关键字会在堆中创建一个对象。
所以,这段代码创建了两个对象。一个在堆中,一个在字符串常量池中。
那么下面这段代码又是创建了几个对象呢?
String t1 = new String("tt");
String t2 = new String("tt");
答是这段代码创建了三个对象,我们上面说了,字符串常量池只会保存一份内容相同的字符串。也就是说,在这两句代码执行之前,字符串常量池就已经创建了内容为”tt”的对象了。这两句代码执行之后,又在堆中创建了两个,所以一共创建了三个。
那么下面这段代码又是创建了几个对象?
String t = "tt";
答是1个,在这段代码执行之前,字符串常量池已经创建了一个”tt”的对象,但由于这行代码并非用new的方法,所以虚拟机会在字符串常量池中寻找是否有内容为”tt”的字符串对象,如果有,则直接返回这个字符串的引用,所以最终结果只创建了一个对象。
回到我们的问题,在这里我们先解释下String 的intern方法。
例如我们调用了t.intern()。
在JDK1.6的时候,调用了这个方法之后,虚拟机会在字符串常量池在查找是否有内容与”tt”相等的对象,如果有,则返回这个对象,如果没有,则会在字符串常量池中添加这个对象。注意,是把这个对象添加到字符串常量池。
到了JDK1.7之后,如果调用了intern这个方法,虚拟机会在字符串常量池在查找是否有内容与”tt”相等的对象,如果有,则返回这个对象,如果没有。则会在堆中把这个对象的引用复制添加到字符串常量池中。注意,这个时候添加的是对象在堆中的引用。
现在开始来分析问题中的代码
t1 = new String(“1”)。
这句代码执行之前,字符串常量池中已经有”t”这个对象,执行之后会在堆中也创建一个”t”的对象,此时t1指向的是堆中的对象。
t1.intern();
这句代码执行之后,会在字符串常量池寻早内容为”t”的对象,字符串常量池已经存在这个对象了,把这个对象返回(不过返回之后并没有变量来接收)。
t2 = “1”。
这句执行后会在字符串常量池查找内容为”t”的对象,字符串常量池已经有这个对象了,返回给t2,此时t2指向的是常量池中的对象。
一个是常量池中的对象,一个是在堆中的对象,两者能相等吗?因此
t1 与 t2不相等。
接着下面
t3 = new String(“2”) + new String(“2”);
这段代码调用之前,字符串常量池有一个”2”的对象,执行之后,实际上会调用StringBuilder的append()方法类进行拼接,最后在堆中创建一个”22”的对象,注意,此时字面量并没有”22”这个字符串,也就是说在字符串常量池并没有”22”这个对象。此时t3指向堆中”22”这个对象
t3.intern();
执行这个方法之后
在JDK1.6的时候,它在字符串常量池中并没有找到内容为”22”的对象,所以这个时候会把这个对象添加到字符串常量池,并把这个对象返回(此时并没有变量来接收这个返回的对象)。注意添加的是对象,而并非引用。
t4 = “22”。
这句代码执行后,会返回字符串常量池中内容为”22”对象,此时t4指向的是字符串常量池中的对象。
显然,一个对象在字符串常量池,一个在堆中,两个对象并非是同一个对象,因此在JDK1.6的时候,t3与t4不相等。
但是在JDK1.7的时候
t3.intern()执行之后,由于在字符串常量池在并没有内容为”22”的对象,所以会把堆中该对象的引用赋值到字符串常量池。注意此时字符串常量池保存的是堆中这个对象的引用。
t4 = “22”。
执行这句代码之后,从字符串常量池返回给t4的是堆中对象的引用。此时t4指向的实际上是堆中对象的引用,也就是说,t3和t4指向的是同一个对象。
因此t3与t4相等。
不知道你明白了没有?反正我是搞了好久才明白…
问题2
至于问题2,我就只讲下半部分的代码,上半部分如果你看懂了问题1,那么问题2也差不多自然懂了。
String t3 = new String("2") + new String("2");
String t4 = "22";
t3.intern();
System.out.println(t3 == t4);t3 = new String(“2”) + new String(“2”)。
这段代码调用之前,字符串常量池有一个”2”的对象,执行之后,实际上会调用StringBuilder的append()方法类进行拼接,最后在堆中创建一个”22”的对象。此时t3指向堆中”22”这个对象
t4 = “22”。
这句代码执行之前,字符串常量池已经存在”22”这个对象了,所有直接把这个对象返回给t4,此时t4指向的是字符串常量池中的对象.
所以t3和t4肯定不是同一个对象啊,t3.intern这句几乎可以忽略,不会给t3和t4造成任何影响。
问题3
public static void main(String[] args){Integer a = 1;Integer b = 2;Integer e = 321;Integer c = 3;Integer d = 3;System.out.println(c == d);Integer f = 321;Long g = 3L;System.out.println(c == (a + b));System.out.Println(e == f);System.out.println(g == (a + b));System.out.println(c.equals(a+b));
}System.out.println(g.equals(a + b));
对于这个问题,我简单说一下可能你就懂了。
(1). 内存中有一个java基本类型封装类的常量池。这些类包括
Byte, Short, Integer, Long, Character, Boolean。需要注意的是,Float和Double这两个类并没有对应的常量池。
(2).上面5种整型的包装类也只是在对象数值在-128~127才可以使用这些常量池。
(3). 在周志明的那本虚拟机中有这样一句话:包装类的
“\==”运行符在不遇到算术运算的情况下不会自动拆箱,以及他们的equals()方法不处理数据类型的关系,可以推断出如果遇到“==”两边有算术运算是话就会自动拆箱和进行数据类型转换处理。
(4).Long的equals方法会先判断是否是Long类型。
(5).无论是Integer还是Long,他们的equals方法比较的是数值。
所以:
System.out.println(c == d)。
由于常量池的作用,c与d指向的是同一个对象(注意此时的==比较的是对象,也就是地址,而不是数值)。因此为true
System.out.println(e == f)。
由于321超过了127,因此常量池失去了作用,所以e和f数值虽然相同,但不是同一个对象,以此为false。
System.out.println(c == (a+b))。
此时==两边有算术运算,会进行拆箱,因此此时比较的是数值,而并非对象。因此为true。
System.out.println(c.equals(a+b))
c与a+b的数值相等,为true。
System.out.pirnln(g == (a + b))
由于==两边有算术运算,所以比较的是数值,因此为true。
System.out.println(g.equals(a+b))。
Long类型的equal在比较是时候,会先判断a+b是否为Long类型,显然a+b不是,因此false
问题到此就结束了,以上便是自己的理解,以上如果有不对劲的地方,非常欢迎你的指点。
原文发布时间为:2018-10-23
本文作者:帅地
本文来自云栖社区合作伙伴“Web项目聚集地”,了解相关信息可以关注“Web项目聚集地”。
聊一聊让我蒙蔽一晚上的各种常量池相关推荐
- python 常量池_聊一聊让我蒙蔽一晚上的各种常量池
在写之前我们先来看几个问题,假如你对这些问题已经很懂了的话,那大可不用看这篇文章,如果不大懂的话,那么可以看看我的想法. 问题1: public static void main(String[] a ...
- 聊一聊Java字符串的不可变
点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 前言 在 Java 开发中 String (字符串)对象是我们使用最频繁的对象,也是很重要的 ...
- 一位拖延症患者程序员的自我救赎!
写在前面 关于时间管理.如何做计划.如何提高执行力-等等相关的话题其实很早之前我就想写了,但一直拖着迟迟没有动笔.但是这次我想了又想,不能再拖了. 因为网上经常会看到类似的问题:不少小伙伴说,有时候总 ...
- 四轮面试阿里菜鸟网络 Java 程序员,失败经历记录分享!
>>号外:关注"Java精选"公众号,回复"面试资料",免费领取资料!"Java精选面试题"小程序,3000+ 道面试题在线刷, ...
- 「Java面试题精华集」Java基础知识篇(2022最新版)附PDF版
两个星期前,我和我的好朋友决定做一系列的 Java 知识点常见重要问题的小册.小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标准. 昨天晚上终于把 ...
- ZJOI2017 Day1 四日游
begin 以省选为由,考前停了一星期课,这星期刷水题刷得特别爽-然而还是没有突破400题大关. 考前各种模拟考啊,正睿OI的试题,学长们的题目,感觉模拟考的时候状态都不是特别好,考完了也就算了,该找 ...
- java基础知识点_「Java面试题/知识点精华集」20000+字的Java基础知识篇(2020最新版) !
" 本文已经收录进我的 79K Star 的 Java 开源项目 JavaGuide:https://github.com/Snailclimb/JavaGuide (「Java学习+面试指 ...
- JVM 基础面试题总结
JVM 的主要作用是什么? JVM 就是 Java Virtual Machine(Java虚拟机)的缩写,JVM 屏蔽了与具体操作系统平台相关的信息,使 Java 程序只需生成在 Java 虚拟机上 ...
- c++ 重载 重写_Java | 深入理解方法调用的本质(含重载与重写区别)
前言 对于习惯使用面向对象开发的工程师们来说,重载 & 重写 这两个概念应该不会陌生了.在中 / 低级别面试中,也常常会考察面试者对它们的理解(隐约记得当年在校招面试时遇到过): 网上大多数资 ...
- Java 基本功之(一)入门知识点
转载自https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/basis/Java%E5%9F%BA%E7%A1%80%E7%9F% ...
最新文章
- pycharm 如何将同一项目中不同模块代码分屏显示
- boost::spirit模块实现错误处理的测试程序
- MacOS录制GIF/录屏的工具
- 融资租赁业务的基本知识
- 如何优雅的处理Restful
- matlab与python实现神经网络_Adaline神经网络简单介绍和MATLAB简单实现
- [AT2306]Rearranging(拓扑序)
- 一道装呀(状压)DP
- 7-11 mmh学长的万能日历 (20分)
- python爬虫:爬东方财富网平安银行历史资金流向并写入MySQL
- Linux固态硬盘 设置写入缓存,固态硬盘做缓存如何设置
- 无网络环境安装docker
- 共码未来 | 2022 Google 谷歌开发者大会参会现场记
- python3.7-secrets模块
- Guided Adversarial Attack for Evaluating and Enhancing Adversarial Defenses
- 服务器远程管理简介(ILO,BMC,RSA)
- yaml文件 *.yml 写法简介
- 功能篇------android 实现“摇一摇”功能
- 联想微型计算机开机黑屏什么原因,联想电脑开机后显示屏是黑屏怎么办
- 电脑怎么压缩jpg图片?上传jpg图片过大怎么办?