当前位置: 首页 > news >正文

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

🔥个人主页:代码不加冰(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:LeetCode刷题日记 , 苍穹外卖日记,SSM框架深入,JavaWeb,
命运的结局尽可永在,不屈的挑战却不可须臾或缺!


前言:

大家好,我是代码不加冰,今天上午用gpt5.5扫了好几个项目,然后写了个skill,用着挺爽的,稍微有点上瘾了(根本用不起),这还是群友分享的,在此特别鸣谢。自我感觉让AI写项目比自己写项目还累,于是休息一会写篇文章,至于写什么呢,那就分享一下前几天学的八股吧。


摘要:

本文面向 Java 初学者与面试备战者,硬核盘点Object类与String类的核心八股文考点。前半部分深入剖析Object核心方法,用严谨的逻辑与大厂视角详解==equals()的本质区别,并揭秘重写equals()必须重写hashCode()的底层契约及哈希冲突机制。后半部分对高频StringAPI 进行分类归纳,多维度横向对比StringStringBufferStringBuilder的优缺点,并直击循环体中使用+拼接字符串的编译底层大坑。全文拒绝死记硬背,助你轻松建立扎实的 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); }
  • 重写之后StringIntegerDate等内置类都重写了equals()方法,将其改为比较对象的内容(属性值)是否相等,而不是比较内存地址

3. 核心区别对比表

特征== 运算符equals() 方法
性质运算符(Keyword)类的方法(Method)
基本类型比较比较值是否相等无法用于基本类型(编译报错)
引用类型比较比较内存地址(是否是同一个对象)默认比较地址;重写后比较内容是否相等
可定制性无法重写,逻辑固定可以由开发者任意重写定制比较逻辑

三。hashCode() 与 equals()

这是八股文中的进阶题,面试官非常看重是否理解哈希表(如 HashMap、HashSet)的底层原理

1. 什么是 hashCode()

hashCode()方法的作用是获取一个整型的哈希码(散列码)。这个哈希码的作用是确定该对象在哈希表中的索引位置。Java 中的哈希表利用它来大幅提高查询和插入的效率。

2. 为什么重写 equals 必须要重写 hashCode

这是 Java 的官方规定。简单一句话总结:如果两个对象通过equals()判定相等,那么它们的hashCode()必须相同。

当你想往HashSet里放一个新对象时,集合会按以下步骤判断是否重复:

  1. 先计算该对象的hashCode,直接定位到它应该存放的位置。

  2. 如果该位置没有其他对象:说明不重复,直接存入。

  3. 如果该位置已经有其他对象(发生哈希冲突)

    • 此时集合会调用equals()方法,拿新对象和该位置上的老对象挨个进行比较。

    • 如果equals()返回true,说明内容重复了,拒绝存入。

    • 如果equals()返回false,说明只是恰好哈希码一样,内容不同,则通过链表或红黑树将新对象连在后面。

3. 如果不遵守契约会发生什么

假设你自定义了一个User类,重写了equals(只要name相同就相等),但没有重写 hashCode

User u1 = new User("张三"); User u2 = new User("张三"); // 此时 u1.equals(u2) 为 true

如果把它们放进HashSet

  • u1u2的内存地址不同,因为没重写hashCode(),它们算出来的哈希码大概率不相同

  • HashSet会把它们分到不同的桶里。

  • 结果:本该去重的HashSet同时存入了u1u2集合的去重机制彻底失效

4. 面试总结

  1. 如果两个对象相等(equals()返回true),它们的hashCode一定相同。

  2. 如果两个对象的hashCode相同,它们并不一定相等(可能发生了哈希冲突)。

  3. 因此,equals()方法被重写过,hashCode()方法也必须被重写。

  4. 先比hashCode(效率高),再比equals(准确度高),这是兼顾性能与准确性的完美方案。


在 Java 中,String类(字符串)是开发中最常用的引用数据类型。为了方便日常开发和应对面试八股文,我们可以将String的常用方法按功能进行分类归纳。

四。 字符串方法

字符串获取

