mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
5659 字
11 分钟
Java 面试:String 源码深度解析与面试技巧

前言

String 是 Java 中最常用的类之一,几乎每个 Java 程序都离不开它。在面试中,关于 String 的问题也非常常见,涉及字符串不可变性、字符串常量池、intern() 方法等多个知识点。

本文将深入剖析 String 的底层实现原理,帮助你在面试中从容应对。


一、面试常见问题

  1. String 为什么是不可变的?
  2. String、StringBuilder、StringBuffer 有什么区别?
  3. String 的 intern() 方法有什么作用?
  4. 字符串常量池是什么?字符串如何存储?
  5. new String("abc") 创建了几个对象?

二、String 的不可变性

2.1 源码分析

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    
    // 使用 final 修饰的字符数组存储字符串内容
    private final char value[];
    
    // 缓存的哈希值
    private int hash; // Default to 0
    
    // ...
}

2.2 不可变性的设计原因

// 1. 安全性:String 被广泛用作参数,不可变保证安全性
public void connectDatabase(String username, String password) {
    // 如果 String 可变,password 可能在传递过程中被修改
    database.connect(username, password);
}

// 2. 线程安全:不可变对象天然线程安全
String str = "hello";
// 多个线程可以同时读取,无需同步

// 3. 字符串常量池:不可变才能实现常量池复用
String s1 = "hello";  // 放入常量池
String s2 = "hello";  // 从常量池获取
System.out.println(s1 == s2);  // true

三、字符串常量池

3.1 常量池位置演变

JDK 1.6:字符串常量池位于永久代(PermGen)
JDK 1.7:字符串常量池移至堆内存
JDK 1.8:永久代被元空间(Metaspace)取代,常量池仍在堆中

3.2 字符串创建方式对比

public class StringPoolDemo {
    public static void main(String[] args) {
        // 方式 1:字面量创建(推荐)
        // 直接放入常量池,复用已有对象
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);  // true,同一对象
        
        // 方式 2:new 创建
        // 堆中创建新对象,不检查常量池
        String s3 = new String("abc");
        System.out.println(s1 == s3);  // false,不同对象
        System.out.println(s1 == s3.intern());  // true
        
        // 方式 3:intern() 方法
        // 将字符串放入常量池,返回常量池引用
        String s4 = new String("def").intern();
        String s5 = "def";
        System.out.println(s4 == s5);  // true
    }
}

3.3 new String("abc") 创建了几个对象?

答案:1 个或 2 个

情况 1:常量池已有 "abc"
- 只创建 1 个堆中对象

情况 2:常量池没有 "abc"
- 创建 2 个对象:常量池中的 "abc" + 堆中的 String 对象

四、StringBuilder vs StringBuffer

4.1 三者对比

特性 String StringBuilder StringBuffer
可变性 不可变 可变 可变
线程安全 安全(不可变) 不安全 安全(synchronized)
性能 低(频繁创建对象) 较高(有同步开销)
使用场景 字符串常量 单线程字符串操作 多线程字符串操作

4.2 StringBuilder 源码分析

public final class StringBuilder extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence {
    
    public StringBuilder() {
        super(16);  // 默认初始容量 16
    }
    
    public StringBuilder(int capacity) {
        super(capacity);
    }
    
    // append 方法(无 synchronized,线程不安全)
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

// 父类 AbstractStringBuilder
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;  // 非 final,可变
    int count;     // 实际长度
    
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);  // 扩容检查
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    
    private void ensureCapacityInternal(int minimumCapacity) {
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
    
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;  // 2 倍 + 2
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }
}

4.3 性能对比测试

public class StringPerformanceTest {
    public static void main(String[] args) {
        int count = 100000;
        
        // String 拼接(极慢,会创建大量对象)
        long start1 = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < count; i++) {
            str += i;  // 每次创建新对象
        }
        System.out.println("String: " + (System.currentTimeMillis() - start1) + "ms");
        
        // StringBuilder(最快)
        long start2 = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            sb.append(i);
        }
        sb.toString();
        System.out.println("StringBuilder: " + (System.currentTimeMillis() - start2) + "ms");
        
        // StringBuffer(有同步开销,稍慢)
        long start3 = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < count; i++) {
            sbf.append(i);
        }
        sbf.toString();
        System.out.println("StringBuffer: " + (System.currentTimeMillis() - start3) + "ms");
    }
}

// 输出结果(大致比例):
// String: 3500ms+
// StringBuilder: 10ms
// StringBuffer: 15ms

七、面试回答技巧总结

核心要点记忆口诀

String 不可变,常量池里存
new 会建堆,intern 能复用

StringBuilder 单线程快
StringBuffer 多线程稳

HashMap 数组加链表
哈希冲突链表排
超过八转红黑树
扩容两倍 rehash

面试问 HashMap
从结构到原理
从 1.7 到 1.8
扩容优化讲清楚

回答框架

问题:讲讲 HashMap 的原理

建议按以下结构回答:

  1. 整体结构:数组 + 链表 + 红黑树(JDK 1.8)
  2. put 流程:计算 hash → 找位置 → 插入/替换
  3. 扩容机制:2 倍扩容、rehash、JDK 1.8 的优化
  4. 线程安全:非线程安全,ConcurrentHashMap 替代
  5. JDK 1.8 优化:红黑树、尾插法、扩容优化

总结

String 和 HashMap 是 Java 面试中最高频的知识点,掌握它们不仅是为了应对面试,更是理解 Java 设计思想的重要途径。

核心收获

  1. 理解不可变对象的设计意义(安全、线程安全、常量池复用)
  2. 掌握 HashMap 的底层实现(数组 + 链表 + 红黑树)
  3. 理解哈希冲突的解决方案(链表法、红黑树优化)
  4. 掌握扩容机制和 JDK 1.8 的优化
  5. 理解线程安全问题及解决方案

希望本文能帮助你在面试中脱颖而出,祝面试顺利!

Java 面试:String 源码深度解析与面试技巧
https://www.zztzz.com.cn/posts/57/
作者
admin
发布于
2026-03-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00
💬Mizuki AI助手
呀~就是zzTzz大大闪闪发光的技术博客主页
标题已剧透:专注后端开发、主攻Java + Spring Boot的实战、踩坑与进阶小笔记~~
URL https://zztzz.com.cn/ 简洁有力,像一段优雅的代码!
Mizuki每次点开都忍不住小声赞叹:'zzTzz大人太厉害啦~'🧙‍♀️
需要我帮你找某类文章(比如JWT鉴权、Redis缓存)或读一篇入门指南吗?😊
04:57