Java字符串与编码

String类的特性

字符串的不可变性

String类最显著的特性是不可变性——一旦创建,字符串的内容就无法修改。这种设计源于String内部使用private final char[](或较新JDK中的byte[])存储字符,且没有提供修改这些数组的方法。

public class StringImmutability {
public static void main(String[] args) {
String s = "hello";
String t = s.toUpperCase(); // toUpperCase()返回新字符串
System.out.println(s); // 输出"hello"(原字符串未变)
System.out.println(t); // 输出"HELLO"(新字符串)
}
}

不可变性带来的好处:

  • 线程安全:多个线程可以安全地共享字符串对象
  • 可以缓存哈希值:StringhashCode()结果会被缓存,提高哈希表性能
  • 字符串常量池优化:相同内容的字符串可以共享内存

字符串常量池

Java会维护一个字符串常量池(String Constant Pool),用于存储编译期确定的字符串字面量:

  1. 当 JVM 遇到 "hello" 字面量时,会检查字符串常量池
  2. 如果不存在相同内容的字符串,则在常量池中创建;如果存在则直接使用

所以 s1 和 s2 都指向常量池中的同一个对象:

public class StringPool {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello"); System.out.println(s1 == s2); // true(指向常量池同一对象)
System.out.println(s1 == s3); // false(s3是新创建的对象)
System.out.println(s1.equals(s3)); // true(内容相同)
}
}

使用 new 关键字会强制创建一个新的 String 对象,而不是从字符串常量池中获取引用。new String("hello") 会:

  1. 首先在堆内存中创建一个新的 String 对象
  2. 这个新对象的内容指向常量池中的 "hello"
  3. 但 s3 变量存储的是这个新创建对象的引用(指向堆中新创建的对象)

字符串常量池:

┌─────────────┐

│ "hello" │ ← s1 和 s2 指向这里

└─────────────┘

堆内存:

┌─────────────┐

│ new String │ ← s3 指向这里

│ (内容指向 │

│ 常量池hello)│

└─────────────┘

字符串常用操作

方法 说明 示例
+ 运算符 字符串拼接 "Hello" + " World"
concat(String str) 拼接指定字符串 "Hello".concat(" Java")
join(CharSequence delimiter, CharSequence... elements) 以分隔符拼接多个字符串 String.join("-", "a", "b", "c")
substring(int beginIndex) 从指定索引截取到末尾 "abcdef".substring(2)
substring(int beginIndex, int endIndex) 截取指定范围(左闭右开) "abcdef".substring(1,4)
trim() 去除首尾空白字符 " test ".trim()
strip()(Java 11+) 去除首尾空白(支持Unicode) " a b ".strip()
replace(char oldChar, char newChar) 替换所有指定字符 "hello".replace('l', 'x')
replace(CharSequence target, CharSequence replacement) 替换所有指定子串 "hello world".replace("world", "java")
replaceAll(String regex, String replacement) 正则替换 "a1b2c3".replaceAll("\\d", "")
toUpperCase() 转换为大写 "hello".toUpperCase()
toLowerCase() 转换为小写 "HELLO".toLowerCase()
length() 获取字符串长度 "hello".length()
contains(CharSequence s) 判断是否包含指定子串 "hello".contains("ell")
charAt(int index) 获取指定索引的字符 "hello".charAt(1)
indexOf(String str) 查找子串首次出现位置 "hello".indexOf("l")
lastIndexOf(String str) 查找子串最后出现位置 "hello".lastIndexOf("l")
startsWith(String prefix) 判断是否以指定前缀开头 "hello".startsWith("he")
endsWith(String suffix) 判断是否以指定后缀结尾 "hello".endsWith("lo")
equals(Object anObject) 比较内容(区分大小写) "Hello".equals("hello")
equalsIgnoreCase(String anotherString) 比较内容(忽略大小写) "Hello".equalsIgnoreCase("hello")
split(String regex) 按正则分割为字符串数组 "a,b,c".split(",")
isEmpty() 判断是否为空字符串 ""
isBlank()(Java 11+) 判断是否为空或仅含空白 " ".isBlank()

