当前位置: 首页 > news >正文

什么是字符串常量池

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

在理解字符串常量前,我们先熟悉一下如何创建一个字符串,在Java中有两种方法可以创建一个字符串对象:

  • 使用new运算符。例如:

1
String str = new String( "Hello" );
  • 使用字符串常量或者常量表达式。例如:

1
2
String str= "Hello" ; //(字符串常量) 或者
String str= "Hel" + "lo" ; //(字符串常量表达式).

这些字符串的创建方式之间有什么区别呢?在Java中,equals方法被认为是对象的值进行深层次的比较,而操作符==是进行的浅层次的比较。equals方法比较两个对象的内容而不是引用。==两侧是引用类型(例如对象)时,如果引用是相同的-即指向同一个对象-则执行结果为真。如果是值类型(例如原生类型),如果值相同,则执行结果为真。equals方法在两个对象具有相同内容时返回真-但是,java.lang.Object类中的equals方法返回真-如果类没有覆盖默认的equals方法,如果两个引用指向同一个对象。

让我们通过下面的例子来看看这两种字符串的创建方式之间有什么区别吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class DemoStringCreation {
     public static void main(String args[]) {
         String str1 = "Hello" ;
         String str2 = "Hello" ;
         System.out.println( "str1 and str2 are created by using string literal." );
         System.out.println( "    str1 == str2 is " + (str1 == str2));
         System.out.println( "    str1.equals(str2) is " + str1.equals(str2)); 
         String str3 = new String( "Hello" );
         String str4 = new String( "Hello" );
         System.out.println( "str3 and str4 are created by using new operator." );
         System.out.println( "    str3 == str4 is " + (str3 == str4));
         System.out.println( "    str3.equals(str4) is " + str3.equals(str4)); 
         String str5 = "Hel" + "lo" ;
         String str6 = "He" + "llo" ;
         System.out.println( "str5 and str6 are created by using string constant expression." );
         System.out.println( "    str5 == str6 is " + (str5 == str6));
         System.out.println( "    str5.equals(str6) is " + str5.equals(str6)); 
         String s = "lo" ;
         String str7 = "Hel" + s;
         String str8 = "He" + "llo" ;
         System.out.println( "str7 is computed at runtime." );
         System.out.println( "str8 is created by using string constant expression." );
         System.out.println( "    str7 == str8 is " + (str7 == str8));
         System.out.println( "    str7.equals(str8) is " + str7.equals(str8)); 
     }
}

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
str1 and str2 are created by using string literal.
     str1 == str2 is true
     str1.equals(str2) is true
str3 and str4 are created by using new operator.
     str3 == str4 is false
     str3.equals(str4) is true
str5 and str6 are created by using string constant expression.
     str5 == str6 is true
     str5.equals(str6) is true
str7 is computed at runtime.
str8 is created by using string constant expression.
     str7 == str8 is false
     str7.equals(str8) is true

使用相同的字符序列而不是使用new关键字创建的两个字符串会创建指向Java字符串常量池中的同一个字符串的指针。字符串常量池是Java节约资源的一种方式。

字符串常量池

字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。例如:

1
2
3
4
5
6
7
8
9
public class Program
{
     public static void main(String[] args)
     {
        String str1 = "Hello"
        String str2 = "Hello" ;
        System.out.print(str1 == str2);
     }
}

其结果是:

1
true

不幸的是,当使用:

1
String a= new String( "Hello" );

一个字符串对象在字符串常量池外创建,即使池里存在相同的字符串。考虑到这些,要避免new一个字符串除非你明确的知道需要这么做!例如:

1
2
3
4
5
6
7
8
9
10
public class Program
{
     public static void main(String[] args)
     {
        String str1 = "Hello"
        String str2 = new String( "Hello" );
        System.out.print(str1 == str2 + " " );
        System.out.print(str1.equals(str2));
     }
}

结果是:

1
false true

