Java核心类——1.字符串和编码
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"(新字符串)
}
}
不可变性带来的好处:
- 线程安全:多个线程可以安全地共享字符串对象
- 可以缓存哈希值:
String的hashCode()结果会被缓存,提高哈希表性能 - 字符串常量池优化:相同内容的字符串可以共享内存
字符串常量池
Java会维护一个字符串常量池(String Constant Pool),用于存储编译期确定的字符串字面量:
- 当 JVM 遇到 "hello" 字面量时,会检查字符串常量池
- 如果不存在相同内容的字符串,则在常量池中创建;如果存在则直接使用
所以 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") 会:
- 首先在堆内存中创建一个新的 String 对象
- 这个新对象的内容指向常量池中的 "hello"
- 但 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[])时,会复制字符数组,后续修改原数组不影响字符串
字符编码详解
常见编码方式
- ASCII:单字节编码,仅包含英文字母、数字和常用符号(0-127)
- GB2312/GBK:中文编码标准,GB2312包含6763个汉字,GBK扩展了更多字符
- Unicode:全球统一编码标准,包含所有语言字符,每个字符占2-4字节
- UTF-8:Unicode的变长编码,英文字符占1字节,中文通常占3字节
Java中的编码处理
Java的String和char在内存中始终使用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.字符串和编码的更多相关文章
- 廖雪峰Java2面向对象编程-6Java核心类-1字符串和编码
Java的字符串用String表示 1.String特点: 可以直接使用"..."表示一个字符串,不强制使用new String 内容不可变 String s1 = "h ...
- Day1 面向对象编程与Java核心类
this变量 在方法内部,可以使用一个隐含的变量this,它始终指向当前实例.如果没有命名冲突,可以省略this. 但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this. 构造方 ...
- 从字节码和JVM的角度解析Java核心类String的不可变特性
1. 前言 最近看到几个有趣的关于Java核心类String的问题. String类是如何实现其不可变的特性的,设计成不可变的好处在哪里. 为什么不推荐使用+号的方式去形成新的字符串,推荐使用Stri ...
- Java核心类
Java核心类的学习: 常用类库 io流 集合 多线程 网络编程 调试与优化 XML和JSON 枚举.注解和反射 设计模式
- Java常用类之字符串类
String 的特性 public final class String implements java.io.Serializable, Comparable<String>, Char ...
- Java按字节截取字符串(GBK编码、UTF-8编码实现)
package FileDemo; import java.io.IOException; public class CutStringTest { /** * @param args * @thro ...
- java工具类去掉字符串String中的.点。android开发java程序员常用工具类
下面是工具类详细代码: package com.qq986945193.david; /** * qq986945193 Project * ============================= ...
- [ java 工具类] xml字符串解析成Map(DOM解析)
package com.tencent.jungle.wechat.util; import com.google.inject.Singleton; import org.w3c.dom.Docum ...
- java转换字符串的编码(转)
package com.Alex.base; import java.io.UnsupportedEncodingException; /** * 转换字符串的编码 */ public class C ...
- Java基础学习笔记二十三 Java核心语法之反射
类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任 ...
随机推荐
- Mysql 修改、删除字段默认值
问题描述: 建表的时候,某个表中的字段设置了默认值,后期发生需求变更,不需要提供默认值,或者需要改为其它默认值. 问题解决: alter table 表名 alter column 字段名 drop ...
- wso2~部署~v4.2.0-alpha本地构建
让我先查看一下v4.2.0-alpha分支的构建相关信息: Search files... 让我继续查看构建说明文档: Search files... 让我查看一下项目根目录下的文件: Ran too ...
- cmake基础知识
Cmake Cmake变量 Cmake的基本类型只有两种,分别是字符串和字符串列表,使用set命令来设置变量 set(var abc) # abc是字符串,var是值为"abc"的 ...
- C++学习思维导图
C++思维导图 整个的思维导图大概的架构如下,Xmind.SVG.PDF格式的下载链接都在下面了,如有需要可自取 Xmind文件分享:https://cnblogs-img.oss-cn-hangzh ...
- python学习课后练习
此次爬虫学习的资源是B站所找,具体如下:Python课程天花板,Python入门+Python爬虫+Python数据分析5天项目实操/Python基础,该课程留了课后练习,我把自己的代码和想法单独整成 ...
- Springboot笔记<7>过滤器与拦截器
过滤器 拦截器 过滤器 过滤器拦截的是URL Spring中自定义过滤器(Filter)一般只有一个方法,返回值是void,当请求到达web容器时,会探测当前请求地址是否配置有过滤器,有则调用该过滤器 ...
- 你应该懂的AI大模型(六)之 transformers
一.Transformer与transformers 结论:Transformer是模型架构,transfortmers是库. 问:为什么我们要知道Transformer与transformers呢? ...
- kards卡组推荐——苏美隐蔽
声明:此卡组抗快能力极差,害怕炮兵和小飞机为中后期 隐蔽核心思路: 在第一回合,尽量找杜斯团,找不到如果对方有单位,可以打一个镰刀 第二回合,①有杜斯团:打出隐蔽单位,按隐蔽顺序(附1)出,如果只有1 ...
- hot100之二叉树上
二叉树的中序队列(094) 先看代码 class Solution { public List<Integer> inorderTraversal(TreeNode root) { Lis ...
- AI大模型完全本地化部署指南——从零硬件开始
本文将从基础硬件购置开始讲起,真正意义上从零开始,最终通过Ollama.LangChain.DeepSeek的一系列交互,输出本地大模型的第一声啼鸣,带你走进另一片广阔的世界.update:2025- ...