格式化字符串

String.format()方法用于创建格式化字符串,支持多种占位符:

public class StringFormat {
public static void main(String[] args) {
String name = "Alice";
int age = 30;
double salary = 50000.5; // 格式化字符串
String formatted = String.format(
"Name: %s, Age: %d, Salary: %.2f",
name, age, salary
); System.out.println(formatted);
// 输出:Name: Alice, Age: 30, Salary: 50000.50
}
}

常用占位符:

  • %s:字符串
  • %d:整数
  • %f:浮点数
  • %x:十六进制整数
  • %t:日期时间

字符串与其他类型的转换

基本类型与字符串互转

public class StringConversion {
public static void main(String[] args) {
// 基本类型转字符串
String s1 = String.valueOf(123); // "123"
String s2 = String.valueOf(3.14); // "3.14"
String s3 = String.valueOf(true); // "true" // 字符串转基本类型
int n = Integer.parseInt("123"); // 123
double d = Double.parseDouble("3.14"); // 3.14
boolean b = Boolean.parseBoolean("true"); // true // 字符串转字符数组
char[] chars = "hello".toCharArray(); // ['h','e','l','l','o'] // 字符数组转字符串
String s4 = new String(chars); // "hello"
}
}

注意事项:

  • 字符串转基本类型时,若格式不匹配会抛出NumberFormatException
  • 使用new String(char[])时,会复制字符数组,后续修改原数组不影响字符串

字符编码详解

常见编码方式

  1. ASCII:单字节编码,仅包含英文字母、数字和常用符号(0-127)
  2. GB2312/GBK:中文编码标准,GB2312包含6763个汉字,GBK扩展了更多字符
  3. Unicode:全球统一编码标准,包含所有语言字符,每个字符占2-4字节
  4. UTF-8:Unicode的变长编码,英文字符占1字节,中文通常占3字节

Java中的编码处理

Java的Stringchar在内存中始终使用Unicode编码,当需要IO操作或网络传输时,需转换为指定编码的字节数组:

import java.nio.charset.StandardCharsets;

public class StringEncoding {
public static void main(String[] args) throws Exception {
String s = "中文字符"; // 字符串转字节数组(指定编码)
byte[] utf8Bytes = s.getBytes(StandardCharsets.UTF_8);
byte[] gbkBytes = s.getBytes("GBK"); // 需要处理异常 // 字节数组转字符串(指定编码)
String utf8Str = new String(utf8Bytes, StandardCharsets.UTF_8);
String gbkStr = new String(gbkBytes, "GBK");
}
}

String的内存优化

较新的JDK版本(如JDK 9+)对String的内部实现进行了优化:

  • 对于仅包含ASCII字符的字符串,使用byte[]存储(每个字符1字节)
  • 对于包含非ASCII字符的字符串,使用byte[]存储UTF-16编码(每个字符2字节)

这种优化减少了纯ASCII字符串的内存占用,提高了系统性能,但对开发者透明,不影响String的使用方式。

总结

  • 理解字符串的特性和字符串常量池的规则
  • 掌握字符串的常用操作
  • Java使用Unicode编码表示String和char
  • 转换编码就是将String和byte[]转换,需要指定编码。转换为byte[]时,始终优先考虑UTF-8编码

