我说我精通字符串,面试官竟然问我Java中的String有没有长度限制!?|附视频讲解
关于String有没有长度限制的问题,我之前单独写过一篇文章分析过,最近我又抽空回顾了一下这个问题,发现又有了一些新的认识。于是准备重新整理下这个内容。
这次在之前那篇文章的基础上除了增加了一些验证过程外,还有些错误内容的修正。我这次在分析过程中会尝试对Jdk的编译过程进行debug,并且会参考一些JVM规范等全方面的介绍下这个知识点。
因为这个问题涉及到Java的编译原理相关的知识,所以通过视频的方式讲解会更加容易理解一些,视频我上传到了B站:https://www.bilibili.com/video/BV1uK4y1t7H1/。
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 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的情况呢。这其实很常见,如以下代码:
String s = "";
for (int i = 0; i <100000 ; i++) {
s+="i";
}
得到的字符串长度就有10万,另外我之前在实际应用中遇到过这个问题。
之前一次系统对接,需要传输高清图片,约定的传输方式是对方将图片转成BASE6编码,我们接收到之后再转成图片。
在将BASE64编码后的内容赋值给字符串的时候就抛了异常。
总结
字符串有长度限制,在编译期,要求字符串常量池中的常量不能超过65535,并且在javac执行过程中控制了最大值为65534。
在运行期,长度不能超过Int的范围,否则会抛异常。
最后,这个知识点 ,我录制了视频(https://www.bilibili.com/video/BV1uK4y1t7H1/),其中有关于如何进行实验测试、如何查阅Java规范以及如何对javac进行deubg的技巧。欢迎进一步学习。
本文由博客一文多发平台 OpenWrite 发布!
我说我精通字符串,面试官竟然问我Java中的String有没有长度限制!?|附视频讲解的更多相关文章
- 我说精通字符串,面试官竟然问我 Java 中的 String 有没有长度限制?
String 是 Java 中很重要的一个数据类型,除了基本数据类型以外,String 是被使用的最广泛的了,但是,关于 String,其实还是有很多东西容易被忽略的. 就如本文我们要讨论的问题:Ja ...
- 【Nginx】面试官竟然问我Nginx如何生成缩略图,还好我看了这篇文章!!
写在前面 今天想写一篇使用Nginx如何生成缩略图的文章,想了半天题目也没想好,这个题目还是一名读者帮我起的.起因就是这位读者最近出去面试,面试官正好问了一个Nginx如何生成缩略图的问题.还别说,就 ...
- 那些面试官必问的JAVA多线程和并发面试题及回答
Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环 ...
- 去年去阿里面试,面试官居然问我Java类和对象,我是这样回答的!
1.谈谈你对Java面向对象的理解? 面向对象就是把构成问题的事务分解成一个个对象,建立对象的目的不是一个步骤,而是为了描述一个事务在解决问题中的行为.类是面向对象的一个重要概念,类是很多个具有相同属 ...
- 我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?
在Java基础中,集合类是很关键的一块知识点,也是日常开发的时候经常会用到的.比如List.Map这些在代码中也是很常见的. 个人认为,关于HashMap的实现,JDK的工程师其实是做了很多优化的,要 ...
- 面试突击 | Redis 如何从海量数据中查询出某一个 Key?附视频
1 考察知识点 本题考察的知识点有以下几个: Keys 和 Scan 的区别 Keys 查询的缺点 Scan 如何使用? Scan 查询的特点 2 解答思路 Keys 查询存在的问题 Scan 的使用 ...
- JVM工作原理和特点(一些二逼的逼神面试官会问的问题)
作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...
- 面试官再问我如何保证 RocketMQ 不丢失消息,这回我笑了!
最近看了 @JavaGuide 发布的一篇『面试官问我如何保证Kafka不丢失消息?我哭了!』,这篇文章承接这个主题,来聊聊如何保证 RocketMQ 不丢失消息. 0x00. 消息的发送流程 一条消 ...
- 面试官突然问我MySQL存储过程,我竟然连基础都不会!(详细)
所有知识体系文章,GitHub已收录,欢迎Star!再次感谢,愿你早日进入大厂! GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual ...
随机推荐
- php用户量剧增导致cpu100%解决办法
在php扩展里边开启opcache扩展,此扩展是解析php的缓存机制,每次解析都要消耗cpu,所以有大量的fpm进程去占用cpu,开启此扩展之后cpu就瞬间下来了,只解析第一次,往后的都使用缓存.很好 ...
- 使用cat命令清空文件
比如要清空 /www/aaa.txt cat /dev/null > /www/aaa.txt; 即可.
- python学习14集合
'''''''''集合:set1.定义:是一个无序的不重复元素序列.2.表示:大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用 ...
- 十六, Oracle约束
前言 数据的完整性用于确保数据库数据遵从一定的商业和逻辑规则,在oracle中,数据完整性可以使用约束.触发器.应用程序(过程.函数)三种方法来实现,在这三种方法中,因为约束易于维护,并且具有最好的性 ...
- Golang项目部署
文章来源:https://goframe.org/deploymen... 一.独立部署 使用GF开发的应用程序可以独立地部署到服务器上,设置为后台守护进程运行即可.这种模式常用在简单的API服务项目 ...
- values/colors.xml
<color name="abcd">#FFC4C4C4</color> <color name="white">#FFFF ...
- unix域源码解析
首先我们先要创建一个用于通信的结构unix_proto_data ,并初始化某些字段 static int unix_proto_create(struct socket *sock, int pro ...
- mac OS 安装 Node.js
打开Node.js官网https://nodejs.org/zh-cn/选择你需要的版本 下载安装 安装完成 输入命令 node -v 查看版本号 输入命令 npm -v 查看版本号
- HyperLeger Fabric开发(三)——HyperLeger Fabric架构
HyperLeger Fabric开发(三)--HyperLeger Fabric架构 一.HyperLeger Fabric逻辑架构 1.HyperLeger Fabric逻辑架构简介 Fabric ...
- Docker学习之搭建MySql容器服务
描述 MySQL 5.6 SQL数据库服务器Docker镜像,此容器映像包含用于OpenShift的MySQL 5.6 SQL数据库服务器和一般用法.用户可以选择RHEL和基于CentOS的图像.然后 ...