【Java.八股】高频面试题:==与equals(),Object 与 String方法 ,StringBuffer,StringBuilder

📅 2026/7/9 4:58:00
【Java.八股】高频面试题:==与equals(),Object 与 String方法 ,StringBuffer,StringBuilder
个人主页代码不加冰欢迎来访作者简介java后端学习者❄️个人专栏LeetCode刷题日记 苍穹外卖日记SSM框架深入JavaWeb✨命运的结局尽可永在不屈的挑战却不可须臾或缺前言大家好我是代码不加冰今天上午用gpt5.5扫了好几个项目然后写了个skill用着挺爽的稍微有点上瘾了根本用不起这还是群友分享的在此特别鸣谢。自我感觉让AI写项目比自己写项目还累于是休息一会写篇文章至于写什么呢那就分享一下前几天学的八股吧。摘要本文面向 Java 初学者与面试备战者硬核盘点Object类与String类的核心八股文考点。前半部分深入剖析Object核心方法用严谨的逻辑与大厂视角详解与equals()的本质区别并揭秘重写equals()必须重写hashCode()的底层契约及哈希冲突机制。后半部分对高频StringAPI 进行分类归纳多维度横向对比String、StringBuffer与StringBuilder的优缺点并直击循环体中使用拼接字符串的编译底层大坑。全文拒绝死记硬背助你轻松建立扎实的 Java 基础认知体系。一。Object 类常用方法在 Java 中所有的类都隐式地继承了java.lang.Object。它里面包含了一些核心方法面试中常问的有以下几个equals(Object obj)用来指示其他某个对象是否与此对象“相等”。hashCode()返回该对象的哈希码整数主要用于哈希表如HashMap。toString()返回对象的字符串表示默认是类名十六进制内存地址通常需要重写。getClass()返回对象的运行时类反射的基础。clone()创建并返回此对象的一个副本涉及浅拷贝与深拷贝。wait() / notify() / notifyAll()多线程协作的核心方法用于线程的等待与唤醒。二。 与 equals() 对比这是八股文中经典的送分题但一定要答出深度。1.运算符是一个比较运算符它的核心逻辑非常纯粹基本数据类型如int,double比较的是具体的值是否相等。引用数据类型如String, 自定义对象比较的是内存中的首地址是否相同即判断它们是不是同一个对象。2.equals()方法equals()是一个方法只能由对象调用。默认情况下如果你不重写equals()它直接调用Object类中的默认实现。而Object的默认实现就是用见下方源码。// Object 类中的原始源码 public boolean equals(Object obj) { return (this obj); }重写之后像String、Integer、Date等内置类都重写了equals()方法将其改为比较对象的内容属性值是否相等而不是比较内存地址。3. 核心区别对比表特征 运算符equals() 方法性质运算符Keyword类的方法Method基本类型比较比较值是否相等无法用于基本类型编译报错引用类型比较比较内存地址是否是同一个对象默认比较地址重写后比较内容是否相等可定制性无法重写逻辑固定可以由开发者任意重写定制比较逻辑三。hashCode() 与 equals()这是八股文中的进阶题面试官非常看重是否理解哈希表如 HashMap、HashSet的底层原理。1. 什么是 hashCode()hashCode()方法的作用是获取一个整型的哈希码散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。Java 中的哈希表利用它来大幅提高查询和插入的效率。2. 为什么重写 equals 必须要重写 hashCode这是 Java 的官方规定。简单一句话总结如果两个对象通过equals()判定相等那么它们的hashCode()必须相同。当你想往HashSet里放一个新对象时集合会按以下步骤判断是否重复先计算该对象的hashCode直接定位到它应该存放的位置。如果该位置没有其他对象说明不重复直接存入。如果该位置已经有其他对象发生哈希冲突此时集合会调用equals()方法拿新对象和该位置上的老对象挨个进行比较。如果equals()返回true说明内容重复了拒绝存入。如果equals()返回false说明只是恰好哈希码一样内容不同则通过链表或红黑树将新对象连在后面。3. 如果不遵守契约会发生什么假设你自定义了一个User类重写了equals只要name相同就相等但没有重写 hashCode。User u1 new User(张三); User u2 new User(张三); // 此时 u1.equals(u2) 为 true如果把它们放进HashSetu1和u2的内存地址不同因为没重写hashCode()它们算出来的哈希码大概率不相同。HashSet会把它们分到不同的桶里。结果本该去重的HashSet同时存入了u1和u2。集合的去重机制彻底失效4. 面试总结如果两个对象相等equals()返回true它们的hashCode一定相同。如果两个对象的hashCode相同它们并不一定相等可能发生了哈希冲突。因此equals()方法被重写过hashCode()方法也必须被重写。先比hashCode效率高再比equals准确度高这是兼顾性能与准确性的完美方案。在 Java 中String类字符串是开发中最常用的引用数据类型。为了方便日常开发和应对面试八股文我们可以将String的常用方法按功能进行分类归纳。四。 字符串方法字符串获取这类方法用于获取字符串的长度、特定位置的字符或者与其他数据类型进行转换。length()返回字符串的长度字符个数。String str Hello; int len str.length(); // 5charAt(int index)返回指定索引处的字符索引从 0 开始。char ch Java.charAt(2); // vtoCharArray()将字符串转换成一个全新的字符数组char[]。valueOf(...)这是一个静态方法可以将基本数据类型如int,double,boolean或对象转换为字符串。String s String.valueOf(123); // 123字符串判断与比较用于检查字符串的内容、状态或前后缀。equals(Object anObject)比较两个字符串的内容是否相等区分大小写。equalsIgnoreCase(String anotherString)比较两个字符串的内容是否相等忽略大小写。boolean res abc.equalsIgnoreCase(ABC); // trueisEmpty()判断字符串长度是否为 0即。isBlank()(Java 11 新增)判断字符串是否为空或者只包含空白字符如空格、制表符\t。 .isEmpty(); // false .isBlank(); // truestartsWith(String prefix)/endsWith(String suffix)判断字符串是否以指定的子串开头或结尾。Main.java.endsWith(.java); // true字符串检索与查找用于查找子字符串在原字符串中的位置。contains(CharSequence s)判断字符串中是否包含指定的子文本返回boolean。Hello World.contains(or); // trueindexOf(String str)返回子字符串第一次出现的索引如果未找到则返回-1。lastIndexOf(String str)返回子字符串最后一次出现的索引。字符串截取注意String对象是不可变的immutable。以下方法看似“修改”了字符串实际上都是返回了一个全新的 String 对象原字符串并未改变。substring(int beginIndex)从指定索引截取到字符串末尾。substring(int beginIndex, int endIndex)截取[beginIndex, endIndex)范围内的字符串左闭右开包含开头不包含结尾。String sub Beautiful.substring(0, 4); // Beaureplace(CharSequence target, CharSequence replacement)替换字符串中所有的目标子串。String newStr aaa.replace(a, b); // bbbreplaceAll(String regex, String replacement)基于正则表达式替换所有匹配的内容。split(String regex)根据匹配的正则表达式将字符串分割成一个字符串数组String[]。String[] parts A,B,C.split(,); // [A, B, C]toLowerCase()/toUpperCase()将字符串转换为全小写或全大写。trim()去除字符串首尾的普通空格ASCII码小于等于32的字符。strip()(Java 11 新增)去除首尾的所有空白字符支持 Unicode 里的各种空格比trim()更强大。字符串拼接与重复join(CharSequence delimiter, CharSequence... elements)(Java 8 新增)用指定的连接符把多个字符串或集合拼接起来。String joined String.join(-, 2026, 07, 08); // 2026-07-08repeat(int count)(Java 11 新增)将字符串重复指定次数String multi A.repeat(3); // AAA面试加分项length()是方法不是属性常考选择题。数组获取长度用的是.length属性而String和集合如List获取长度/大小用的是.length()和.size()方法。String的不可变性在调用replace、substring、toLowerCase等方法时一定要记得用新变量接收返回值因为原字符串本身绝不会改变。split()的坑如果要用特殊符号如.,|,*作为分隔符因为它们在正则表达式中有特殊含义必须进行转义。例如用点分割str.split(\\.)。五。String、StringBuffer、StringBuilderString不可变字符串每次操作都会生成新对象效率低线程安全。StringBuffer可变字符串方法加了同步锁synchronized效率较低线程安全。StringBuilder可变字符串方法没有加锁效率最高线程不安全。核心区别详解1. 可变性ImmutabilityString内部是用char[]数组Java 9 之后改为byte[]数组来保存字符的。关键在于这个数组被private final修饰且String类没有暴露任何修改这个数组的方法。所以String对象一旦创建其内容就绝对不可变。StringBuffer与StringBuilder都继承自AbstractStringBuilder类它们内部的字符数组没有final修饰。它们提供了append()、insert()、delete()等方法可以直接在原有内存地址上修改字符串内容。2. 线程安全性Thread SafetyString因为不可变天生就是线程安全的多个线程同时读它不会产生任何问题。StringBuffer绝大多数公开方法都加上了synchronized关键字同步锁。这意味着在多线程环境下多个线程同时操作同一个StringBuffer会排队虽然牺牲了性能但保证了线程安全。StringBuilder去掉了synchronized锁。多线程同时操作同一个StringBuilder可能会发生数据错乱它是线程不安全的。3. 性能PerformanceStringBuilderStringBufferStringStringBuilder因为没有锁的开销速度最快。StringBuffer其次。String的速度最慢因为任何修改、拼接操作如str abc在底层通常都需要创建新的对象并涉及垃圾回收GC的开销。内在联系共同的父接口三者都实现了CharSequence接口这意味着它们在很多方法如length()、charAt()、subSequence()的设计上具有一致性。底层一脉相承StringBuffer和StringBuilder的绝大部分底层源代码和扩容机制是完全一样的都依赖于AbstractStringBuilder。StringBuffer仅仅是在调用这些底层方法时套上了一层synchronized锁。可以相互转换String转后两者通过构造函数new StringBuilder(str)。后两者转String调用普通的.toString()方法。特性StringStringBufferStringBuilder可变性不可变可变可变线程安全性安全天生安全安全方法加同步锁不安全无锁性能/速度慢频繁产生新对象中等受锁影响快无锁直接修改引入版本JDK 1.0JDK 1.0JDK 1.5适用场景少量字符串操作、常量定义多线程环境下的字符串大量拼接单线程环境下的字符串大量拼接避坑与面试加分点Q。既然 String 这么慢为什么str1 str2没觉得卡顿A。在现代 Java 中编译器对String的拼接做了优化如果是常量拼接如String s a b c;编译器在编译期就会将其直接优化为String s abc;这叫常量折叠。如果是变量拼接如String s str1 str2;在 JDK 8 中编译器在底层会自动将其转化为StringBuilder.append()的方式来处理。但是如果拼接发生在for循环里面编译器每次循环都会重新new一个StringBuilder对象。所以在循环体内进行字符串拼接必须手动在循环外定义StringBuilder2. 总结字符串改动少优先用String。单线程下大量拼接优先用StringBuilder90% 的业务场景选它。多线程如多个线程共享同一个拼接缓冲器下大量拼接才考虑用StringBuffer。