Java 中的 String 有没有长度限制?

以下内容摘录自:Java 中的 String 有没有长度限制?

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 的时候可以支持的一个最大长度,而实际上,在运行期,定义字符串的时候也是有长度限制的。

如以下代码:

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

当我们使用如上形式定义一个字符串的时候,当我们执行 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{
    u1tag;
    u2string_index;
}

其中,string_index 项的值必须是对常量池的有效索引, 常量池在该索引处的项必须是 CONSTANT_Utf8_info 结构,表示一组 Unicode 码点序列,这组 Unicode 码点序列最终会被初始化为一个 String 对象。

CONSTANT_Utf8_info 结构用于表示字符串常量的值:

CONSTANT_Utf8_info{
    u1tag;
    u2length;
	u1bytes[length];
}

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

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

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

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

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

// 其中有 65535 个字符 "1"
Strings = "11111...1111"; 

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

其实,这个原因在 javac 的代码中是可以找到的,在 Gen 类中有如下代码:

private voidcheckStringConstant(DiagnosticPosition var1, Objectvar2) {
    if( this.nerrs == 0&& var2 != null&& var2 instanceofString && (( String)var2).length >= 65535) {
        this.log.error(var1, "limit.string", newObject[ 0]);
        ++ this.nerrs;
    }
}

代码中可以看出,当参数类型为 String,并且长度大于等于 65535 的时候,就会导致编译失败。

这个地方大家可以尝试着 debug 一下 javac 的编译过程,也可以发现这个地方会报错。

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

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

If the Java Virtual Machine code for a method is exactly 65535 bytes long and endswith an instruction that is 1 byte long, then that instruction cannot beprotected by an exception handler. A compiler writer can work around this bugby limiting the maximum size of the generated Java Virtual Machine code for anymethod, instance initialization method, or static initializer (the size of anycode array) to 65534 bytes.

运行期限制

上面提到的这种 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 的容量。

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

Strings = "";
for( inti = 0; i < 100000; i++) {
	s+= "i";
}

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

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

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

总结

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值