前言

读完这篇文章你可以了解,String对象在虚拟机内存中的存放,intern的作用,这么多String对象的创建到底有什么区别,String 创建的对象有几个!!

正题

先科普几个知识点
1.常量池存放于方法区中

2.jdk1.6 方法区放在永久代(java堆的一部分),jdk1.7 特别将字符串常量池移动到了的堆内存中(使用参数-XX:PermSize 和-XX:MaxPermSize指定大小),jdk1.8放在单独的元空间里面(-XX:MaxMetaspaceSzie设定大小),和堆相独立。所以导致string的intern方法因为以上变化在不同版本会有不同表现。

3.jdk1.6将Hotspot虚拟机使用永久代来实现方法区,因为方法区的内存回收跟堆内存回收其实没什么区别,这样实现可以用垃圾收集器来管理这部分内存,但这样容易导致内存溢出(达到-XX:MaxPermSize)。

JDK1.6,JDK1.7常量池的存放如下都存放于堆内存中

JDK1.8常量池的存放如下


具体可参考:https://blog.csdn.net/zhyhang/article/details/17246223/

知道了常量池在内存中的存放后,我们需要先了解一下 String str=“abc”;和 String str =new String(“abc”);的区别

1.String str=“abc”;

JDK1.6
(1) 当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个String对象,复制这个对象加入常量池,返回常量池中的对象。


(2) 当常量池中存在"abc"这个字符串对象,str指向这个对象的引用;

JDK1.7以上
(1) 当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个新的String对象,将这个对象的引用加入常量池。(跟1.6的区别是常量池不再存放对象,只存放引用。)
(2) 当常量池中存在"abc"这个字符串的引用,str指向这个引用;

2.String str =new String(“abc”);

单纯的在堆内存中new一个String对象,通过StringBuilder 跟StringBuffer 构建的对象也是一样

3.intern方法 (1.7版本,返回常量池中该字符串的引用)

(1) 当常量池中不存在"abc"这个字符串的引用,将这个对象的引用加入常量池,返回这个对象的引用。
(2) 当常量池中存在"abc"这个字符串的引用,返回这个对象的引用;

测试代码如下

String str1 = "计算机";
String str2 = "计算机";
System.out.println("str1==str2:" + (str1 == str2)); String str3 = new String("计算机");
System.out.println("str1==str3:" + (str1 == str3));
System.out.println("str1==str3.intern():" + (str1 == str3.intern()));
System.out.println("str2==str3.intern():" + (str2 == str3.intern())); String str4 = new String("计算机");
System.out.println("str3==str4:" + (str3 == str4));
System.out.println("str3.intern()==str4.intern():" + (str3.intern() == str4.intern())); String str5 = new StringBuilder("软件").append("工程").toString();
System.out.println("str5.intern() == str5:" + (str5.intern() == str5)); String str6 = new String(new StringBuilder("物联网").append("工程").toString());
System.out.println("str6.intern() == str6:" + (str6.intern() == str6)); String str7 = new String("物联网");
System.out.println("str7.intern() == str7:" + (str7.intern() == str7));

JDK1.8输出结果如下:

str1==str2:true
str1==str3:false
str1==str3.intern():true
str2==str3.intern():true
str3==str4:false
str3.intern()==str4.intern():true
str5.intern() == str5:true
str6.intern() == str6:true
str7.intern() == str7:false

这里着重讲解一下为什么str5,str6和str7的结果不一样,

str7直接用new String(“物联网”)创建,"物联网"这字符串在一出现就自动创建成对象存放到常量池中,所以常量池里面存放的是"物联网"字符串的引用,并不是str7创建的对象的引用。

str5是通过StringBuilder构建的 在new StringBuilder(“软件”).append(“工程”).toString方法运行后,"软件工程"这个字符串对象才第一次出现。执行了intern方法后str5才被放到常量池中,此时str5跟str5.intern是同一个对象。

str6是作为对照组出现,这里为了确认StringBuilder 在toString方法执行后会不会把最终字符串放进常量池。很显然并没有,所以str6的intern才会跟str6是同一个对象。同时它也能验证出str7的new String()方式在初始化的时候就会把"物联网"字符串放进常量池中,同理我们可以得出在str5构建的时候常量池里面加入了"软件","工程"这两个字符串(可以自己动手去验证一下!!)。

JDK1.7结果如下

str1==str2:true
str1==str3:false
str1==str3.intern():true
str2==str3.intern():true
str3==str4:false
str3.intern()==str4.intern():true
str5.intern() == str5:true
str6.intern() == str6:true
str7.intern() == str7:false

当然JDK1.6我们也能大致明确,输出结果会如下

str1==str2:true
str1==str3:false
str1==str3.intern():true
str2==str3.intern():true
str3==str4:false
str3.intern()==str4.intern():true
str5.intern() == str5:false
str6.intern() == str6:false
str7.intern() == str7:false

分析一下jdk1.6中 str5.intern() == str5的结果为什么为false,这里可以这么看在软件工程这个对象中构建完成之后,常量池里面其实存放的只有“软件”,"工程"这两个字符串,而"软件工程"这个字符串是存放于堆内存中的。所以JDK1.6执行了intern后会把“软件工程”复制到常量池中,并返回常量池中的对象。常量池中的"软件工程"跟堆内存中的软件工程并不是同一个。

因为没有尝试过,如果jdk1.6有不一致的地方欢迎大家指点。

总结

1.String对象可能会在堆内存中分配一块空间创建一个String对象,在常量池中创建该对象的复制jdk 1.6(或引用jdk 1.7及以上),也可能直接指向常量池(永久带)中的引用(例String str=“xxx”)。还有一种可能是直接在堆内存中分配一块空间创建一个String对象(例 String str=new String(xxx))。

