尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
爆肝一个月,Java 从入门到精通21 - 字符串和编码
String在Java中String是一个引用类型它本身也是一个class。但是Java编译器对String有特殊处理即可以直接用...来表示一个字符串String s1 Hello!;实际上字符串在String内部是通过一个char[]数组表示的因此按下面的写法也是可以的String s2 new String(new char[] {H, e, l, l, o, !});因为String太常用了所以Java提供了...这种字符串字面量表示方法。Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段以及没有任何修改char[]的方法实现的。我们来看一个例子// String public class Main { public static void main(String[] args) { String s Hello; System.out.println(s); s s.toUpperCase(); System.out.println(s); } }根据上面代码的输出试解释字符串内容是否改变。字符串比较当我们想要比较两个字符串是否相同时要特别注意我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用。我们看下面的例子// String public class Main { public static void main(String[] args) { String s1 hello; String s2 hello; System.out.println(s1 s2); System.out.println(s1.equals(s2)); } }从表面上看两个字符串用和equals()比较都为true但实际上那只是Java编译器在编译期会自动把所有相同的字符串当作一个对象放入常量池自然s1和s2的引用就是相同的。所以这种比较返回true纯属巧合。换一种写法比较就会失败// String public class Main { public static void main(String[] args) { String s1 hello; String s2 HELLO.toLowerCase(); System.out.println(s1 s2); System.out.println(s1.equals(s2)); } }结论两个字符串比较必须总是使用equals()方法。要忽略大小写比较使用equalsIgnoreCase()方法。String类还提供了多种方法来搜索子串、提取子串。常用的方法有// 是否包含子串: Hello.contains(ll); // true注意到contains()方法的参数是CharSequence而不是String因为CharSequence是String实现的一个接口。搜索子串的更多的例子Hello.indexOf(l); // 2 Hello.lastIndexOf(l); // 3 Hello.startsWith(He); // true Hello.endsWith(lo); // true提取子串的例子Hello.substring(2); // llo Hello.substring(2, 4); ll注意索引号是从0开始的。去除首尾空白字符使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格\t\r\n \tHello\r\n .trim(); // Hello注意trim()并没有改变字符串的内容而是返回了一个新字符串。另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是类似中文的空格字符\u3000也会被移除\u3000Hello\u3000.strip(); // Hello Hello .stripLeading(); // Hello Hello .stripTrailing(); // HelloString还提供了isEmpty()和isBlank()来判断字符串是否为空和空白字符串.isEmpty(); // true因为字符串长度为0 .isEmpty(); // false因为字符串长度不为0 \n.isBlank(); // true因为只包含空白字符 Hello .isBlank(); // false因为包含非空白字符替换子串要在字符串中替换子串有两种方法。一种是根据字符或字符串替换String s hello; s.replace(l, w); // hewwo所有字符l被替换为w s.replace(ll, ~~); // he~~o所有子串ll被替换为~~另一种是通过正则表达式替换String s A,,B;C ,D; s.replaceAll([\\,\\;\\s], ,); // A,B,C,D上面的代码通过正则表达式把匹配的子串统一替换为,。关于正则表达式的用法我们会在后面详细讲解。分割字符串要分割字符串使用split()方法并且传入的也是正则表达式String s A,B,C,D; String[] ss s.split(\\,); // {A, B, C, D}拼接字符串拼接字符串使用静态方法join()它用指定的字符串连接字符串数组String[] arr {A, B, C}; String s String.join(***, arr); // A***B***C格式化字符串字符串提供了formatted()方法和format()静态方法可以传入其他参数替换占位符然后生成新的字符串// String public class Main { public static void main(String[] args) { String s Hi %s, your score is %d!; System.out.println(s.formatted(Alice, 80)); System.out.println(String.format(Hi %s, your score is %.2f!, Bob, 59.5)); } }有几个占位符后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有%s显示字符串%d显示整数%x显示十六进制整数%f显示浮点数。占位符还可以带格式例如%.2f表示显示两位小数。如果你不确定用啥占位符那就始终用%s因为%s可以显示任何数据类型。要查看完整的格式化语法请参考JDK文档。类型转换要把任意基本类型或引用类型转换为字符串可以使用静态方法valueOf()。这是一个重载方法编译器会根据参数自动选择合适的方法String.valueOf(123); // 123 String.valueOf(45.67); // 45.67 String.valueOf(true); // true String.valueOf(new Object()); // 类似java.lang.Object636be97c要把字符串转换为其他类型就需要根据情况。例如把字符串转换为int类型int n1 Integer.parseInt(123); // 123 int n2 Integer.parseInt(ff, 16); // 按十六进制转换255把字符串转换为boolean类型boolean b1 Boolean.parseBoolean(true); // true boolean b2 Boolean.parseBoolean(FALSE); // false要特别注意Integer有个getInteger(String)方法它不是将字符串转换为int而是把该字符串对应的系统变量转换为IntegerInteger.getInteger(java.version); // 版本号11转换为char[]String和char[]类型可以互相转换方法是char[] cs Hello.toCharArray(); // String - char[] String s new String(cs); // char[] - String如果修改了char[]数组String并不会改变// String - char[] public class Main { public static void main(String[] args) { char[] cs Hello.toCharArray(); String s new String(cs); System.out.println(s); cs[0] X; System.out.println(s); } }这是因为通过new String(char[])创建新的String实例时它并不会直接引用传入的char[]数组而是会复制一份所以修改外部的char[]数组不会影响String实例内部的char[]数组因为这是两个不同的数组。从String的不变性设计可以看出如果传入的对象有可能改变我们需要复制而不是直接引用。例如下面的代码设计了一个Score类保存一组学生的成绩// int[] import java.util.Arrays; public class Main { public static void main(String[] args) { int[] scores new int[] { 88, 77, 51, 66 }; Score s new Score(scores); s.printScores(); scores[2] 99; s.printScores(); } } class Score { private int[] scores; public Score(int[] scores) { this.scores scores; } public void printScores() { System.out.println(Arrays.toString(scores)); } }观察两次输出由于Score内部直接引用了外部传入的int[]数组这会造成外部代码对int[]数组的修改影响到Score类的字段。如果外部代码不可信这就会造成安全隐患。请修复Score的构造方法使得外部代码对数组的修改不影响Score实例的int[]字段。字符编码在早期的计算机系统中为了给字符编码美国国家标准学会American National Standard InstituteANSI制定了一套英文字母、数字和常用符号的编码它占用一个字节编码范围从0到127最高位始终为0称为ASCII编码。例如字符A的编码是0x41字符1的编码是0x31。如果要把汉字也纳入计算机编码很显然一个字节是不够的。GB2312标准使用两个字节表示一个汉字其中第一个字节的最高位始终为1以便和ASCII编码区分开。例如汉字中的GB2312编码是0xd6d0。类似的日文有Shift_JIS编码韩文有EUC-KR编码这些编码因为标准不统一同时使用就会产生冲突。为了统一全球所有语言的编码全球统一码联盟发布了Unicode编码它把世界上主要语言都纳入同一个编码这样中文、日文、韩文和其他语言就不会冲突。Unicode编码需要两个或者更多字节表示我们可以比较中英文字符在ASCII、GB2312和Unicode的编码英文字符A的ASCII编码和Unicode编码┌────┐ ASCII: │ 41 │ └────┘ ┌────┬────┐ Unicode: │ 00 │ 41 │ └────┴────┘英文字符的Unicode编码就是简单地在前面添加一个00字节。中文字符中的GB2312编码和Unicode编码┌────┬────┐ GB2312: │ d6 │ d0 │ └────┴────┘ ┌────┬────┐ Unicode: │ 4e │ 2d │ └────┴────┘那我们经常使用的UTF-8又是什么编码呢因为英文字符的Unicode编码高字节总是00包含大量英文的文本会浪费空间所以出现了UTF-8编码它是一种变长编码用来把固定长度的Unicode编码变成14字节的变长编码。通过UTF-8编码英文字符A的UTF-8编码变为0x41正好和ASCII码一致而中文中的UTF-8编码为3字节0xe4b8ad。UTF-8编码的另一个好处是容错能力强。如果传输过程中某些字符出错不会影响后续字符因为UTF-8编码依靠高字节位来确定一个字符究竟是几个字节它经常用来作为传输编码。在Java中char类型实际上就是两个字节的Unicode编码。如果我们要手动把字符串转换成其他编码可以这样做byte[] b1 Hello.getBytes(); // 按系统默认编码转换不推荐 byte[] b2 Hello.getBytes(UTF-8); // 按UTF-8编码转换 byte[] b2 Hello.getBytes(GBK); // 按GBK编码转换 byte[] b3 Hello.getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换注意转换编码后就不再是char类型而是byte类型表示的数组。如果要把已知编码的byte[]转换为String可以这样做byte[] b ... String s1 new String(b, GBK); // 按GBK转换 String s2 new String(b, StandardCharsets.UTF_8); // 按UTF-8转换始终牢记Java的String和char在内存中总是以Unicode编码表示。延伸阅读对于不同版本的JDKString类在内存中有不同的优化方式。具体来说早期JDK版本的String总是以char[]存储它的定义如下public final class String { private final char[] value; private final int offset; private final int count; }而较新的JDK版本的String则以byte[]存储如果String仅包含ASCII字符则每个byte存储一个字符否则每两个byte存储一个字符这样做的目的是为了节省内存因为大量的长度较短的String通常仅包含ASCII字符public final class String { private final byte[] value; private final byte coder; // 0 LATIN1, 1 UTF16对于使用者来说String内部的优化不影响任何已有代码因为它的public方法签名是不变的。喜欢这个系列的同学可以关注我爆肝一个月Python 从入门到精通
RELATED

