`

字符串常量池

    博客分类:
  • JAVA
 
阅读更多

转自:http://www.importnew.com/10756.html

 

在理解字符串常量前,我们先熟悉一下如何创建一个字符串,在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方法后产生的结果与原来存在的同样内容的字符串常量是一样的。
分享到:
评论

相关推荐

    第4节: 揭秘JVM字符串常量池和Java堆-01

    第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: 揭秘JVM字符串常量池和Java堆-01第4节: ...

    字符数组的存储方式 字符串常量池.docx

    字符串常量池 字符串在java程序中被大量使用,为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一定的优化,在Permanent Generation中专门有一块区域用来存储字符串常量池(一组指针...

    String int 字符串常量池 包装类型 函数参数 值传递引用传递 的 内存分配例子——源码

    String int 字符串常量池 包装类型 函数参数 值传递引用传递 的 内存分配例子——源码 public static void fun_ref (Ref_test ref_out){ Ref_test ref_in=new Ref_test(); ref_in.s1="in"; //ref_out.s1=...

    什么是字符串常量池?Java开发Java经验技巧共6页.p

    什么是字符串常量池?Java开发Java经验技巧共6页.pdf.zip

    Java中的字符串常量池详细介绍

    主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池,需要的朋友可以参考下

    Java 中的字符串常量池详解

    本文主要介绍Java中的字符串常量池的知识,这里整理了相关资料及简单示例代码帮助大家学习理解此部分的知识,有需要的小伙伴可以参考下

    JVM常量池教程吐血整理干货.md

    字符串常量池(全局常量池) 包装类型缓存池 JVM常量池 Jvm常量池分为: Class常量池(静态常量池) 运行时常量池 字符串常量池(全局常量池) 包装类型缓存池 Class常量池(静态常量池) 当Java源文件被编译后,就会生成...

    Java String 字符串常量池解析

    主要介绍了Java String 字符串常量池解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    深入解析JVM之内存结构及字符串常量池(推荐)

    Java作为一种平台无关性的语言,其主要依靠于Java虚拟机——JVM,接下来通过本文给大家介绍JVM之内存结构及字符串常量池的相关知识,需要的朋友可以参考下

    图解JVM的内存结构及字符串常量池方法详解.docx

    JVM包含了非常多的知识,比较核心的有 内存结构 、 类加载 、 类文件结构 、 垃圾回收 、 执行 引擎 、 性能调优 、 监控 等等这些知识,但所有的功能都是围绕着 内存结构 展开的,因为我们编译后的...

    C#之CLR内存字符串常量池(string)

    主要介绍了C#之CLR内存字符串常量池(string),对于学习和理解C#内存原理很有帮助,需要的朋友可以参考下

    jvm如何处理长字符串

    jvm如何处理长字符串?java的classs文件中,constant_utf8_info的长度是u2,也就是说,一个字符串最长是65535个字节,但是,在本机做测试,超过这个长度的字符串也是允许的,原因是什么?

    Android代码-android-manifest-parser

    String Chunk // 字符串常量池 Chunk Type(0x001C0001) 4bytes // Chunk类型 Chunk Size 4bytes // Chunk长度 String Count 4bytes // 字符串个数 Style Count 4bytes Unknown 4bytes String Pool Offset 4...

    day08 03 字符串的常量池

    day08_03_字符串的常量池

    深入理解Java String#intern()内存模型

    大家知道,Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,会在字符串常量池中创建该字符串然后再返回。  字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, ...

    String s = new String(” a “) 到底产生几个对象?

    老生常谈的一个梗,到2020了还在争论,你们一天天的,哎哎哎,我不是针对你一个,我是说...毕竟我和各位都是人才,java知识底蕴不能如此短浅,这题还没谢幕我们还能对面试官多哔哔几句:字符串常量池在不同版本的jvm中

    安卓手机端破解神器!ApkCrack v0.7

    2.Dex字符串常量池和Smali代码编辑 3.Dex反编译和回编译 4.Apk资源混淆 5.Arsc和Axml的字符串常量池 6.Apk签名工具,支持自定义签名和创建自己的密钥 7.一键将odex文件转化为dex文件 8.一键将oat文件转化为dex文件 9...

    Java语言程序设计(第3版)第06章-字符串.pptx

    6.1.1 字符串比较 Java语言程序设计(第3版) 不能使用"=="号来比较字符串内容是否相等 比较内容是否相等: boolean equals(String str) boolean equalsIgnoreCase(String str) s1 s2 Hello Hello 字符串常量池 s1 s2...

    通过String.intern()方法浅谈堆中常量池

    主要介绍了通过String.intern()方法浅谈堆中常量池,在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍。,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics