关于String是否有长度限制的问题,我之前单独写过一篇文章分析过。最近抽空复习了下这个问题,发现了一些新的感悟。所以我要重新组织这个内容。这次在上一篇文章的基础上,除了增加了一些验证过程外,还进行了一些错误修正。在这次的分析过程中,我会尝试调试Jdk的编译过程,同时会参考一些JVM规范和其他对这个知识点的全面介绍。因为这道题涉及到Java编译原理相关的知识,所以通过视频讲解会更容易理解。视频我上传到了B站,大家可以点击文末阅读原文查看。String的长度限制要弄清楚这个问题,首先我们需要浏览String的源代码,看看是否有长度限制或定义。String类中有很多重载的构造函数,其中一些重载的构造函数支持用户传入length来执行长度:publicString(bytebytes[],inoffset,intlength),可以看到,这里的参数length是使用int定义的type是的,也就是说,当定义String时,支持的最大长度是int的最大范围值。根据Integer类的定义,java.lang.Integer#MAX_VALUE的最大值为2^31-1;那么,我们是不是可以认为String能够支持的最大长度就是这个值呢?其实不是的,这个值只是在运行时,我们在构造String的时候可以支持一个最大长度,但是实际上在运行时,定义一个字符串也是有长度限制的。比如下面的代码:Strings="11111...1111";//有100,000个字符"1"当我们用上面的形式定义一个字符串的时候,当我们执行javac编译的时候,会抛出异常,提示如下:错误:常量字符串太长。那么String的构造函数指定的长度可以支持2147483647(2^31-1)。上面的形式定义的为什么不能编译呢?其实就是Strings="xxx";的形式在定义String的时候,xxx被我们称为一个字面量,这个字面量编译后会以常量的形式进入Class常量池。那么问题来了,因为要进入常量池,就必须遵守常量池的相关规定。常量池限制我们知道javac是将Java文件编译成class文件的命令,所以在生成Class文件的过程中,需要遵循一定的格式。根据♂第4.4章常量池的定义,使用CONSTANT_String_info表示一个java.lang.String类型的常量对象,格式如下:CONSTANT_String_info{u1tag;u2string_index;}其中,string_index项的值必须对常量池Index有效,该索引处的常量池项必须是一个CONSTANT_Utf8_info结构,表示一组Unicode码位序列,这组Unicode码位序列最终会被初始化为一个字符串对象。CONSTANT_Utf8_info结构体用于表示一个字符串常量的值:CONSTANT_Utf8_info{u1tag;u2length;u1bytes[length];}其中length表示bytes[]数组的长度,其类型为u2。通过阅读《规范》,我们可以学习。U2表示两个字节的无符号数,那么1字节有8位,2字节有16位。一个16位无符号数所能表示的最大值是2^16-1=65535。也就是说,Class文件中常量池的格式规定,其字符串常量的长度不能超过65535。然后,我们尝试这样定义字符串:Strings="11111...1111";//有65535万个字符"1"试图用javac编译,也会得到"error:Theconstantstring太长了”,那是什么原因呢?其实在javac的代码中可以找到这个原因。Gen类中有如下代码:privatevoidcheckStringConstant(DiagnosticPositionvar1,Objectvar2){if(this.nerrs==0&&var2!=null&&var2instanceofString&&((String)var2).length()>=65535){this.log.error(var1,"limit.string",newObject[0]);++this.nerrs;}}从代码中可以看出,当参数类型为String,且长度大于等于65535时,会导致编译失败。可以尝试在这个地方调试javac编译过程(视频中有调试java编译过程的方法),也可以发现这个地方会报错。如果我们尝试定义一个65534个字符的字符串,我们会发现它编译得很好。其实这个值在《Java虚拟机规范》中也有解释:如果一个方法的Java虚拟机代码正好是65535字节长,并且以一条1字节长的指令结束,那么这条指令就不能被异常处理程序保护。编译器编写者可以通过将任何方法、实例初始化方法或静态初始化程序(任何代码数组的大小)生成的Java虚拟机代码的最大大小限制为65534字节来解决此错误。对String长度的限制是编译时的一个限制,即仅在使用Strings=""时存在;这个字面值定义。所以。String在运行时有限制吗?答案是肯定的。就是我们前面提到的Integer.MAX_VALUE。这个值约等于4G。在运行时,如果String的长度超过这个范围,可能会抛出异常。(jdk1.9之前)int是32位的变量类型。Ifthepositivepartiscounted,theycanhaveupto2^31-1=214748364716-bitUnicodecharacter2147483647*16=34359738352bits34359738352/8=4294967294(Byte)4294967294/1024=4194303.998046875(KB)4194303.998046875/1024=4095.9999980926513671875(MB)4095.9999980926513671875/1024=3.99999999813735485076904296875(GB)有近4G的容量。很多人都有疑问。编译时要求最大长度小于65535。怎么会在运行时大于65535呢?这个其实很常见,比如下面的代码:Strings="";for(inti=0;i<100000;i++){s+="i";}得到的字符串长度为100000。遇到这个问题。在之前的系统连接中,需要传输高清图片。约定的传输方式是对方将图片转成BASE6编码,我们收到后转成图片。将BASE64编码的内容分配给字符串时,会抛出异常。总结字符串有长度限制。编译时要求字符串常量池中的常量不超过65535,javac执行时最大值控制为65534。运行时,长度不能超过Int的范围,否则会抛出异常。最后是这个知识点,我录制了一个视频,里面有关于如何进行实验测试、如何查阅Java规范、如何deubgjavac的技巧。