JVM中有一个常量池,任何字符串至多维护一个对象。字符串常量总是指向字符串池中的一个对象。通过new操作符创建的字符串对象不指向字符串池中的任何对象,但是可以通过使用字符串的intern()方法来指向其中的某一个。java.lang.String.intern()返回一个保留池字符串,就是一个在全局字符串池中有了一个入口。如果以前没有在全局字符串池中,那么它就会被添加到里面。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Program
{
     public static void main(String[] args)
     {
         // Create three strings in three different ways.
         String s1 = "Hello" ;
         String s2 = new StringBuffer( "He" ).append( "llo" ).toString();
         String s3 = s2.intern();
 
         // Determine which strings are equivalent using the ==
         // operator
         System.out.println( "s1 == s2? " + (s1 == s2));
         System.out.println( "s1 == s3? " + (s1 == s3));
     }
}

输出是:

1
2
s1 == s2? false
s1 == s3? true

为了优化空间,运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用。这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收。

Java语言规范第三版中的字符串常量

每一个字符串常量都是指向一个字符串类实例的引用。字符串对象有一个固定值。字符串常量,或者一般的说,常量表达式中的字符串都被使用方法 String.intern进行保留来共享唯一的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
package testPackage;
class Test {
         public static void main(String[] args) {
                 String hello = "Hello" , lo = "lo" ;
                 System.out.print((hello == "Hello" ) + " " );
                 System.out.print((Other.hello == hello) + " " );
                 System.out.print((other.Other.hello == hello) + " " );
                 System.out.print((hello == ( "Hel" + "lo" )) + " " );
                 System.out.print((hello == ( "Hel" +lo)) + " " );
                 System.out.println(hello == ( "Hel" +lo).intern());
         }
}
class Other { static String hello = "Hello" ; }

编译单元:

1
2
package other;
public class Other { static String hello = "Hello" ; }

产生输出:

1
true true true true false true

这个例子说明了六点:

  • 同一个包下同一个类中的字符串常量的引用指向同一个字符串对象;

  • 同一个包下不同的类中的字符串常量的引用指向同一个字符串对象;

  • 不同的包下不同的类中的字符串常量的引用仍然指向同一个字符串对象;

  • 由常量表达式计算出的字符串在编译时进行计算,然后被当作常量;

  • 在运行时通过连接计算出的字符串是新创建的,因此是不同的;

  • 通过计算生成的字符串显示调用intern方法后产生的结果与原来存在的同样内容的字符串常量是一样的。


转载于:https://my.oschina.net/u/1453975/blog/221840

相关文章:

  • Shell实战训练营Day11
  • java并发处理 (同步与原子性)
  • Java入门项目:学生信息管理系统V2
  • IE6不支持a标签以外元素的hover的解决方案
  • 40个Java多线程问题详解复习
  • 【Linux】目录权限与文件权限
  • YUV数据格式与YUV_420_888
  • vue-cli3.0项目中使用vw——相比flexible更原生的移动端解决方案
  • rsync 数据镜像备份 记录
  • Lucene:基于Java的全文检索引擎简介
  • Android 其他特效展示
  • DataUml Design 教程7 - 数据库生成模型
  • request
  • luanet分布式lua框架
  • 解决 LLVM 错误 fatal error: ‘csignal’ file not found
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • canvas绘制圆角头像
  • CEF与代理
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • ES6语法详解(一)
  • HTTP那些事
  • JavaScript中的对象个人分享
  • js ES6 求数组的交集,并集,还有差集
  • JWT究竟是什么呢?
  • learning koa2.x
  • node和express搭建代理服务器(源码)
  • oldjun 检测网站的经验
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 记一次删除Git记录中的大文件的过程
  • 技术胖1-4季视频复习— (看视频笔记)
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 如何合理的规划jvm性能调优
  • 数据仓库的几种建模方法
  • 思维导图—你不知道的JavaScript中卷
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 通信类
  • 温故知新之javascript面向对象
  • 一个JAVA程序员成长之路分享
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (4)(4.6) Triducer
  • (二)PySpark3:SparkSQL编程
  • (分类)KNN算法- 参数调优
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (理论篇)httpmoudle和httphandler一览
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (一)Linux+Windows下安装ffmpeg
  • (转)详解PHP处理密码的几种方式
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .NET 设计一套高性能的弱事件机制