这类方法用于获取字符串的长度、特定位置的字符,或者与其他数据类型进行转换。

  • length():返回字符串的长度(字符个数)。

    String str = "Hello"; int len = str.length(); // 5
  • charAt(int index):返回指定索引处的字符(索引从 0 开始)。

    char ch = "Java".charAt(2); // 'v'
  • toCharArray():将字符串转换成一个全新的字符数组(char[])。

  • valueOf(...):这是一个静态方法,可以将基本数据类型(如int,double,boolean)或对象转换为字符串。

    String s = String.valueOf(123); // "123"

字符串判断与比较

用于检查字符串的内容、状态或前后缀。

  • equals(Object anObject):比较两个字符串的内容是否相等(区分大小写)。

  • equalsIgnoreCase(String anotherString):比较两个字符串的内容是否相等,忽略大小写

    boolean res = "abc".equalsIgnoreCase("ABC"); // true
  • isEmpty():判断字符串长度是否为 0(即"")。

  • isBlank()(Java 11+ 新增):判断字符串是否为空或者只包含空白字符(如空格、制表符\t)。

    " ".isEmpty(); // false " ".isBlank(); // true
  • startsWith(String prefix)/endsWith(String suffix):判断字符串是否以指定的子串开头或结尾。

    "Main.java".endsWith(".java"); // true

字符串检索与查找

