最近在阅读《深入理解Jav虚拟机》的运行时常量池章节,看到“java语言并不要求常量池一定只有编译器才能产生...运行期间也可以将新的常量放入常量池,这种特性被开发人员利用得比较多的时String类的intern()方法。"于是我便去深入了解了一下。

1     public static void main(String[] args) {
2 String a="王者";
3 String b="荣耀";
4 String c=a+b;
5 }

上述代码中,我通过javap命令查看编译期的常量池,发现只有字符串"王者"和"荣耀",并无”王者荣耀“,通过反编译得到的String c=a+b变成了,原来+号会被转为StringBuilder,并且拼接,注意当String c="王者"+"荣耀"时,编译器则会优化,常量池里只有王者荣耀,并无“王者”和“”

1 String c= (new StringBuilder()).append(a).append(b).toString()

这就很有意思了,也就是说编译时常量池中并无"王者荣耀"了,因为编译期不会运算,只有在运行期才能执行方法得到结果。

由上图可知,"王者荣耀"直接指向堆,并且运行时常量池无"王者荣耀"

1     public static void main(String[] args) {
2 String a="王者";
3 String b="荣耀";
4 String c=a+b;
5 String d=c.intern();
6 System.out.println(d==c);
7 }

上面为什么是true呢,接下来讲述inter()

JDK1.7后,intern方法先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,当字符串常量池中找不到对应的字符串时,而只是生成一个对该字符串的引用在字符串常量池。所以”王者荣耀”本身不会在字符串常量池,而常量池里面保存了在堆中王者荣耀的索引,所以为true。JDK1.7之前这里就不说了。

前面说了intern可以在程序运行时将新的常量放入运行时常量池,接下来就开始演示intern的强大用处

 public static void main(String[]  args) throws Exception {
long start=System.currentTimeMillis(); //获取开始时间
String[] arr = new String[10000000];
Integer[] a= new Integer[10000000];
for (int i = 0; i < 10000000; i++) {
a[i] = (int)(1+Math.random()*(3));
}
for (int i = 0; i < 10000000; i++) {
arr[i] = new String(String.valueOf(a[i]));
}
long end=System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: "+(end-start)+"ms");
}
//程序运行时间: 14461ms
    public static void main(String[]  args) throws Exception {
long start=System.currentTimeMillis(); //获取开始时间
String[] arr = new String[10000000];
Integer[] a= new Integer[10000000];
for (int i = 0; i < 10000000; i++) {
a[i] = (int)(1+Math.random()*(3));
}
for (int i = 0; i < 10000000; i++) {
arr[i] = new String(String.valueOf(a[i])).intern();
}
long end=System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: "+(end-start)+"ms");
}
//程序运行时间: 1688ms

很简单的小程序,就是有无调用intern方法,但程序运行时间差了10倍!

我们明确的知道,会有很多重复的相同的字符串产生,但是这些字符串的值都是只有在运行期才能确定的。所以,我们通过intern显示的将其加入常量池,这样可以减少很多字符串的重复创建。

new String("王者荣耀")和new String("王者")+new String("荣耀")不管是在编译期就确定放入常量池还是在运行期,都还是要在堆里面创建对象的。不同就是new String("王者荣耀")还要指向常量池。new String("王者")+new String("荣耀")不需要指向常量池

new String.intern中。这个过程是不会在Java堆中再创建一个String对象的。

注意,是因为随机数是运行期才知道的,intern把最先出现的,”1“,“2”,”3“,对象索引放入了常量池,下次在看见”1“,“2”,”3“,返回第一次在堆中String的对象地址,不用创建新对象,所以intern高效,使用intern()在堆中只会创建3个对象,而不使用intern则创建100000000个对象,并且常量池里并没有”1“,“2”,”3“

小问题,arri[]=new String("王者荣耀").intern()没什么意义的,虽然这样写比arri[]=new String("王者荣耀")效率高,可用代码验证,arri[]=new String("王者荣耀").intern()编译时常量池就出现“王者荣耀”,因为intern不会产生对象所以快,这里是直接引用了常量池中的字符串“王者荣耀”。但intern()并不是这样的初衷,而是为了解决运行时才出现的常量,不是解决编译时在字符串常量池的问题,这里比较抽象,多理解一下

笔者技术有限,如有错误,请指正,谢谢

