尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

字节跳动·应届 Android 面试——String 为什么设计成不可变

发布时间:2026/9/2 16:29:44

资讯中心
01
ARTICLE

字节跳动·应届 Android 面试——String 为什么设计成不可变

字节跳动·应届 Android 面试——String 为什么设计成不可变
朋友去年面字节前面算法、项目都顺三面面试官来了句你就说说 String 吧为什么设计成不可变的他脱口而出因为 final 修饰线程安全。面试官点点头又问那new String(abc)创建了几个对象intern 在 JDK 7 之后有啥变化他卡了。最后三面挂在这。讲真String 是 Android 里出场率最高的类没有之一。字节尤其爱拿它当应届生的照妖镜——背得出 final 不算本事能把常量池、内存布局、版本差异讲清楚才算过关。这篇我把这几连环拆开讲透。一、不可变到底好在哪面试官先说String 不可变好处是啥候选人标准答法线程安全、可以进常量池复用、hash 值能缓存。都对但太浅。深度解析不可变immutable在 JVM 里意味着对象创建后状态不可改。String 的不可变带来四个实打实的好处1.字符串常量池String Pool复用字面量相同就重用同一份省内存。2.哈希缓存hashCode()算一次就存起来HashMap 的 key 用 String 时不用重复算。3.线程安全不可变对象天然线程安全多个线程读同一份无锁也不乱。4.安全类加载器用 String 当类名、网络连引用 String 当主机名若可变攻击者改了串就乱套了。普通答法 vs 高分答法普通线程安全能被常量池缓存。高分我会把第4点安全拎出来说——这是很多人忽略的。类加载时ClassLoader.loadClass(name)的 name 是 String若可变加载一半被改沙箱直接崩。这层讲出来考官知道你理解不可变为啥是底层刚需。二、它怎么做到不可变的源码层面试官那源码上它怎么保证不可变深度解析看String的声明以 Java 8 为例public final class String implements Serializable, ComparableString, CharSequence { private final char value[]; // 关键final 数组引用 private int hash; // 默认 0 }三个要点final class不能被继承杜绝子类篡改行为。private final char value[]数组引用 final不能指向别的数组且 private 无 setter外部改不了元素。所有看似修改的方法substring、concat、replace都返回新 String原串不动。补充Android 相关Java 9 起value从char[]改成byte[]紧凑字符串 Compact Strings一个 Latin-1 字符只占 1 字节省一半内存。Android 的 ART 也沿用类似优化。这细节一提面试官知道你跟过版本演进。高分答法我会追一句——final 数组只保证引用不变理论上反射还能改数组元素但这属于破坏私有契约正常代码不该碰而且 JVM 对 String 有内在化保护。展示你知道 final 的边界在哪。三、new String(abc) 到底几个对象这是本篇第二道核心问答也是翻车重灾区。面试官String s new String(abc);创建了几个对象深度解析答案分两步看1.类加载/编译期字面量abc进字符串常量池class 文件的常量池项首次用到时ldc指令在运行期常量池建一份。2.运行期newnew在堆上创建一个 String 对象它的value指向常量池那份abc的字符数据Java 7 实际是复用同一数组引用。所以字面量首次出现时常量池 1 个 堆上 1 个 2 个如果abc之前已在池里比如前面用过那常量池不再新建只有堆上 1 个。看字节码更清楚0: new #2 // class java/lang/String → 堆上对象 3: dup 4: ldc #3 // String abc → 常量池加载字面量 6: invokespecial #4 // String.init → 构造普通答法 vs 高分答法普通创建了 2 个对象。高分我会补取决于字面量是否已在池里并强调s abc是false堆对象 vs 池对象地址不同而s.intern() abc是true。这个等号辨析初级候选人十个有八个答错。四、intern() 与 JDK 7 的搬池革命本篇第三道核心问答版本差异是高分关键。面试官intern() 干嘛的JDK 7 之后有啥变化深度解析intern()返回字符串在常量池中的规范化引用。若池里已有相等串返回池中那个没有则把当前串加入池并返回。变化在内存位置JDK 6 及以前常量池在永久代PermGen空间小且 GC 不勤。intern()会把字符串复制一份实例进永久代。大量 intern 容易OutOfMemoryError: PermGen space。JDK 7 开始常量池搬到了堆Heap。intern()不再复制实例而是把堆中已存在的那个 String 的引用放进池。换句话说池里存的是引用而非副本。val s1 StringBuilder(ja).append(va).toString() println(s1.intern() s1) // JDK7 常 true池指向同一堆对象实战坑很多人以为intern()一定省内存其实在 Android 上乱用 intern 反而拖累——ART 的字符串池回收策略和 JVM 不同长生命周期的 intern 串会常驻。我的建议除非做超大量重复短串的去重如日志模板否则别手痒调 intern。高分答法我会点出JDK 7 搬池解决了 PermGen OOM 的老大难也解释了为啥现在intern()返回的是原对象引用——理解这点上面的为 true 才讲得通。五、开放追问 拼接与 substring 的内存往事面试官那平时拼接字符串性能很差吗深度解析分场景。Java 编译器对编译期确定的常量拼接会直接优化成.concat 结果甚至常量折叠ab编译完就是ab不产生 StringBuilder。但循环里的 是重灾区var s for (i in 0..10000) s i // 每次生成新 StringBuilder 新 StringO(n²) 临时对象正确写法是StringBuilder单线程或StringBuffer多线程val sb StringBuilder() for (i in 0..10000) sb.append(i) val s sb.toString()Android 专属踩坑Android 4.x 之前String.substring()共享原串的char[]导致截一小段却让整大串无法回收内存泄漏经典案例。Google 后来改成了复制子串数据才修复。这故事我每次讲都强调——不可变虽好但早期实现为了省拷贝走了共享数组的捷径反倒埋雷。高分答法补充Kotlin 的原生字符串、字符串模板${}在字节码层也是 StringBuilder 拼接别以为写法优雅就免了开销热点路径该用StringBuilder还是得用。收尾我的实战心得String 这题字节考的不是记忆是你有没有真正摸过 JVM 的内存布局。常量池在永久代还是堆、intern 复制还是引用、new 创建几个对象——这三连把背过和懂一刀切开。面试 Tips具体到答法被问不可变好处主动带上安全那条区分度最高。问 new 创建几个对象永远加前提字面量是否已在池别给死答案。提 JDK 7 搬池顺带说 intern 返回引用而非副本直接封神。聊拼接性能举个循环的 O(n²) 反例再给 StringBuilder 正解。我的看法是应届生把 String 讲透比刷十道算法题更能让面试官记住你——因为它能在一道题里串起内存、版本、线程三条线。评论区聊聊你曾在哪个 String 的坑里栽过是 substring 泄漏还是 intern OOM---点赞、在看、转发三连是对我最大的支持「Android 大厂面经·从入门到精通」连载系列上一篇第001篇 美团·初级 Android 面试——面向对象三大特性人人会背下一篇预告第003篇 快手·Android 开发面试——StringBuilder 和 StringBuffer 选错一个评论区聊聊你面试遇到过最难的 Android 问题是哪道关于本系列「Android 大厂面经·从入门到精通」是360篇连载系列覆盖美团、字节跳动、阿里巴巴、快手、百度、京东、华为、小米等30家公司从 Java 基础到架构师终面的真实面试内容。本篇属于阶段1·入门篇——Java 语言基础。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。