0

0

Java String中intern方法怎么使用

WBOY

WBOY

发布时间:2023-05-05 17:28:13

|

1420人浏览过

|

来源于亿速云

转载

    常量池简介

    java 语言中有8种基本类型和一种比较特殊的类型string。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池(在方法区)的概念。常量池就类似一个java系统级别提供的缓存。8种基本类型的常量池都是系统协调的,string类型的常量池比较特殊。

    String的常量池的主要使用方法有两种:

    直接使用双引号声明出来的String对象会直接存储在常量池中。

    如果不是用双引号声明的String对象,可以使用String提供的intern方法将其放到常量池。

    intern方法简介(JDK7)

    原型:public native String intern();

    立即学习Java免费学习笔记(深入)”;

    说明:

    从字符串常量池中查询当前字符串是否存在(通过equals判断)。

    • 如果存在,返回常量池中的字符串引用。

    • 如果不存在,把这个String对象引用存到常量池,然后返回这个String对象的引用。

    返回值:都是返回String变量对应的字符串常量池的引用。

    示例

    package com.example;
     
    public class Demo {
        public static void main(String argv[]) {
            String s = "test";
            System.out.println(s == s.intern());
        }
    }

    JDK6及之前:输出false

    JDK7及之后:输出true

    原理(JDK6与JDK7)

    常量池里的字符串的由来

    JDK6及以前调用String.intern()

    • 若常量池中有,则返回常量池中这个字符串的引用

    • 若常量池中没有,则拷贝一份对象,放到常量池(永久代)中;返回值是常量池(永久代)中对应字符串实例的引用。

    JDK7及以后调用String.intern()

    • 若常量池中有,则返回常量池中这个字符串的引用

    • 若常量池中没有,则拷贝一份引用,放到常量池(堆)中;(JDK1.7将String常量池从Perm区移动到了Java Heap区)

    例程测试

    例程1: 

    package org.example.a;
     
    public class Demo {
        public static void main(String argv[]) {
            String s1 = new String("1");
            s1.intern();
            String s2 = "1";
            System.out.println(s1 == s2);
     
            String s3 = new String("1") + new String("1");
            s3.intern();
            String s4 = "11";
            System.out.println(s3 == s4);
        }
    }

    结果

    jdk6:false falsejdk7:false truejdk8:false true

    例程2: 

    package org.example.a;
     
    public class Demo {
        public static void main(String argv[]) {
            String s1 = new String("1");
            s1.intern();
            String s2 = "1";
            System.out.println(s1 == s2);
     
            String s3 = new String("1") + new String("1");
            String s4 = "11";
            s3.intern();
            System.out.println(s3 == s4);
        }
    }

    上述代码第二部分有个对调。

    结果

    jdk6:false false
    jdk7:false false
    jdk8:false false

    例程分析

    下边图中:绿色线条代表 String 对象的内容指向。 红色线条代表地址指向。

    jdk1.6

    例程1与例程2的分析

    Java String中intern方法怎么使用

    如上图所示。首先说一下 jdk6中的情况,在 jdk6中上述的所有打印都是 false 的,因为 jdk6中的常量池是放在 Perm 区中的,Perm区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new出来的 String 对象是放在 JAVA Heap 区域。所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的。

    jdk1.7

    在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 会产生java.lang.OutOfMemoryError:PermGen space错误的。在 jdk7 的版本中,字符串常量池已经从Perm区移到正常的Java Heap区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称jdk8已经直接取消了Perm区域,而新建立了一个元区域。应该是jdk开发者认为Perm区域已经不适合现在 JAVA的发展了。字符串常量池移动到JAVA Heap区域,现在解释为什么会有上述的打印结果。

    例程1的分析

    Java String中intern方法怎么使用

    1.String s1 = new String("1"); 

    分析:这行代码生成了2个对象(常量池中的“1” 和 JavaHeap 中的字符串对象)。s.intern(); 这一句是 s1 对象去常量池中寻找后发现 “1” 已经在常量池里了。

    Zyro AI Image Upscaler
    Zyro AI Image Upscaler

    Zyro出品的AI图片放大工具

    下载

    此时s1指向的是Java Heap中的字符串对象。

    2.String s2 = "1"; 

    分析:这行代码生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s1 和 s2 的引用地址不同。

    3.String s3 = new String("1") + new String("1");

    分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。
    此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

    4.s3.intern();

    分析:这行代码将 s3中的"11"字符串放入String 常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个"11"的对象,关键点是 jdk7 中常量池不在Perm区域,而是在堆中了。常量池中不需再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。

    此时,s3是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是有 “11”对象,它保存的就是s3引用地址。

    5.String s4 = "11"; 

    这行代码”11″是显式声明的,因此会直接去常量池中创建,创建时发现已经有这个对象了。

    此时:s4 == 常量池的“11”对象引用 == s3引用对象的引用

    例程2的分析

    Java String中intern方法怎么使用

    String s1 = new String("1");

    s1.intern();

    String s2 = "1";

    分析:s1.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s1 和 s2 的引用地址是不会相等的。

    String s3 = new String("1") + new String("1");

    分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。

    此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

    String s4 = "11";

    分析:声明 s4 的时候常量池中是不存在“11”对象的,执行完后,s4是常量池里“11“对象的引用。

    s3.intern(); 

    分析:此时常量池中“11”对象已经存在了,不会有任何操作,s3仍然是堆中String对象的引用。因此 s3 != s4

    应用实例

    package org.example.a;
     
    import java.util.Random;
     
    public class Demo {
        static final  int MAX = 1000 * 10000;
        static final String[] arr = new String[MAX];
        public static void main(String argv[]) {
            Integer[] DB_DATA = new Integer[10];
            Random random = new Random(10 * 10000);
            for(int i = 0; i < DB_DATA.length; i++){
                DB_DATA[i] = random.nextInt();
            }
     
            long t = System.currentTimeMillis();
            for(int i = 0; i < MAX; i++){
                //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
                arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
            }
     
            System.out.println((System.currentTimeMillis() -t) + "ms");
            System.gc();
        }
    }

    上述代码是一个演示代码,其中有两条语句不一样,一条是使用 intern,一条是未使用 intern。

    运行的参数是:-Xmx2g -Xms2g -Xmn1500M

    不用intern

    2160ms

    Java String中intern方法怎么使用

    使用intern

    826ms

    Java String中intern方法怎么使用

    通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

    细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后, 然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern占用空间导致 jvm 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。

    相关文章

    java速学教程(入门到精通)
    java速学教程(入门到精通)

    java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    热门AI工具

    更多
    DeepSeek
    DeepSeek

    幻方量化公司旗下的开源大模型平台

    豆包大模型
    豆包大模型

    字节跳动自主研发的一系列大型语言模型

    通义千问
    通义千问

    阿里巴巴推出的全能AI助手

    腾讯元宝
    腾讯元宝

    腾讯混元平台推出的AI助手

    文心一言
    文心一言

    文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

    讯飞写作
    讯飞写作

    基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

    即梦AI
    即梦AI

    一站式AI创作平台,免费AI图片和视频生成。

    ChatGPT
    ChatGPT

    最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

    相关专题

    更多
    string转int
    string转int

    在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

    1010

    2023.08.02

    java基础知识汇总
    java基础知识汇总

    java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

    1564

    2023.10.24

    字符串常量的表示方法
    字符串常量的表示方法

    字符串常量的表示方法:1、使用引号;2、转义字符;3、多行字符串;4、原始字符串;5、字符串连接;6、字符串字面量和对象;7、编码问题。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

    143

    2023.12.26

    js 字符串转数组
    js 字符串转数组

    js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

    760

    2023.08.03

    js截取字符串的方法
    js截取字符串的方法

    js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

    220

    2023.09.04

    java基础知识汇总
    java基础知识汇总

    java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

    1564

    2023.10.24

    字符串介绍
    字符串介绍

    字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

    649

    2023.11.24

    java读取文件转成字符串的方法
    java读取文件转成字符串的方法

    Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

    1208

    2024.03.22

    Go高并发任务调度与Goroutine池化实践
    Go高并发任务调度与Goroutine池化实践

    本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

    4

    2026.03.10

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Kotlin 教程
    Kotlin 教程

    共23课时 | 4.3万人学习

    C# 教程
    C# 教程

    共94课时 | 11.1万人学习

    Java 教程
    Java 教程

    共578课时 | 80.2万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号