大家都用过String字符串,有的人可能还不知道它的长度在某些方面是有一些限制。
public String(byte bytes[], int offset, int length);
这是java.lang.String
中的一个构造函数,可以看到它的长度是int类型,int的最大取值是2^31-1.但是我们却不能认为String支持的最大长度是这么大,这个长度的范围是JVM在运行期对String的一种限制,并非是编译器定义字符串的时候的限制。因为JVM是有StringTable
字符串常量池这个概念的,如果编译器里面允许的最大长度那就是int类型的最大范围,大家知道这意味这什么吗,这意味着一个String字符串可以支持4G长度大小了,这怎么可能允许?
String s = "111111.....1111";// 其中有10万个字符"1";
当执行javac编译时会抛出异常,IDEA会提示你常量字符串过长
你可能很好奇为什么不是int类型的长度限制。这其实跟Java虚拟机规范有关,当我们按照String s= "xxx"的形式定义字符串时,xxx被我们称为字面量,这种字面量在编译之后会以常量的形式进入Class常量池,既然要进入常量池,就需要遵循常量池的有关规定
《Java虚拟机规范》官方链接
https://docs.oracle.com/javase/specs/jvms/se7/html/index.html
根据《Java虚拟机规范》中的4.4节定义,CONSTANT_String_info
用于表示java.lang.String
类型的常量对象,格式:
CONSTANT_String_info{
u1 tag;
u2 string_index;
}
其中,string_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Uft8_info结构,表示一组Unicode字符绪列,这组Unicode字符序列最终会被初始化为一个String对象
CONSTANT_Uft8_info结构用于表示字符串常量的值
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
其中length指明了bytes[]数组的长度,其类型为u2
根据《Java虚拟机规范》,u2表示2字节的无符号数,1字节有8位,2字节有16位,而16位无符号数可以表示的最大值为2^16-1=65535
也就是说,Class文件中常量池的格式规定了其字符串常量的长度不能超过65535
String s = "11111...11"; //其中有65535个1
但是编译时,仍然会报错,还是常量字符串过长,有的人可能会说不是说最大时65535吗?为什么这里还会报错,这个原因可以在javac的代码中可以找到Gen类中有如下代码
private void checkStringConstant(DiagnosticPosition var1, Object var2) {
if (this.nerrs == 0 && var2 != null && var2 instanceof String && ((String)var2).length() >= 65535) {
this.log.error(var1,"limit.string", new Object[0]);
++this.nerrs;
}
}
上面就是编译期的限制了,运行期的限制就是int类型的4GB的大小限制了,我们可以通过以下代码实现
String s ="";
for (int = 0; i < 100000; i++) {
s+= "" +i;
}