探秘inter()方法的更多相关文章

  1. .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法

    开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...

  2. 【python学习笔记】9.魔法方法、属性和迭代器

    [python学习笔记]9.魔法方法.属性和迭代器 魔法方法:xx, 收尾各有两个下划线的方法 __init__(self): 构造方法,创建对象时候自动执行,可以为其增加参数, 父类构造方法不会被自 ...

  3. 《深入理解Java虚拟机》Java内存区域与内存溢出异常

    注:“蓝色加粗字体”为书本原语 先来一张JVM运行时数据区域图,再接下来一一分析各区域功能:   程序计数器 程序计数器(program Counter Register)是一块较小的内存空间,它可以 ...

  4. Java简明教程

    Java与C++比较概况 C++ Java class Foo { // 声明 Foo 类 public: int x; // 成员变量 Foo(): x() { // Foo 的构造函数Constr ...

  5. 1.Java内存区域

    Java虚拟机在执行java程序的过程中会把他管理的内存划分为若干个不同的数据区域各自用途.创建以及销毁时间各不相同.有的随着虚拟机进行的启动而存在,有的区域依赖于线程的启动和结束而建立以及销毁.如图 ...

  6. Java和C#中String直接赋值与使用new创建(==与equals进行比较)的区别

    在Java中,字符串可以直接赋值或者使用new来新建,直接赋值的话是编译阶段(.class文件)中就将该字符串值放到常量池中,以后如果有其他变量直接赋予同样的值的话就不再分配内存空间,而是直接给它个引 ...

  7. javascript扩充基本类型的功能

    可以通过给Function.prototype增加方法来使得该方法对所有函数可用. 通过给Function.prototype增加一个method方法,下次给对象增加方法的时候就不必键入prototy ...

  8. JAVA中==与equals的区别

    equals如果没有被重写的话,和==的作用是一样的,都是判断两个对象引用是否指向同一个地址.一般重写了equals()方法就表示比较它们“实际意义上相等”,比较的是内容,而不是引用地址.Java中S ...

  9. [CLR via C#]异常和状态管理

    当CLR检测到某个正在运行的.NET应用程序处于一种特殊的正常执行顺序被打断的状态时,会生成一个异常对象来表示这个错误,并将此对象在方法调用堆栈中向上传送.如果一个程序引发了一个异常却没有处理,CLR ...

随机推荐

  1. mysql按照时间分组查询

    mysql 按年.月.周.日分组查询 1.按照年份分组查询 SELECT DATE_FORMAT(t.bill_time,'%Y') month_time,sum(t.pay_price) total ...

  2. GIS :元宇宙未来发展的有力技术支撑

    摘要:元宇宙是描述未来互联网迭代发展的一个概念,是一个将现实世界和虚拟世界相互融合的一个可感知的持久.共享的3D虚拟空间组成的世界. 本文分享自华为云社区<[云驻共创]元宇宙漫游指南-新一代GI ...

  3. Java代码批量插入数据到MySQL

    1.批量插入 import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundExc ...

  4. 使用Cesium的地下渲染技术可视化瑞士的地质资源

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Camptocamp为瑞士拓扑的孪生数字增加了地下可视化功能. ...

  5. Qt之图片

    widget.h: #ifndef WIDGET_H #define WIDGET_H #include<QPaintEvent> #include <QWidget> #in ...

  6. 用8个命令调试Kubernetes集群

    如果使用任何系统的时间足够长,那么你肯定必须对其进行调试,Kubernetes也不例外.它是一个分布式系统,有许多运动部件.我们将介绍8个可以运行以调试任何Kubernetes集群的命令. 它将帮助你 ...

  7. python31day

    内容回顾 网编总结,思维导图 计划 并发编程的开始,计划6天 操作系统1天 进程2天 线程2天 携程1天 今日内容 操作系统 多道操作系统: 从顺序的一个个执行的思路变成:并行轮流使用cpu 一个程序 ...

  8. JavaScript通过父节点ID递归生成JSON树

    JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回)   · 代码示 ...

  9. python 小兵面向对象

    Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. 如果你以前没有接触过 ...

  10. 「CTSC2010」产品销售

    「CTSC2010」产品销售 30pts的费用流都会吧... 100pts只要模拟费用流就行了,是不是很简单呀( 咕咕咕 令\(M_i\)表示\(i-1\to i\)的正向边,\(M_i^{'}\)表 ...