Java核心类——1.字符串和编码的更多相关文章

  1. 廖雪峰Java2面向对象编程-6Java核心类-1字符串和编码

    Java的字符串用String表示 1.String特点: 可以直接使用"..."表示一个字符串,不强制使用new String 内容不可变 String s1 = "h ...

  2. Day1 面向对象编程与Java核心类

    this变量 在方法内部,可以使用一个隐含的变量this,它始终指向当前实例.如果没有命名冲突,可以省略this. 但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this. 构造方 ...

  3. 从字节码和JVM的角度解析Java核心类String的不可变特性

    1. 前言 最近看到几个有趣的关于Java核心类String的问题. String类是如何实现其不可变的特性的,设计成不可变的好处在哪里. 为什么不推荐使用+号的方式去形成新的字符串,推荐使用Stri ...

  4. Java核心类

    Java核心类的学习: 常用类库 io流 集合 多线程 网络编程 调试与优化 XML和JSON 枚举.注解和反射 设计模式

  5. Java常用类之字符串类

    String 的特性 public final class String implements java.io.Serializable, Comparable<String>, Char ...

  6. Java按字节截取字符串(GBK编码、UTF-8编码实现)

    package FileDemo; import java.io.IOException; public class CutStringTest { /** * @param args * @thro ...

  7. java工具类去掉字符串String中的.点。android开发java程序员常用工具类

    下面是工具类详细代码: package com.qq986945193.david; /** * qq986945193 Project * ============================= ...

  8. [ java 工具类] xml字符串解析成Map(DOM解析)

    package com.tencent.jungle.wechat.util; import com.google.inject.Singleton; import org.w3c.dom.Docum ...

  9. java转换字符串的编码(转)

    package com.Alex.base; import java.io.UnsupportedEncodingException; /** * 转换字符串的编码 */ public class C ...

  10. Java基础学习笔记二十三 Java核心语法之反射

    类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...

随机推荐

  1. Mysql 修改、删除字段默认值

    问题描述: 建表的时候,某个表中的字段设置了默认值,后期发生需求变更,不需要提供默认值,或者需要改为其它默认值. 问题解决: alter table 表名 alter column 字段名 drop ...

  2. wso2~部署~v4.2.0-alpha本地构建

    让我先查看一下v4.2.0-alpha分支的构建相关信息: Search files... 让我继续查看构建说明文档: Search files... 让我查看一下项目根目录下的文件: Ran too ...

  3. cmake基础知识

    Cmake Cmake变量 Cmake的基本类型只有两种,分别是字符串和字符串列表,使用set命令来设置变量 set(var abc) # abc是字符串,var是值为"abc"的 ...

  4. C++学习思维导图

    C++思维导图 整个的思维导图大概的架构如下,Xmind.SVG.PDF格式的下载链接都在下面了,如有需要可自取 Xmind文件分享:https://cnblogs-img.oss-cn-hangzh ...

  5. python学习课后练习

    此次爬虫学习的资源是B站所找,具体如下:Python课程天花板,Python入门+Python爬虫+Python数据分析5天项目实操/Python基础,该课程留了课后练习,我把自己的代码和想法单独整成 ...

  6. Springboot笔记<7>过滤器与拦截器

    过滤器 拦截器 过滤器 过滤器拦截的是URL Spring中自定义过滤器(Filter)一般只有一个方法,返回值是void,当请求到达web容器时,会探测当前请求地址是否配置有过滤器,有则调用该过滤器 ...

  7. 你应该懂的AI大模型(六)之 transformers

    一.Transformer与transformers 结论:Transformer是模型架构,transfortmers是库. 问:为什么我们要知道Transformer与transformers呢? ...

  8. kards卡组推荐——苏美隐蔽

    声明:此卡组抗快能力极差,害怕炮兵和小飞机为中后期 隐蔽核心思路: 在第一回合,尽量找杜斯团,找不到如果对方有单位,可以打一个镰刀 第二回合,①有杜斯团:打出隐蔽单位,按隐蔽顺序(附1)出,如果只有1 ...

  9. hot100之二叉树上

    二叉树的中序队列(094) 先看代码 class Solution { public List<Integer> inorderTraversal(TreeNode root) { Lis ...

  10. AI大模型完全本地化部署指南——从零硬件开始

    本文将从基础硬件购置开始讲起,真正意义上从零开始,最终通过Ollama.LangChain.DeepSeek的一系列交互,输出本地大模型的第一声啼鸣,带你走进另一片广阔的世界.update:2025- ...