在写之前我们先来看几个问题,假如你对这些问题已经很懂了的话,那大可不用看这篇文章,如果不大懂的话,那么可以看看我的想法。

问题1:

public static void main(String[] args){

String t1 = new String("2");

t1.intern();

String t2 = "2";

System.out.println(t1 == t2);

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");

String t2 = "2";

t1.intern();

System.out.println(t1 == t2);

String t3 = new String("2") + new String("2");

String t4 = "22";

t3.intern();

System.out.println(t3 == t4);

}

答案输出:

false false

对于这两个问题,看了几个人的博客,可谓百花齐放,越看越懵逼

问题3

public static void main(String[] args){

Integer a = 1;

Integer b = 2;

Integer c = 3;

Integer d = 3;

Integer e = 321;

Integer f = 321;

Long g = 3L;

System.out.println(c == d);

System.out.Println(e == f);

System.out.println(c == (a + b));

System.out.println(c.equals(a+b));

System.out.println(g == (a + b));

System.out.println(g.equals(a + b));

}

答案输出:

true

false

true

true

true

false

问题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");

t1.intern();

String t2 = "1";

System.out.println(t1 == t2);

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 c = 3;

Integer d = 3;

Integer e = 321;

Integer f = 321;

Long g = 3L;

System.out.println(c == d);

System.out.Println(e == f);

System.out.println(c == (a + b));

System.out.println(c.equals(a+b));

System.out.println(g == (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

问题到此就结束了,以上便是自己的理解,以上如果有不对劲的地方,非常欢迎你的指点。

完。

关注公众号「苦逼的码农」,获取更多原创文章,后台回复「礼包」送你一份特别的大礼包

python 常量池_聊一聊让我蒙蔽一晚上的各种常量池相关推荐

  1. java 关闭阻塞线程池_如果优雅地关闭ExecutorService提供的java线程池

    每一个线程都会占用系统资源,因此线程池的关闭与清理同样重要,本文介绍我们如何优雅地关闭线程池. 一. ExecutorService中关闭线程池的方法 1. shutdown() 停止接收新任务,原来 ...

  2. mysql多个字符串连接池_使用Coroutine\Channel实现一个简单的MySQL连接池

    Channel通道,类似于go语言的chan,支持多生产者协程和多消费者协程,Swoole底层自动实现了协程的切换和调度 Channel实现原理 通道与PHP的Array类似,仅占用内存,没有其他额外 ...

  3. mysql5.6 线程池_[MySQL5.6] Percona Server 5.6.14的线程池浅析

    Percona的线程池基本上是从Mariadb中引入,其实现思路也比较简单,就是在线程调度器那增加了一组新的回调函数.线程池可以有效改善在大并发下的性能: Thread pool的原理在Percona ...

  4. easyswoole数据库连接池_如何在 Swoole 中优雅的实现 MySQL 连接池

    如何在 Swoole 中优雅的实现 MySQL 连接池 一.为什么需要连接池 ? 数据库连接池指的是程序和数据库之间保持一定数量的连接不断开, 并且各个请求的连接可以相互复用, 减少重复连接数据库带来 ...

  5. hikaridatasource连接池_完美!细数SpringBoot中的那些“连接池”

    推荐阅读: 47天时间,洒热血复习,我成功"挤进"了字节跳动(附Java面试题+学习笔记+算法刷题)​zhuanlan.zhihu.com 拼多多终面竟被"Out&quo ...

  6. python sqlserver api连接池_非常老的话题 SQLSERVER连接池

    非常老的话题 SQLSERVER连接池 写这篇文章不是说要炒冷饭,因为园子里有非常非常多关于SQLSERVER连接池的文章,但是他们说的都是引用MSDN里的解释 或者自己做一些测试试验一下连接池的性能 ...

  7. java string 常量池_用了这么久Java String,你真的懂字符串常量池吗?

    字符串问题可谓是 Java 中经久不衰的问题,尤其是字符串常量池经常作为面试题出现.可即便是看似简单而又经常被提起的问题,还是有好多同学一知半解,看上去懂了,仔细分析起来却又发现不太明白. 本文以 J ...

  8. python 线程池_老程序员的经验分享:Python 从业十年是种什么体验?

    出于某些原因,想记录一下我过去数年使用 Python 的经验和一些感悟.毕竟算是一门把我带入互联网行业的语言,而我近期已经几乎不再写 Py 代码, 做一个记录,也许会对他人起到些微的帮助,也算是纪念与 ...

  9. 运行时常量池_从String的intern()到常量池

    前言 在知乎上遇到一个刚学Java就接触的字符串比较的问题: 通常,根据"==比较的是地址,equals比较的是值"介个定理就能得到结果.但是String有些特殊,通过new St ...

最新文章

  1. 面试之Hashtable和ConcurrentHashMap
  2. NSTimer用法,暂停,继续,初始化
  3. 转载:DIV+CSS有可能遇到的问题
  4. Hibernate Write operations are not allowed in read-only
  5. java8中的Lamba表达式
  6. sql server 隔离级别
  7. 用栈来求解汉诺塔变形问题
  8. minicom 串口信息过长分行显示
  9. 【算法分析与设计】最大连续子序列和问题
  10. Protobuf3教程
  11. 贾跃亭致信债权人:将努力打工还债,请相信我!
  12. Unity URP一分钟实现遮挡透视
  13. Oracle遇到的一些坑
  14. vscode如何关闭Pylint警告或错误提示
  15. L1L2 Regularization的原理
  16. 图解ZooKeeper的典型应用场景(转载)
  17. hdu4556(欧拉函数)
  18. 百度的世界里,AI没有退路
  19. 淘宝的商品中心和类目体系是怎么设计的 | 极客分享第 16 期
  20. maven中无法下载ojdbc

热门文章

  1. Python:代码规范和命名规范
  2. QThread使用方法
  3. QList (链表) QVector (数组)
  4. 浅论园子的人对广告的认识
  5. docker-ce-17.09 容器创建,运行,进入,删除,导入/导出
  6. React 生命周期
  7. 性能测试知多少---性能需求分析
  8. 《健康报》携手健康之路帮助医院医生免费搭建微官网
  9. web服务器 linux+apache+tomcat+mysql+jsp+php 整合安装
  10. 建立mysql服务器连接失败_解决项目启动无法创建到数据库服务器的连接错误