相关推荐

EAS Workflows 实战指南:从 0 到 1 搭建 Expo 应用商店发布 CI/CD 流水线

EAS Workflows 实战指南:从 0 到 1 搭建 Expo 应用商店发布 CI/CD 流水线

EAS Workflows 实战指南:从 0 到 1 搭建 Expo 应用商店发布 CI/CD 流水线 【免费下载链接】skills A collection of AI agent skills for working with Expo projects and Expo Application Services 项目地址: https://gitcode.com/GitHub_Trending/skills9/skil…

📅 2026/10/31 14:38:30
DeepSeek新模型编程基准超Claude,还开放了搜索API:RAG玩家先别急,AI话题解读

DeepSeek新模型编程基准超Claude,还开放了搜索API:RAG玩家先别急,AI话题解读

昨天 AI 圈最大的新闻,DeepSeek 把 V4.1 Flash 更新到了 1017 版本,还开放了自研搜索 API。编程基准 DeepSWE 拿了 77.0 分,超过了 Claude Opus 5。数字看着吓人,今天聊聊这事到底意味着什么,尤其是对做 RAG 的人。 先…

📅 2026/10/31 14:38:30
零基础搭建 OpenClaw ,本地 AI 智能体完整部署避坑指南

零基础搭建 OpenClaw ,本地 AI 智能体完整部署避坑指南

