String字符串的长度限制
字符串的长度限制
最近和第三方进行系统对接,约定是我们这边将文件的转成BASE64编码后,进行传输。对方反馈将BASE64字符串转成文件后,文件损坏打不开,怀疑是BASE64字符串的问题。我在本地尝试将base64字符串嵌入到img标签,
图片正常显示,说明字符串是没问题的。然后尝试将字符串保存为文件:
1 | String a = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAA......"; // 7w多个字符 |
点击运行时,在编译阶段提示 字符串常量
过长提示报错:
1 | Error:(6, 20) java: constant string too long |
常量池限制
在 Java语言规范 3.10.5 中定义,被 ""
括起来的字符称为 字符串字面常量
,通常叫它字面量。
字面量会被放到 字符串常量池
中。
在.java文件编译成.class的过程中,编译器会进行类型检查,数值范围检查等一系列的检查操作。并且编译成的字节码文件也是有一定格式的,才能被虚拟机正确的执行。
根据 Java虚拟机规范 4.4 常量池中定义,CONSTANT_String_info
用于表示 java.lang.String 类型的常量对象,格式如下:
1 | CONSTANT_String_info { |
其中,string_index 项的值必须是对常量池的有效索引, 常量池在该索引处的项必须是 CONSTANT_Utf8_info 结构,表示一组 Unicode 码点序列,这组 Unicode 码点序列最终会被初始化为一个 String 对象。
CONSTANT_Utf8_info 结构用于表示字符串常量的值:
1 | CONSTANT_Utf8_info { |
其中,length
代表数组 bytes[]
的长度,其类型为u2,bytes[]
是表示字符串值的byte数组。
在规范 Java虚拟机规范 4 中说明:u2表示两个字节的无符号数,那么1个字节有8位,2个字节就有16位。
16位无符号数可表示的最大值位2^16 - 1 = 65535。在 Java虚拟机规范 4.11 中也说明:字段和方法名称、字段和方法描述符以及其他常量字符串值(包括由ConstantValue(§4.7.2)属性引用的值)的长度被常量信息结构(§4.4.7)的16位无符号长度项限制为65535个字符。
也就是说,Class文件中常量池的格式规定了,其字符串常量的长度不能超过65535。
尝试定义长度为65535的字符串,来证实以上:
1 | public static void main(String[] args) { |
尝试使用javac编译,同样会得到”错误: 常量字符串过长”,那么原因是什么呢?
其实在编译时是有对字符串常量长度进行检查的。在javac代码中可以找到,具体在 Gen
类 checkStringConstant
方法:
1 | private void checkStringConstant(DiagnosticPosition var1, Object var2) { |
可以看到当类型为String,并且长于 >=
65535 时,就会导致编译失败。尝试65534个字符,则可以正常编译通过。
那为什么是 65534
,而不是 65535
呢?在 Java虚拟机规范 也有做解释:
if the Java Virtual Machine code for a method is exactly 65535 bytes long and ends with an instruction that is 1 byte long, then that instruction cannot be protected by an exception handler. A compiler writer can work around this bug by limiting the maximum size of the generated Java Virtual Machine code for any method, instance initialization method, or static initializer (the size of any code array) to 65534 bytes.
这其实是为了弥补java虚拟机的一个bug,所以将长度限制为65534
运行时限制
上面提到的这种String长度的限制是编译期的限制,也就是使用String s= “”; 这种字面值方式定义的时候才会有的限制。
String在运行期有没有限制呢,答案是有的,进入String类源码。里面有许多重载的构造方法,其中有的是支持用户输入长度的
1 | public String(byte bytes[], int offset, int length) |
length
使用 int
类型来定义的,也就说 length
最大长度也就是 Integer.MAX_VALUE。
int 是一个 32 位变量类型,取正数部分来算的话,他们最长可以有 2^31-1 = 2147483647 个
。这个值约等于4G,在运行期,如果String的长度超过这个范围,就可能会抛出异常。(在jdk 1.9之前)
1 | -2^31 ~ 2^31-1 = -2147483648 ~ 2147483647 个 16-bit Unicodecharacter |
总结
字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。
在运行期,长度不能超过Int的范围,否则会抛异常