2.intern方法可以看成返回常量池中该字符串对象的引用。如果没有该字符串对象就把这个对象(或引用)加到常量池。

3.jdk1.6跟jdk1.7以上的区别是当常量池中不存在这个字符串,jdk1.6是直接复制对象到常量池,而jdk1.7以上是把对象的引用加入常量池。

4.类似于”abc”这样的字符串,在第一次被使用到(比如String a=”abc”或者String a=new String(“abc”)或者"abc".equals(xxx))后就会被加载到常量池中去。

5.面试问题:

(1)现在当有人问 String str = new String(“abc”);创建了几个对象,常量池有abc字段是1个,常量池没有"abc"字段则是2个。
(2)String str=“abc”;创建了几个对象(如果常量池里面已经有对象了就是0个。如果没有就是1个);
(3)new String(“abc”).intern();创建了几个对象(如果常量池里面已经有该字符串对象了就是1个,如果没有就是两个)

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

原文链接:https://blog.csdn.net/guoxiaolongonly/article/details/80425548

String中intern方法的作用的更多相关文章

  1. java String 中 intern方法的概念

    1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...

  2. Java - 记录String中intern()方法的学习与理解

    intern()方法:把堆中的引用丢入常量池中,然后返回这个引用.当常量池中已经存在这个引用,就直接返回这个引用.(jdk1.8) 由于jdk1.7中将字符串常量池改为存放在堆中,因此intern() ...

  3. 关于对String中intern方法的理解

    在java的String中有个一直被我们忽视了的方法intern方法:它的官方解释是:一个初始时为空的字符串池,它由类 String 私有地维护. 当调用 intern 方法时,如果池已经包含一个等于 ...

  4. String中intern()方法

    intren方法:通俗的讲,是将字符串放入常量池中. new出来的字符串是放在堆中,直接赋值的字符串是放在常量池中的. 对字符串做拼接操作,即做“+”运算,分两种情况 (1)表达式右边是纯字符串常量, ...

  5. String类中intern方法的原理分析

    一,前言 ​ 昨天简单整理了JVM内存分配和String类常用方法,遇到了String中的intern()方法.本来想一并总结起来,但是intern方法还涉及到JDK版本的问题,内容也相对较多,所以今 ...

  6. String的intern方法的使用场景

    在讲intern方法前,我们先简单回顾下Java中常量池的分类. 常量池的分类 Java中常量池可以分为Class常量池.运行时常量池和字符串常量池. 1. Class文件常量池 在Class文件中除 ...

  7. String 的intern() 方法说明

    1.说明 Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 2.源码说明 public native ...

  8. 自己(转)JAVA中toString方法的作用

    JAVA中toString方法的作用 因为它是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”. 它通常只是为了方便输出,比如System.out.print ...

  9. String 的 intern() 方法解析

    一.概述 JDK7 之前和之后的版本,String 的 intern() 方法在实现上存在差异,本文的说明环境是 JDK8,会在文末说明 intern() 方法的版本差异性. intern() 方法是 ...

随机推荐

  1. IDEA调试进入class文件

    今天用IDEA调试时,一直进入class文件,而没有进入java文件. 错误原因 出现这种情况,有可能是Module有多个同名的依赖,调试时程序进入了同名的jar包里面,而不是同名的Module. 如 ...

  2. DI 依赖注入之unity的MVC版本使用Microsoft.Practices.Unity1.2与2.0版本对比

    DI 依赖注入之unity的MVC版本使用Microsoft.Practices.Unity1.2与2.0版本对比 参考:https://www.cnblogs.com/xishuai/p/36702 ...

  3. 连上Microbit板

    我们是在自己电脑上,用Mu编程,最终程序要烧录到Microbi板子的控制芯片中去,还记得这款芯片是哪家公司生产的?是多少位的?呵呵. 因此,我们要把板子和电脑连起来,用常见的一大一小的USB线就可以了 ...

  4. unity的yield

    这里说的是Unity通过StartCoroutine开启IEnumerator协程里的yield相关 1.yield return 0,yield return null 等待下一帧接着执行下面的内容 ...

  5. LeetCode dp专题

    1. 动态规划的适用场景 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法. 2. 动态规划的基本思想 动态规划背后的基本思想非常简单.大致上,若要解一个给 ...

  6. Spring Cloud OkHttp设计原理

    Spring Cloud 框架最底层核心的组件就是服务调用方式,一般Spring Cloud框架采用的是HTTP的调用框架,本文将在 Spring Cloud应用场景下,介绍组件OkHttp3的设计原 ...

  7. malloc,free,calloc,realloc函数

    malloc函数 原型:extern void* malloc(unsigned int size): 功能:动态分配内存: 注意:size仅仅为申请内存字节大小,与申请内存块中存储的数据类型无关,故 ...

  8. Angular的Observable可观察对象(转)

    原文:https://blog.csdn.net/qq_34414916/article/details/85194098 Observable 在开始讲服务之前,我们先来看一下一个新东西——Obse ...

  9. Java的常用API之Date类简介

    Data类 java.util.Date:表示日期和时间的类类Date 表示特定的瞬间,精确到毫秒.毫秒:千分之一秒 1000毫秒=1秒 把日期转换成毫秒: 当前的日期:2019-07-18 时间原点 ...

  10. Java中守护线程的总结 thread.setDaemon(true)

    https://www.cnblogs.com/ziq711/p/8228255.html 在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) 用个比较 ...