📖 前言 本文面向 Windows 系统用户,系统梳理 OpenClaw 的标准化部署流程。全程无需输入任何命令行,所有操作均依托可视化向导完成,即便是零基础用户也能独立走完整套部署。文中同时汇总了高频报错的对应解决方案,力求…

📅 2026/10/31 14:38:30
MORE NEWS

更多资讯

📰

语音社交产品冷启动策略与内容增长引擎设计

1. 语音社交产品的冷启动困境与破局点2023年全球语音社交市场规模已达47.8亿美元,但新产品的90日留存率普遍低于15%。这个数据背后反映的是语音社交产品面临的典型增长困境:用户获取成本高、内容沉淀难、社交关系链脆弱。我在操盘多款语音社交产品时发现…

📰

终端Raw模式与ANSI语法高亮实现指南

终端里做界面,最让人又爱又恨的东西就是 ANSI 转义序列。爱它,是因为只要一行 \e[38;5;208m ,你就能在任何支持颜色的终端里画出想要的层次;恨它,是因为大多数 UI 框架会把你的字符串当作纯文本处理,帮你…

📰

可解释AI在孤独症诊断中的突破与应用

1. 项目概述:AI如何解码孤独症的大脑密码 在精神健康领域,孤独症谱系障碍(ASD)的诊断一直面临重大挑战。传统诊断主要依赖行为观察和量表评估,存在主观性强、早期识别困难等问题。这项发表在《eClinicalMedicine》的研…

📰

京东价格API接入与动态定价策略实战

1. 京东价格API技术实现概述 在电商运营领域,商品定价策略直接影响转化率和利润率。京东价格API(jd.union.open.goods.price.query)为开发者提供了获取商品历史价格数据的官方通道,通过系统化分析这些数据,可以建立科学…

📰

context-mode 上下文模式设计:从工程概念到落地实践

1. 从“context-mode”说起:一个被低估的工程概念 第一次看到“context-mode”这个词,很多人会下意识地把它归到某个具体框架的配置项里,觉得无非是切换一下运行模式、改个参数的事。但我在实际项目里反复踩坑之后发现,这个词背后…

📰

Minecraft Forge 1.20.1 模组安装指南:从零基础到精通

简介:这份资源是面向 Minecraft 模组开发者的 Forge 1.20.1 模组开发工具包(MDK),适合具备一定 Java 与 Gradle 基础、希望上手或迁移到 1.20.1 版本的开发者使用,可解决从零搭建模组工程、配置构建环境与了解版本变更…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