Java中String类型详解(2026)

一、String 类型的核心本质

String 是 Java 中最常用的引用数据类型,用于表示字符串常量,其核心特性需重点掌握:

  1. 不可变性(Immutable):String 对象一旦创建,其底层字符数组(char [],JDK9 后改为 byte [])的内容无法修改。所有看似修改的操作(如substring、replace)都会创建新的 String 对象,原对象保持不变。
String str = "abc";

str = str + "d"; // 原"abc"对象未改变,创建新对象"abcd"并让str指向它
  1. 常量池优化:Java 为 String 提供字符串常量池(String Pool),位于方法区(JDK8 后是元空间)。当使用字面量创建 String(如String s = "abc")时,JVM 会先检查常量池是否存在该字符串,若存在则直接引用,否则创建新对象存入常量池,避免重复创建相同字符串,节省内存。
  1. 实现接口:String 实现了Serializable(支持序列化)、Comparable)、CharSequence(字符序列接口,提供charAt、length` 等方法)三个核心接口,这也是其具备诸多实用功能的基础。

二、String 的创建方式与内存差异

String 有两种常见创建方式,其内存分配和引用逻辑截然不同,是面试高频考点:

1. 字面量创建(String s = "abc")
  • 执行流程:JVM 先查询字符串常量池,若存在 "abc" 则直接将引用赋值给 s;若不存在,在常量池创建 "abc" 对象,再将引用赋值给 s。
  • 内存位置:对象存储在常量池,s 指向常量池中的对象。
2. 构造方法创建(String s = new String("abc"))
  • 执行流程:无论常量池是否存在 "abc",都会先在堆内存中创建一个 String 对象,再检查常量池:若常量池无 "abc",则在常量池也创建一个 "abc" 对象;最终 s 指向堆内存中的对象。
  • 代码验证:
String s1 = "abc";

String s2 = new String("abc");

System.out.println(s1 == s2); // false(==比较引用地址,s1指向常量池,s2指向堆)

System.out.println(s1.equals(s2)); // true(equals比较字符串内容)
3. intern () 方法的作用

intern()方法用于手动将 String 对象存入常量池(JDK7 后优化,常量池移至堆中):

  • 若常量池已存在该字符串,返回常量池中的引用;
  • 若不存在,将当前对象的引用存入常量池(而非创建新对象),返回该引用。
  • 代码示例:
String s3 = new String("xyz").intern();

String s4 = "xyz";

System.out.println(s3 == s4); // true(s3通过intern()指向常量池,s4直接指向常量池)

三、String 的常用方法(实战必备)

String 类提供了大量实用方法,以下是开发中高频使用的核心方法,结合场景说明:

方法签名

功能描述

示例

int length()

获取字符串长度

"abc".length() → 3

char charAt(int index)

获取指定索引的字符(索引从 0 开始)

"abc".charAt(1) → 'b'

boolean equals(Object obj)

比较字符串内容(区分大小写)

"Abc".equals("abc") → false

boolean equalsIgnoreCase(String str)

忽略大小写比较内容

"Abc".equalsIgnoreCase("abc") → true

String concat(String str)

拼接字符串(等价于+,但效率更高)

"a".concat("b").concat("c") → "abc"

String substring(int beginIndex)

从指定索引截取子串(含 beginIndex)

"abcd".substring(1) → "bcd"

String substring(int beginIndex, int endIndex)

截取 [beginIndex, endIndex) 区间的子串

"abcd".substring(1,3) → "bc"

boolean contains(CharSequence s)

判断是否包含指定字符序列

"abcde".contains("bc") → true

boolean startsWith(String prefix)

判断是否以指定前缀开头

"http://www".startsWith("http") → true

boolean endsWith(String suffix)

判断是否以指定后缀结尾

"test.txt".endsWith(".txt") → true

String replace(char oldChar, char newChar)

替换所有指定字符

"abac".replace('a','x') → "xbxc"

String replace(CharSequence target, CharSequence replacement)

替换指定字符序列

"a-b-c".replace("-","_") → "a_b_c"

String trim()

去除首尾空白字符(空格、制表符等)

" abc ".trim() → "abc"(JDK11 后可用strip(),支持更多空白字符)

String[] split(String regex)

按指定正则表达式分割字符串

"a,b,c".split(",") → ["a","b","c"]

static String valueOf(Object obj)

将任意类型转为字符串(静态方法)

String.valueOf(123) → "123"

int compareTo(String str)

按字典序比较(返回差值,0 表示相等)

"a".compareTo("b") → -1,"c".compareTo("a") → 2

关键注意点:
  • split(regex)中若分割符是正则特殊字符(如.、|),需转义(如split("\\."));
  • substring在 JDK6 中会创建新字符数组,JDK7 后直接引用原数组的指定区间,避免内存浪费;
  • 字符串拼接优先使用StringBuilder/StringBuffer(尤其是循环拼接),效率远高于+和concat(+会频繁创建新 String 对象)。

四、String、StringBuilder、StringBuffer 的区别(面试必问)

开发中字符串拼接场景频繁,三者的选择直接影响性能,核心区别如下:

特性

String

StringBuilder

StringBuffer

可变性

不可变(修改创建新对象)

可变(直接操作底层数组)

可变(直接操作底层数组)

线程安全

线程安全(无修改操作)

线程不安全(效率高)

线程安全(方法加synchronized,效率低)

性能

最低(频繁修改时创建大量对象)

最高(单线程场景首选)

中等(多线程场景使用)

适用场景

字符串不频繁修改(如常量定义)

单线程下字符串频繁拼接、修改

多线程下字符串频繁拼接、修改

性能测试示例(循环拼接 10000 次):
long start = System.currentTimeMillis();

// String拼接(最慢)

String s = "";

for (int i = 0; i 10000; i++) {

s += i;

}

System.out.println("String耗时:" + (System.currentTimeMillis() - start) + "ms");

// StringBuilder拼接(最快)

start = System.currentTimeMillis();

StringBuilder sb = new StringBuilder();

for (int i = 0; i 0000; i++) {

sb.append(i);

}

System.out.println("StringBuilder耗时:" + (System.currentTimeMillis() - start) + "ms");

// StringBuffer拼接(中等)

start = System.currentTimeMillis();

StringBuffer sbf = new StringBuffer();

for (int i = 0; i 0; i++) {

sbf.append(i);

}

System.out.println("StringBuffer耗时:" + (System.currentTimeMillis() - start) + "ms");

输出结果(参考):String 耗时约 500ms,StringBuilder 耗时约 1ms,StringBuffer 耗时约 3ms,差异显著。

五、String 的常见误区与避坑指南

  1. 误区 1:认为String s = nullString s = ""等价
    • 区别:s = null表示 s 未指向任何对象(引用为空);s = ""表示 s 指向一个长度为 0 的字符串对象(引用非空)。
    • 避坑:调用null的 String 方法会抛出NullPointerException,需先判空:
// 错误写法(若s为null会报错)

if (s.equals("abc")) { ... }

// 正确写法(先判非空,或用常量在前)

if ("abc".equals(s)) { ... } // 即使s为null,也不会报错(返回false)
  1. 误区 2:循环中使用+拼接字符串
    • 问题:每次+都会创建新的 String 对象,循环 10000 次会创建大量对象,导致内存浪费和性能下降。
    • 解决:使用StringBuilder(单线程)或StringBuffer(多线程):

  1. 误区 3:String.intern()一定能节省内存
    • 注意:JDK7 后intern()会将对象引用存入常量池,但若字符串是动态生成的(如用户输入),且重复率低,使用intern()反而会占用常量池内存,适得其反。
    • 适用场景:字符串重复率高(如配置项、固定枚举值)时,使用intern()可复用对象,节省内存。
  1. 误区 4:忽略 String 的空字符串判断
    • 判空推荐写法(兼顾null和空字符串):
// 方法1:使用StringUtils(Apache Commons Lang工具类,推荐)

if (StringUtils.isNotBlank(s)) { ... } // 排除null、""、" "等情况

// 方法2:原生写法

if (s != null && !s.trim().isEmpty()) { ... }

六、总结

String 作为 Java 中最核心的类型之一,其不可变性常量池优化是理解的基础,而创建方式、内存分配、常用方法及与 StringBuilder/StringBuffer 的区别,是开发和面试中的重点。掌握以下核心要点,即可应对大部分场景:

  1. 字面量创建走常量池,new创建走堆,intern()可手动入池;
  1. 不可变性导致修改操作创建新对象,频繁修改优先用StringBuilder;
  1. 线程安全用StringBuffer,单线程用StringBuilder,字符串常量用String;
  1. 判空、拼接、截取时注意避坑,优先使用工具类和高效方法。

如果需要进一步深入 String 的底层源码(如hashCode实现、replace方法原理)或更多实战场景案例,可以留言补充说明!

概要:   DevCon 实用工具是一种命令行实用工具,可以替代设备管理器。使用 DevCon,您可以启用禁用、重新启动、更新、删除查询单个设备或一组设备。DevCon 提供与开发人员有关但无法在设备管理器中看到的信息。   您可以将 DevCon 用于 Windows 2000 、Windows XPWindows vista。不能将 Devcon 用于 Microsoft Windows 95、Windows 98、或 Windows Millennium Edition。   下载:http://download.microsoft.com/download/1/1/f/11f7dd10-272d-4cd2-896f-9ce67f3e0240/devcon.exe 用法及参数说明:   devcon.exe [-r] [-m:\\] [...]   -r 如果指定它,在命令完成后若需要则重新启动计算机。    是目标计算机的名称。    是将要执行的命令(如下所示)。   ... 是命令需要的一个或多个参数。   要获取关于某一特定命令的帮助,请键入:devcon.exe help   classfilter 允许修改类别筛选程序。   classes 列出所有设备安装类别。   disable 禁用与指定的硬件或实例 ID 匹配的设备。   driverfiles 列出针对设备安装的驱动程序文件。   drivernodes 列出设备的所有驱动程序节点。   enable 启用与指定的硬件或 实例 ID 匹配的设备。   find 查找与指定的硬件或 实例 ID 匹配的设备。   findall 查找设备,包括那些未显示的设备。   help 显示此信息。   hwids 列出设备的硬件 ID。   install 手动安装设备。   listclass 列出某一安装类别的所有设备。   reboot 重新启动本地计算机。   remove 删除与特定的硬件或 实例 ID 匹配的设备。   rescan 扫描以发现新的硬件。   resources 列出设备的硬件资源。   restart 重新启动与特定的硬件或 实例 ID 匹配的设备。   stack 列出预期的设备驱动程序堆栈。   status 列出设备的运行状态。   update 手动更新设备。   UpdateNI 手动更新设备,无用户提示   SetHwID 添加、删除更改根枚举设备的硬件 ID 的顺序。 示例:   devcon -m:\\test find pci\* 列出计算机 test 上的所有已知 PCI 设备。(通过使用 -m,您可以指定一个目标计算机。您必须使用“进程间通信”(IPC) 访问此计算机。)   devcon -r install Windows directory\Inf\Netloop.inf *MSLOOP 安装一个新的 Microsoft 环回适配器实例。这将创建一个新的根枚举设备节点,使用此节点您可以安装“虚拟设备”,如环回适配器。如果需要重新启动计算机,此命令还将以安静模式重启计算机。   devcon classes 列出所有已知的安装类别。输出结果包含短的未本地化的名称(例如,“USB”)描述性名称(例如,“通用串行总线控制器”)。 禁用启用网卡的步骤:   1.用devcon hwids PCI*命令得到所有以PCI开头的设备。这时会列出很多设备,那么哪个才是网卡对应的呢?   2.打开设备管理器,展开网络适配器,找到网卡的名称,然后记住到刚才得到的列表中找对应的Name,然后你会在下面看到好几个ID,随便挑一个就行   3.用devcon disable "PCI\VEN_11AB&DEV_4380&SUBSYS_301B17AA&REV_10"禁用网卡(启用的话讲disable换成enable就行了)   4.其实用PCI开头得到的几组设备中一般第一个就是网卡设备 sysdzw 16:01 2010-11-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值