我很好奇为什么可以在不调用new String()
的情况下创建字符串,因为API提到它是Object
class
java.lang.String
那么我们如何才能使用String s="hi"
而不是String s=new String("hi")
?
This post澄清了==
运算符的使用情况以及new
的缺席情况,并说这是由于String
文字被实习或取自<{1}}的文字池,因此JVM
是不可变的。
看到诸如
之类的陈述Strings
第一次真正发生了什么?
String s="hi"
是否会像JVM
一样替换它,
其中创建了一个Object,并将String s=new String("hi")
添加到 String中
文字池以及后续调用,例如"hi"
从泳池中取出?
这是底层机制的运作方式吗?如果是这样,那么
String s1="hi"
与
相同String s=new String("Test");
String s1="Test";
在内存利用率和效率方面?
此外,我们可以通过哪种方式访问字符串池
检查其中有多少String s="Test";
String s1="Test";
个文字,占用了多少空间等?
答案 0 :(得分:14)
Java编译器对字符串文字有特殊支持。假设没有,那么在源代码中创建字符串真的很麻烦,你必须写下这样的东西:
// Suppose that we would not have string literals like "hi"
String s = new String(new char[]{ 'h', 'i' });
回答你的问题:
或多或少,如果你真的想知道细节,你必须研究JVM的源代码,你可以在OpenJDK找到它,但要注意它既庞大又复杂。
不,这两个不相同。在第一种情况下,您将显式创建一个新的String
对象:
String s=new String("Test");
将包含由文字String
表示的"Test"
对象的副本。请注意,在Java中编写new String("some literal")
是一个好主意永远 - 字符串是不可变的,并且永远不需要复制字符串文字。
我无法知道检查字符串池中的内容。
答案 1 :(得分:9)
- 醇>
String s="hi"
第一次真正发生了什么?JVM是否像
String s=new String("hi")
一样替换它, 其中创建了一个对象并且&#34; hi&#34;被添加到String文字中 池等后续调用,如String s1 =&#34; hi&#34;取自 游泳池?。
没有。真正发生的是 - String Literals 在编译期间已解析并且 interned (添加到String常量池),只要该类是加载/初始化或懒洋洋地。因此,它们可供JVM中的类使用。
请注意,即使在字符串常量池中有一个值为"hi"
的String,new String("hi")
也将创建堆上的另一个String并返回其引用。
- 是
醇>
String s=new String("Test");
String s1="Test";
与
相同
String s="Test";
String s1="Test";
在内存利用率方面 效率η
不,在第一种情况下2&#34;测试&#34;字符串已创建。一个将被添加到String常量池(假设它不存在)和另一个在堆上。第二种情况可以是GCed。在第二种情况下,String常量池中只有一个String literal ,并且有2个引用(s
和s1
)
- 此外,如果我们有任何方式可以访问字符串池 检查它中存在多少个字符串文字,占用空间等 来自该计划或任何监测工具?
醇>
我认为我们无法看到String常量池的内容。我们只能基于我们的假设假设和确认行为。
答案 2 :(得分:6)
这与主题没有密切关系,但每当你怀疑java编译器会做什么时,你可以使用
javap -c CompiledClassName
打印实际发生的事情。 (来自dir的CompiledClassName,其中CompiledClassName.class是)
要添加到Jesper的答案,有更多机制在起作用,例如当您从文字或最终变量连接String时,它仍将使用实习池:
String s0 = "te" + "st";
String s1 = "test";
final String s2 = "te";
String s3 = s2 + "st";
System.out.println(s0==s1); //true
System.out.println(s3==s1); //true
但是当你使用非final变量连接时,它将不使用池:
String s0 = "te";
String s1 = s0 + "st";
String s2 = "test";
System.out.println(s1 == s2); //false
答案 3 :(得分:6)
以下是略微简化,因此不要试图引用其中的确切细节,但一般原则适用。
每个编译的Java类都包含一个数据blob,它指示在该类文件中声明了多少字符串,每个字符串的长度以及属于所有字符串的字符。加载类时,类加载器将创建一个合适大小的String[]
来保存该类中定义的所有字符串;对于每个字符串,它将生成一个合适大小的char[]
,从类文件中读取适当数量的字符到char[]
,创建一个封装这些字符的String
,并存储引用课程String[]
。
编译某个类(例如Foo
)时,编译器知道它遇到的第一个,第二个,第三个,第五个等字符串文字。如果代码显示myString = "George";
而George是第六个字符串文字,它将在代码中显示为&#34;加载字符串文字#6&#34;指令;正常时编译器,当它为该指令生成代码时,将生成一条指令来获取与该类关联的第六个字符串引用。
答案 4 :(得分:5)
一种但不完全正确
字符串常量在constant pool resolution期间创建并实现。这是在第一次执行加载字符串文字的LDC
字节码时发生的。第一次执行后,JVM将JVM_CONSTANT_UnresolvedString
常量池标记替换为JVM_CONSTANT_String
标记,以便下次LDC
采用现有字符串而不是创建新字符串。
没有。第一次使用"Test"
将创建一个新的字符串对象。然后new String("Test")
将创建第二个对象。
是的,使用HotSpot Serviceability Agent。这是example。
答案 5 :(得分:0)
我认为创建String的基础机制是StringBuilder,它最后组装String对象。至少我知道如果你有一个你想要改变的字符串,例如:
String str = "my String";
// and then do
System.out.println(str + "new content");
这样做是因为它从旧对象创建了一个StrigBuilder,并将其替换为从构建器构造的新对象。这就是为什么使用StringBuilder而不是常规字符串来增加内存效率的原因。
有一种方法可以使用String.intern()方法访问已创建的String池。它告诉java为相同的字符串使用相同的内存空间,并在内存中为您提供对该位置的引用。这也允许您使用==运算符来比较字符串,并且内存效率更高。
答案 6 :(得分:-2)
字符串池,因为它是存储在堆中的字符串池,用于exp:
String s="Test";
String s1="Test";
两者都存储在堆中并引用单个&#34;测试&#34;因此s1 = s, 而
String s=new String("Test");
是一个也存储在堆中但不同形式的对象s1 = s 参考here