我正在查看String的openjdk实现,并且私有的每个实例成员看起来像:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
[...]
}
但我知道Java使用字符串的引用和池,以避免重复。我天真地期待一种流行语成语,其中String实际上只是一个impl的参考。到目前为止我还没有看到。如果我放一个String x,有人可以解释Java如何知道使用引用;我的一个班级的成员?
附录:这可能是错误的,但是如果我处于32位模式,我应该计算:4个字节用于引用“value []”,4个字节用于偏移,4个用于计数,4个用于哈希用于所有实例类String?这意味着写“String x;”在我的一个班级自动添加至少32个字节到我班级的“重量”(我可能在这里错了)。
答案 0 :(得分:2)
偏移/计数字段与池/ intern()
问题有些正交。当你有类似的东西时,会发生偏移和计数:
String substring = myString.substring(5);
实现此方法的一种方法是:
char[]
元素myString.length() - 5
myString.length()
到新的char[]
substring
是使用此新char[]
构建的
substring.charAt(i)
直接转到chars[i]
substring.length()
直接转到chars.length
如您所见,这种方法是O(N) - 其中N是新字符串的长度 - 并且需要两个分配:新的String和新的char []。因此,substring
通过重新使用原始char []来工作,但使用偏移量:
substring.offset
= myString.offset + newOffset
substring.count
= myString.count - newOffset
myString.chars
作为substring
的字符数组
substring.charAt(i)
转到chars[i+substring.offset]
substring.length()
转到substring.count
请注意,我们不需要创建新的char [],更重要的是,我们不需要将旧的char []中的chars复制到新的char [因为没有新的char]。所以这个操作只是O(1),只需要一个分配,即新String的分配。
答案 1 :(得分:1)
Java 始终使用对任何对象的引用。没有办法让它不使用引用。至于字符串池,这是由编译器为字符串文字实现的,并在运行时通过调用String.intern
来实现。很自然地,String
的大多数实现都不知道它是否正在处理常量池引用的实例。
答案 2 :(得分:0)
Java字符串是不可变的。这意味着实现可以对内部表示做很多事情,而不会破坏任何应用程序代码。
请注意,Java String.intern()
已在Oracle的JDK实现中定义为本机。 本机代码可以访问对象的所有字段,并且可以更改水下的引用。因此,实现者必须做的就是将引用和偏移更改为字符串被插入的位置和瞧。当然这打破了类的不变性,所以这意味着intern()更新更好地是线程安全的。
当您在新生成的字符串上调用intern()
时,可以检查字段会发生什么。如果没有任何反应,则可能是引用本身包含内存位置。 Java语言规范没有定义如何实现引用。
答案 3 :(得分:0)
接受的答案和其他答案已过时。在Java 7更新6之后,Java中的字符串不再使用偏移量,也不会针对子字符串优化进行调整。相反,每个子字符串都会创建一个新的字符串副本。
如果您想使用原始字符串实现,则必须使用CharSequence。
有关详细信息:https://jaxenter.com/the-state-of-string-in-java-107508.html