面试回答
有,编译期和运行期不一样。
编译期需要用 CONSTANT_Utf8_info 结构用于表示字符串常量的值,而这个结构是有长度限制,他的限制是 65535。
运行期,String 的 length 参数 int 类型的,那么也就是说,String 定义的时候,最大支持的长度就是 int 的最大范围值。根据 Integer 类的定义,java.lang.Integer#MAX_VALUE
的最大值是 2^31 -1 ;
知识扩展
常量池限制
我们知道,javac 是将文件编译成 class 文件的一个命令,那么在 Class 文件生成过程中,就需要遵守一定的格式。
根据《Java虚拟机规范》中第 4.4 章节常量池的定义,CONSTANT_String_info 用于表示 java.lang.String 类型的常量对象,格式如下:
CONSTANT_String_info{
u1 tag;
u2 string_index;
}
其中,string_index 项的值必须是对常量池的有效索引,常量池在该索引处的项必须是 CONSTANT_Utf8_info 结构,表示一组 Unicode 码点序列,这组 Unicode 码点序列最终会被初始化为一个 String 对象。
CONSTANT_Utf8_info 结构用于表示字符串常量的值:
CONSTANT_Utf8_info{
u1 tag;
u2 length;
u2 bytes[length];
}
其中,length 则指明了 bytes[] 数组的长度,其类型为 u2。
通过翻阅《规范》,我们可以获悉。u2 表示两个字节的无符号数,那么1个字节有8位,2个字节就有16位。
16位无符号数可表示的最大值 2^16 - 1 = 65535。
也就是说,Class 文件中常量池的格式规定了,其字符串常量的长度不能超过 65535。
那么,我们尝试使用以下方式定义字符串:
String s="11111...11111";//其中有 65535 个字符 "1"
尝试使用 javac 编译,同样会得到“java:常量字符串过长”的错误提示,那么原因是什么呢?
其实,这个原因在 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;
}
}
代码中可以看出,当参数类型为 String,并且长度大于等于 65535 的时候,就会导致编译失败。
运行期限制
上面提到的这种 String 长度的限制是编译期的限制,也就是使用 String s= "" ; 这种字面值方式定义的时候才会有的限制。
那么。String 在运行期有没有限制呢,答案是有的。
String 类中有很多重载的构造函数,其中有几个是支持用户传入 length 来执行长度的:
public String(byte bytes[], int offset, int length)
可以看到,这里面的参数 length 是使用 int 类型定义的,那么也就是说,String 定义的时候,最大支持的长度就是 int 的最大范围值。
根据 Integer 类的定义,java.lang.Integer#MAX_VALUE
的最大值 2^31 - 1;这个值约等于 4G,在运行期,如果 String 的长度超过这个范围,就可能会抛出异常。(在 jdk 1.9 之前)