哪吒说精通字符串,面试官竟然问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的方法),也可以发现这个地方会报错。

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

其实,关于这个值,在《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

运行期限制

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

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

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

2^31-1 =2147483647 个 16-bit Unicodecharacter2147483647 * 16 = 34359738352 位34359738352 / 8 = 4294967294 (Byte)4294967294 / 1024 = 4194303.998046875 (KB)4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)复制代码

有近 4G 的容量。

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

String s ="";for(int i = 0; i <100000 ; i++) {    s+="i";}复制代码

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

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

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

总结

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

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

作者:哪吒通天之路

链接:https://juejin.im/post/5f0419b2f265da22c75735f5

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350