最近在阅读《深入理解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. python中join()方法介绍

    描述 Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串. 语法 join()方法语法:str.join(sequence) 参数 sequence -- 要连接的元 ...

  2. 5.14-HTTP间通信

    1.社长社员通信WEBSOCKET WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输.但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现. 以前客户端想知道 ...

  3. Cesium1.70-介绍CesiumOSM建筑新特性

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 我们很高兴宣布Cesium OSM建筑,一个覆盖整个世界的3D建 ...

  4. netty基础知识

    参考 http://www.infoq.com/cn/articles/netty-high-performance 1. 传统 RPC 调用性能差的三宗罪 1)网络传输方式问题 2)序列化方式问题 ...

  5. gin框架中的中间件

    全局中间件 所有请求都经过此中间件 中间件中设置值 func MiddleWare() gin.HandlerFunc { return func(context *gin.Context) { t ...

  6. mysqldump 逻辑备份和物理备份

    逻辑备份 逻辑备份是备份sql语句,在恢复的时候执行备份的sql语句实现数据库数据的重现. 工具:mysqldump 特点: 1.可移植性比较强 2.备份和恢复的花费时间长,不适用于大型业务系统 物理 ...

  7. linux中sort命令全面解析

    目录 一:sort命令作用 二:sort格式 1.sort参数 2.参数解析 sort命令简介 Linux sort 命令用于将文本文件内容加以排序. sort 可针对文本文件的内容,以行为单位来排序 ...

  8. CICD流程

    1.开发者git提交代码至gitlab仓库 2.jenkins从gitlab拉取代码,触发镜像构建 3.镜像上传至harbor私有仓库 4.镜像下载至执行机器--k8s node kubelet 5. ...

  9. Shell 脚本进阶,经典用法及其案例

    一.条件选择.判断 1.条件选择if (1)用法格式 if 判断条件 1 ; then 条件为真的分支代码 elif 判断条件 2 ; then 条件为真的分支代码 elif 判断条件 3 ; the ...

  10. Java多线程专题3: Thread和ThreadLocal

    合集目录 Java多线程专题3: Thread和ThreadLocal 进程, 线程, 协程的区别 进程 Process 进程提供了执行一个程序所需要的所有资源, 一个进程的资源包括虚拟的地址空间, ...