用于查找子字符串在原字符串中的位置。

  • contains(CharSequence s):判断字符串中是否包含指定的子文本,返回boolean

    "Hello World".contains("or"); // true
  • indexOf(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); // "Beau"
  • replace(CharSequence target, CharSequence replacement):替换字符串中所有的目标子串。

    String newStr = "aaa".replace("a", "b"); // "bbb"
  • replaceAll(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-08"
  • repeat(int count)(Java 11+ 新增):将字符串重复指定次数

    String multi = "A".repeat(3); // "AAA"

面试加分项

  1. length()是方法,不是属性:常考选择题。数组获取长度用的是.length属性,而String和集合(如List)获取长度/大小用的是.length().size()方法。

  2. String的不可变性:在调用replacesubstringtoLowerCase等方法时,一定要记得用新变量接收返回值,因为原字符串本身绝不会改变。

  3. split()的坑:如果要用特殊符号(如.,|,*)作为分隔符,因为它们在正则表达式中有特殊含义,必须进行转义。例如用点分割:str.split("\\.")


五。String、StringBuffer、StringBuilder

  • String不可变字符串,每次操作都会生成新对象,效率低,线程安全。

  • StringBuffer可变字符串,方法加了同步锁(synchronized),效率较低,线程安全

  • StringBuilder可变字符串,方法没有加锁,效率最高,线程不安全

核心区别详解

1. 可变性(Immutability)
  • String内部是用char[]数组(Java 9 之后改为byte[]数组)来保存字符的。关键在于,这个数组被private final修饰,且String类没有暴露任何修改这个数组的方法。所以String对象一旦创建,其内容就绝对不可变。

  • StringBufferStringBuilder都继承自AbstractStringBuilder类,它们内部的字符数组没有final修饰。它们提供了append()insert()delete()等方法,可以直接在原有内存地址上修改字符串内容。

2. 线程安全性(Thread Safety)
  • String:因为不可变,天生就是线程安全的,多个线程同时读它不会产生任何问题。

  • StringBuffer:绝大多数公开方法都加上了synchronized关键字(同步锁)。这意味着在多线程环境下,多个线程同时操作同一个StringBuffer会排队,虽然牺牲了性能,但保证了线程安全

  • StringBuilder:去掉了synchronized锁。多线程同时操作同一个StringBuilder可能会发生数据错乱,它是线程不安全的。

3. 性能(Performance)
  • StringBuilder>StringBuffer>String

  • StringBuilder因为没有锁的开销,速度最快。

  • StringBuffer其次。

  • String的速度最慢,因为任何修改、拼接操作(如str + "abc")在底层通常都需要创建新的对象,并涉及垃圾回收(GC)的开销。

内在联系

  1. 共同的父接口:三者都实现了CharSequence接口,这意味着它们在很多方法(如length()charAt()subSequence())的设计上具有一致性。

  2. 底层一脉相承StringBufferStringBuilder的绝大部分底层源代码和扩容机制是完全一样的,都依赖于AbstractStringBuilderStringBuffer仅仅是在调用这些底层方法时套上了一层synchronized锁。

  3. 可以相互转换

    • 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对象。所以,在循环体内进行字符串拼接,必须手动在循环外定义StringBuilder

2. 总结

  • 字符串改动少,优先用String

  • 单线程下大量拼接,优先用StringBuilder(90% 的业务场景选它)。

  • 多线程(如多个线程共享同一个拼接缓冲器)下大量拼接,才考虑用StringBuffer

http://www.jsqmd.com/news/1152232/

相关文章:

  • AMD EPYC Naples vs Rome vs Milan:3代Chiplet架构成本与性能演进分析
  • API 中转站怎么选?从稳定接入到多模型管理的完整指南
  • 【大数据课程设计/毕业设计】基于 Spark 数据挖掘的新能源汽车竞品分析系统的设计与实现 基于 Spark 的星云新能源汽车大数据智能分析系统【附源码、数据库、万字文档】
  • ios 页面生命周期 ViewController 与安卓生命周期对比
  • 企业级实时图表编辑平台架构解析与性能优化最佳实践
  • 便携式与无人值守光伏清洁机器人技术路线对比及选型决策框架
  • 为什么顶尖AI原生团队已弃用Cursor转向Windsurf?——基于37个真实工程项目的IDE迁移成本与ROI分析(附可复用评估清单)
  • 2026企业400电话服务商深度测评|运营商直营、低价代理、一级授权服务商全方位对比
  • 性价比高的GEO优化解决措施服务商企业
  • 显存不足?出图模糊?崩溃频发?Stable Diffusion十大高频故障,99%用户都在踩的坑,现在立刻修复
  • DALL-E 3 vs Midjourney——2024下半年唯一值得重押的AI图像引擎:基于模型架构、训练数据源与推理成本的三维决策矩阵
  • SCADA系统选型五大要点,机加工企业别再花冤枉钱了
  • 从卡顿到丝滑:用.cursorrules重写AI上下文感知逻辑,3步完成响应延迟从2.4s→180ms的权威调优
  • AI搜索优化公司排名:年度服务商综合评测
  • 如今投资网站域名还是一门好生意吗?AI 时代下的域名价值正在变化
  • 解锁 Linux 自动化大门:零基础轻松入门 Shell 编程
  • 扩展 Lucas 定理(exLucas)学习笔记 详解,一文带你彻底看懂扩展 Lucas 定理。---------------------著作权归作者所有。商业转载请联系作者获得授权,非商业
  • Orca论文:从Token预测到状态预测的AI范式革命
  • 工程师正在悄悄切换的真相(Windsurf用户流失率骤升23%?Cursor新架构如何重构AI IDE信任边界)
  • 大数据计算机毕设之基于大数据处理的养老院运营趋势分析系统的设计与实现 基于 Hadoop 分布式架构的养老院服务数据分析系统(完整前后端代码+说明文档+LW,调试定制等)
  • 一文讲清 Docker 和容器:它们是什么,又有什么关系?
  • 从0到1搭一个云原生大模型推理平台:FastAPI + vLLM + Kubernetes + Prometheus
  • RPA 浏览器自动化:多账号防检测、网页批量数据采集、风控应对
  • 写了5年Python才发现,90%的人都用错了lambda表达式,难怪代码可读性差到被吐槽
  • 开发者必读:Claude Code和Cursor到底该用哪个?3类典型工作流(全栈/前端/数据工程)适配性分析,错过=多花47%调试时间
  • 174.1.智能消防喷淋监测-WiFi-基于STM32单片机物联网设计【硬件+APP+云平台】
  • AI文档时代已来,你还在手动改稿?Gemini for Docs高效工作流全解析,
  • CentOS7.5 编译 Python3.6.8+OpenStack 私有云 API 运维
  • Ubuntu 22.04下编译安装wave2Foam与OpenFOAM v2206完整指南
  • 阿里千问智能体下线:数据备份与迁移技术指南