您的位置 首页 java

面试官竟然问我Java中的String有没有长度限制!?| 附视频讲解

关于String有没有长度限制的问题,我之前单独写过一篇文章分析过,最近我又抽空回顾了一下这个问题,发现又有了一些新的认识。于是准备重新整理下这个内容。

这次在之前那篇文章的基础上除了增加了一些验证过程外,还有些错误内容的修正。我这次在分析过程中会尝试对 Jdk 的编译过程进行debug,并且会参考一些JVM规范等全方面的介绍下这个知识点。

因为这个问题涉及到 Java 的编译原理相关的知识,所以通过视频的方式讲解会更加容易理解一些,视频我上传到了B站,大家可以点击:,查看视频

String的长度限制

想要搞清楚这个问题,首先我们需要翻阅一下String的源码,看下其中是否有关于长度的限制或者定义。

String类中有很多重载的 构造函数 ,其中有几个是支持用户传入length来执行长度的:

 public String(byte bytes[], int offset, int length)   

可以看到,这里面的参数length是使用int类型定义的,那么也就是说,String定义的时候,最大支持的长度就是int的最大范围值。

根据Integer类的定义,java.lang.Integer#MAX_VALUE的最大值是2^31 – 1;

那么,我们是不是就可以认为String能支持的最大长度就是这个值了呢?

其实并不是,这个值只是在运行期,我们构造String的时候可以支持的一个最大长度,而实际上,在运行期,定义 字符串 的时候也是有长度限制的。

如以下代码:

 String s = "11111...1111";//其中有10万个字符"1"  

当我们使用如上形式定义一个字符串的时候,当我们执行 javac 编译时,是会抛出异常的,提示如下:

 错误: 常量字符串过长  

那么,明明String的构造函数指定的长度是可以支持2147483647(2^31 – 1)的,为什么像以上形式定义的时候无法编译呢?

其实,形如String s = “xxx”;定义String的时候,xxx被我们称之为字面量,这种字面量在编译之后会以常量的形式进入到Class常量池。

那么问题就来了,因为要进入常量池,就要遵守常量池的有关规定。

常量池限制

我们知道,javac是将Java文件编译成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;

    u1 bytes[length];

}  

其中,length则指明了 bytes[]数组的长度,其类型为u2,

通过翻阅《规范》,我们可以获悉。u2表示两个字节的无符号数,那么1个字节有8位,2个字节就有16位。

16位无符号数可表示的最大值位2^16 – 1 = 65535。

也就是说,Class文件中常量池的格式规定了,其字符串常量的长度不能超过65535。

那么,我们尝试使用以下方式定义字符串:

  String s = "11111...1111";//其中有65535万个字符"1"  

尝试使用javac编译,同样会得到”错误: 常量字符串过长”,那么原因是什么呢?

其实,这个原因在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的时候,就会导致编译失败。

这个地方大家可以尝试着debug一下javac的编译过程(视频中有对java的编译过程进行debug的方法),也可以发现这个地方会报错。

如果我们尝试以35534个字符定义字符串,则会发现可以正常编译。

其实,关于这个值,在《Java虚拟机规范》也有过说明:

运行期限制

上面提到的这种String长度的限制是编译期的限制,也就是使用String s= “”;这种字面值方式定义的时候才会有的限制。

那么。String在运行期有没有限制呢,答案是有的,就是我们前文提到的那个Integer.MAX_VALUE ,这个值约等于4G,在运行期,如果String的长度超过这个范围,就可能会抛出异常。(在jdk 1.9之前)

int 是一个 32 位变量类型,取正数部分来算的话,他们最长可以有

 2^31-1 =2147483647 个 16-bit Unicodecharacter



2147483647 * 16 = 34359738352 位

34359738352 / 8 = 4294967294 (Byte)

4294967294 / 1024 = 4194303.998046875 (KB)

4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)

4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)  

有近 4G 的容量。

很多人会有疑惑,编译的时候最大长度都要求小于35535了,运行期怎么会出现大于65535的情况呢。这其实很常见,如以下代码:

 String s = "";

for (int i = 0; i <100000 ; i++) {

    s+="i";

}  

得到的字符串长度就有10万,另外我之前在实际应用中遇到过这个问题。

之前一次系统对接,需要传输高清图片,约定的传输方式是对方将图片转成BASE6编码,我们接收到之后再转成图片。

在将BASE64编码后的内容赋值给字符串的时候就抛了异常。

总结

字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。

在运行期,长度不能超过Int的范围,否则会抛异常。

最后,这个知识点 ,我录制了视频,其中有关于如何进行实验测试、如何查阅Java规范以及如何对javac进行deubg的技巧。欢迎进一步学习。

点击:,查看视频。

关于作者 :Hollis(ID:hollischuang),一个对Coding有着独特追求的人,现任 阿里巴巴 技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。

文章来源:智云一二三科技

文章标题:面试官竟然问我Java中的String有没有长度限制!?| 附视频讲解

文章地址:https://www.zhihuclub.com/174381.shtml

关于作者: 智云科技

热门文章

网站地图