首页 > 编程知识 正文

运行时常量池和字符串常量池,常量池中的字符串是对象吗

时间:2023-05-05 06:15:19 阅读:119999 作者:1568

String s1='Hello ';

String s2='Hello ';

String s3='Hel' 'lo ';

stringS4='Hel'newstring('lo );

stringS5=newstring(Hello );

String s6=s5.intern (;

字符串S7=' h ';

String s8='ello ';

String s9=s7 s8;

system.out.println(S1==S2;//真

system.out.println(S1==S3 );//真

system.out.println(S1==S4 ); //false

system.out.println(S1==S9 ); //false

system.out.println(S4==S5; //false

system.out.println(S1==S6 );//真

刚开始看字符串的时候,看到很相似的问题,总会有点困惑。 看回答总是提到字符串常量池、执行常量池等概念,容易混淆。

接下来,我们将讨论Java字符串是如何创建的。

String有两种赋值方法。 第一,按“字面量”赋值。

String str='Hello ';

第二,通过new关键字创建新对象。

stringstr=newstring('Hello );

要弄清楚这两种方法的区别,首先需要知道存储器中的存储位置。

通常内存是图中的运行时数据区(Runtime Data Area ),与字符串创建有关的有方法区(Method Area )、堆区(Heap Area )、堆栈区(Stack Area )

方法区域:存储类信息、常量和静态变量。 全球共享。

堆:存储对象和数组。 全球共享。

堆栈区域:基本数据类型,对对象的引用存储在此。 线程的私人。

每次执行方法时,都会在堆栈区域创建堆栈帧,基本数据类型和对象引用存在于堆栈帧内的局部变量表(Local Variables )中。

加载类后,类信息存储在堆以外的方法区域中。 方法区域有一个称为“运行时常量池”(Runtime Constant Pool )的区域。 这对于每个类都是专用的,每个class文件中的“常量池”由加载器加载,然后映射并存储在其中。 这将在后面叙述。

与String关联最大的是字符串池(String Pool ),其位置是方法区域上方的驻留字符串)的位置,一直以来与运行时常量池混淆,但实际上是两个完全不同的存储区域,字符串常量池是全局共享的字符串为String.intern (调用方法时,其引用存储在String Pool中。

在理解这些概念的基础上,介绍两个字符串创建方法的区别。

以下Test类使用main方法中的“文字”赋值方法将" Hello "赋值给字符串str。

公共类测试{ publicstaticvoidmain (字符串[ ] args ) }

String str='Hello ';

}

}

编译Test.java文件将得到. class文件。 该文件包含类的信息,其中有一个称为常量池的区域。 class常量池和内存中的常量池不同。

. class文件常量池主要存储常量,而常量包含类中定义的常量。 因为String是不变的,所以“String为什么是不变的? ),所以字符串“Hello”保存在这里。

当程序使用Test类时,Test.class解析为内存中的方法区域。 class文件中的常量池信息加载到运行时常量池中,而字符串则不是。

示例中的" Hello "在堆中创建对象,并将引用存储在“字符串池”(String Pool )中,如下图所示。

Test类刚刚加载,没有为主函数创建str," Hello "对象是在堆中创建的。

当主线程开始创建str变量时,虚拟机会去查找字符串池中是否存在equals(“Hello”字符串,如果等于,则将字符串池中对“Hello”的引用复制到str )。 如果找不到等效字符串,请在将引用放在字符串池中的同时,在堆中创建新对象并将引用分配给str。

使用文字赋值方法创建字符串时,如果字符串的值相同,则无论创建多少次,它都指向堆中的同一对象。

公共类

est {public static voidmain(String[] args) {

String str1= "Hello";

String str2=“Hello”;

String str3=“Hello”;

}

}

当利用new关键字去创建字符串时,前面加载的过程是一样的,只是在运行时无论字符串池中有没有与当前值相等的对象引用,都会在堆中新开辟一块内存,创建一个对象。

public classTest {public static voidmain(String[] args) {

String str1= "Hello";

String str2=“Hello”;

String str3= new String("Hello");

}

}

现在我们来回头看之前的例子。

String s1 = "Hello";

String s2= "Hello";

String s3= "Hel" + "lo";

String s4= "Hel" + new String("lo");

String s5= new String("Hello");

String s6=s5.intern();

String s7= "H";

String s8= "ello";

String s9= s7 +s8;

System.out.println(s1== s2); //true

System.out.println(s1 == s3); //true

System.out.println(s1 == s4); //false

System.out.println(s1 == s9); //false

System.out.println(s4 == s5); //false

System.out.println(s1 == s6); //true

有了上面的基础,之前的问题就迎刃而解了。

s1在创建对象的同时,在字符串池中也创建了其对象的引用。

由于s2也是利用字面量创建,所以会先去字符串池中寻找是否有相等的字符串,显然s1已经帮他创建好了,它可以直接使用其引用。那么s1和s2所指向的都是同一个地址,所以s1==s2。

s3是一个字符串拼接操作,参与拼接的部分都是字面量,编译器会进行优化,在编译时s3就变成“Hello”了,所以s1==s3。

s4虽然也是拼接,但“lo”是通过new关键字创建的,在编译期无法知道它的地址,所以不能像s3一样优化。所以必须要等到运行时才能确定,必然新对象的地址和前面的不同。

同理,s9由两个变量拼接,编译期也不知道他们的具体位置,不会做出优化。

s5是new出来的,在堆中的地址肯定和s4不同。

s6利用intern()方法得到了s5在字符串池的引用,并不是s5本身的地址。由于它们在字符串池的引用都指向同一个“Hello”对象,自然s1==s6。

总结一下:

字面量创建字符串会先在字符串池中找,看是否有相等的对象,没有的话就在堆中创建,把地址驻留在字符串池;有的话则直接用池中的引用,避免重复创建对象。

new关键字创建时,前面的操作和字面量创建一样,只不过最后在运行时会创建一个新对象,变量所引用的都是这个新对象的地址。

参考资料:

https://www.zhihu.com/question/29884421/answer/113785601

http://www.cnblogs.com/iyangyuan/p/4631696.html

http://blog.csdn.net/sugar_rainbow/article/details/68150249

原文:http://www.cnblogs.com/justcooooode/p/7603381.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。