前言

读完这篇文章你可以了解,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. 几句话总结一个算法之CTR预估模型

    背景 假设现在有个商品点击预测的任务,有用户端特征性别.年龄.消费力等,商品侧特征价格.销量等,样本为0或者1,现在对特征进行one hot encode,如性别特征用二维表示,男为[1,0],女为[ ...

  2. prometheus添加自定义监控与告警(etcd为例)

    一.步骤及注意事项(前提,部署参考部署篇) 一般etcd集群会开启HTTPS认证,因此访问etcd需要对应的证书 使用证书创建etcd的secret 将etcd的secret挂在到prometheus ...

  3. 030 ElasticSearch----全文检索技术05---基础知识详解03-聚合

    聚合可以让我们极其方便的实现对数据的统计.分析.例如: 什么品牌的手机最受欢迎? 这些手机的平均价格.最高价格.最低价格? 这些手机每月的销售情况如何? 实现这些统计功能的比数据库的sql要方便的多, ...

  4. Hive sampling 语法之TABLESAMPLE用法理解

    官网关于LanguageManual Sampling的教程,部分截图如下,这里主要分享对TABLESAMPLE(BUCKET 3 OUT OF 16 ON id)子句的理解 ​ 官网中假设创建表时设 ...

  5. Linux常用命令wc

    wc名字来源: wc -- word, line, character, and byte count The wc utility displays the number of lines, wor ...

  6. Java 加载动态库 dll 文件

    不知道具体原理,但是,加载 dll 文件时,带路径或者更改 dll 文件的名字,都会报错.虽然库记载成功了,但是处女座认为这不可接受.于是有了这个解决方案. 在根目录为库创建软连接,然后使用 syst ...

  7. Python开发【第十五篇】模块的导入

    的导入语句 import 语句 语法: import 模块名1 [as 模块别名] 作用: 将某模块整体导入到当前模块 示例: import math import sys,os 用法: 模块名.属性 ...

  8. winform+cefSharp实现窗体加载浏览器

    1:新建winform项目 2:安装cefSharp 3:配置管理器更改为X86 4:添加引用 using CefSharp; using CefSharp.WinForms; 5:项目启动,打开网页 ...

  9. 虚拟化x86的三种途径

    虚拟化x86的三种途径 作者:缪天翔链接:https://www.zhihu.com/question/20145026/answer/34527331 x86上的全系统虚拟化有三种主要的途径: 二进 ...

  10. 第一阶段:Java基础 1.JAVA开发介绍---5. Java的注释,标识符、标识符的命名规范

    1.java注释 java中有三种注释方式,单行注释,多行注释,文档注释. (1).单行注释:快捷键Ctrl+/再次按撤销注释, (2).多行注释:Ctrl+shift+/ 撤销Ctrl+shift+ ...