JSON、XML、ZIP、加密与正则表达式完全指南
JSON/XML/ZIP/Crypto/Pattern完全指南:JSON(JSON.serialize+deserialize+deserializeStrict+deserializeUntyped/Map键类型限制11种/API28.0+null不序列化+额外字段序列化/API63.0+异常不序列化)、JSONGenerator(writeStartObject/字段/数组/prettyPrint)、JSONParser(JSONToken枚举10种/readValueAs/skipChildren)、XmlStreamReader(前向只读50节点深/hasNext+next/事件类型7种)、XmlStreamWriter(命名空间+属性+CData/空元素)、DOM(Document+XmlNode/createRootElement+addChildElement/getChildElements/XML命名空间/元素vs节点区别)、ZIP(Compression/ZipReader/getEntry+extractEntry)、Crypto(generateDigest+generateMac+sign+encryptWithManagedIV+decryptWithManagedIV+generateAesKey/AWS HMacSHA1集成)、EncodingUtil(urlEncode+base64Encode+convertToHex/HTTP Digest认证)、Pattern+Matcher(compile+matcher/matches+lookingAt+find/start+end+group/region+anchoring+transparent bounds/捕获组编号/1M访问限制/邮件正则化示例)。
本课程共有 11 个章节
同学们,今天我们来聊聊Apex里的一群得力助手——实用程序类。它们就像工具箱里的常用工具,帮我们处理数据交换、安全、压缩和文本这些日常任务。 首先,是处理JSON的类。现在大部分系统之间交换数据都用JSON格式,Apex直接内置了序列化和反序列化的方法,就是说你可以轻松地把一个对象转成JSON字符串发送出去,或者把收到的JSON字符串转回对象来使用,非常方便。 接着,还有处理XML的类。有时候我们需要读写XML文档,Apex提供了两种方式:流和DOM。如果你要处理很大的XML文件,可以用流,一边读一边处理,不用把整个文件加载到内存。而DOM呢,是把整个XML解析成树状结构,方便你来回查找和修改节点。 然后,我们说说压缩和解压缩。Apex自带Zip处理的类,你可以把多个文件打包成一个Zip,或者从Zip里提取文件。比如你要生成一份报告,把相关的文件压缩在一起,就可以用这个功能。 再来看安全方面,Apex支持与AWS集成,用加密来保护数据安全。你可以调用AWS的密钥管理服务,对敏感数据进行加密解密,确保数据在传输和存储中都安全。 日常开发中,经常要处理URL,拼接链接或者拿到URL里的参数,这时候URL编码和解码的类就上场了。比如你要把用户输入的文本变成URL安全的字符串,或者还原回来。 最后是正则表达式的类,用来做文本搜索和匹配。你可以用模式来验证邮箱格式、手机号码,或者从大段文字里提取出你想要的信息。 总结一下,这些实用类就是Apex提供的基础能力封装,让我们的开发更高效、更安全。无论是数据交换、文件压缩、安全加密还是文本处理,它们都帮你做好了准备,只要按需调用就好。 好了,这节课就到这里,下次我们再深入看看每个类具体怎么用。
同学们,我们来看这一页,它讲的是Apex里面处理JSON最核心的几个工具。简单说,Apex给了我们三个主要的类来跟JSON打交道,你可以根据场景灵活选择。 第一个是System.JSON,它是最常用的,提供了高级序列化和反序列化操作。什么叫序列化呢?就是把Apex里的对象、集合这些数据,直接转成JSON字符串。反过来,把一段JSON字符串转回Apex对象,就叫反序列化。用System.JSON,一行代码就搞定了,非常适合大多数情况。 第二个是JSONGenerator,这个类呢,是让你像写文章一样,一步一步“写”出JSON。你可以先用writeStartObject开始一个对象,再用writeStringField写字段,最后writeEndObject结束。这种增量构建的方式,在你需要精细控制输出、或者数据是边算边生成的场景下特别好用。 第三个是JSONParser,它和JSONGenerator正好相反,是读取用的。它不是一次性把整个JSON解析成对象,而是像读流一样,一个令牌一个令牌地读,比如遇到一个左花括号、一个字段名、一个值。这种方式适合解析特别大的JSON,或者你需要逐个检查内容、做特殊处理的情况。 那在解析过程中,可能遇到的每种元素,比如字段名、数字值、布尔值、空值等等,都对应着一种令牌类型。Apex用了一个JSONToken枚举来列举所有这些可能的类型,方便你在遍历的时候做判断。 当然,无论是序列化还是解析,如果格式不对,比如少了个引号,或者结构不合法,这三个类的方法都会抛出异常,这个异常叫JSONException。所以写代码时记得做好异常捕获。 最后我们提一个方便的功能:System.JSON里面还有一个serializePretty方法。它和普通的serialize一样,也是把对象转成JSON,但输出结果是带有缩进和换行的,我们管它叫人类可读的格式。调试或者展示JSON的时候用它,看起来就特别清晰。 以上就是这页的核心内容,三个类各司其职,再加上令牌枚举和异常处理,构成了Apex中完整而灵活的JSON工具集。大家先有个轮廓印象,后面练习时我们还会深入用起来。
同学们,咱们今天来看看Apex里一个很实用的功能——SON序列化。说白了,序列化就是把一个对象转成一串J-S-O-N格式的文本,方便我们在系统之间传数据,或者记日志,又或者在前端处理。 首先,Apex的SON序列化支持哪几类东西呢? 它很强大,普通的数据类型,比如字符串、数字、日期,还有集合里的列表、集合,都能序列化。最常用的是sObject,像Account、Contact这些标准或自定义的对象,直接就能转成JSON。还有数据库查询返回的结果容器,比如单个sObject或者列表,也支持。更棒的是,你完全可以自己写一个Apex类,里面定义几个属性,它也能被序列化,这样你就能把复杂的结构化数据打包成JSON发出去。 但是,这里面有几个坑得小心。 第一个限制:托管包里的Apex类实例,如果从包外面想要序列化它,对不起,不行。这是出于安全考虑,防止外部代码偷窥包里的数据结构。 第二个,如果你序列化一个Map,它对键的类型要求很严格,只能是那些简单的、可以直接排序的类型,像布尔值、日期、时间等等,复杂对象比如sObject绝对不行。 第三个,父子关系的问题很有意思。比如一个Account下面有Contacts,你把Account作为父对象序列化,它内嵌的子列表里每个Contact对象却会丢掉那些只在子类型里才有的字段。它只会保留Contact和Account共有的那部分属性,有点坑人。 还有,如果你试图序列化一个自引用的对象图,比如一个节点自己指向自己,Apex会抛出一个JSONResponse异常来保护你,阻止无限递归。即便你强行走通了,序列化出来的引用图其实是拷贝,不再是原对象引用关系。 最后,JSONParser这个类本身是不能被序列化的,你得用它来解析JSON,而不是把它打包进去。 再讲几个版本变迁中的行为差异,因为不同API版本编译的代码表现会不一样: 从API 63.0开始,如果代码执行中抛出了异常,你在序列化时不会把这个异常对象也转成JSON——它直接阻止这么做,这是为了避免敏感信息泄漏。 更早的API 53.0修复了一个日期时间精度的问题,之前可能损失毫秒,现在能精准保留了。 如果你用API 28.0以上版本,查询出的sObject在序列化时会自动带上关联查询的外键字段,但请注意,值为空的字段是不会出现的,这是为了减小JSON体积。 而API 34.0以下的版本里,有一个历史遗留问题:把一个序列化后的JSON再反序列化回来,对象的ID比较可能会失败,因为ID的内部结构不一样了。用新版本就没这烦恼。 记住这些规则,你在写集成或调试的时候就能少走很多弯路。好了,今天就说到这儿,大家可以在练习项目里动手试试,感受一下JSON序列化的方便与限制。
大家好,今天我们来聊聊 Apex 里一个非常方便的类——JSONGenerator。很多时候我们需要在代码里生成 JSON 数据,比如要传给外部系统,或者构造一个响应。如果手动拼接字符串,很容易出错,而且代码不美观。JSONGenerator 就提供了很优雅的增量式构建方式。 首先,我们怎么创建它呢?就是用 JSON.createGenerator,后面可以传一个布尔参数 true。这个 true 的作用是开启“漂亮打印”,让生成的 JSON 自动带缩进和换行,阅读起来特别清晰。如果传 false 或者不传,那就是紧凑格式。创建好以后,我们就可以一步步往里写内容了。 写入的时候,就像在搭积木:用一个 writeStartObject 表示一个大括号开始,这是对象的边界;写完里面的内容后,再用 writeEndObject 把这个对象关上。如果字段名需要写,就用 writeFieldName 指定名字。然后,我们可以用一系列类型化的写方法,比如 writeNumberField、writeStringField、writeBooleanField,甚至是 writeNullField,这些方法直接接收字段名和值,一步到位,省得你先写字段名再写值。还有一个 writeObjectField,可以把你一个字段名和一个 Apex 对象一起传进去,它会自动把对象序列化放到这个字段下面。 那如果要写整个 Apex 对象呢?还有一个更厉害的方法叫 writeObject,直接把一个 Apex 实例传给它,它就会把对象的所有可序列化属性展开成一个 JSON 对象,嵌套在当前位置。这对于把 Salesforce 记录输出成 JSON 特别便捷。 最后,等全部内容写入完毕,调用 getAsString 方法,就能拿到完整的 JSON 字符串了。 为了让你更直观地理解,我描述一下我们课堂上演示的例子:我们构建了一个嵌套的 JSON 结构,里面有一个人的基本信息,比如姓名和年龄;然后有一个地址,这是一个嵌套对象,包含了城市和街道;还有一个爱好列表,是一个数组,里面放了几项字符串;最精彩的是,我们还直接序列化了一个自定义 Apex 对象,比如一个 Account 记录,它被整个放到了 “account” 字段下面。所有这些内容,都集中在一个 JSON 结构里,清晰得很。通过这个例子,你就能看到 JSONGenerator 处理复杂层级是多么游刃有余。 总结一下,JSONGenerator 就是用增量、类型安全的方式来生成 JSON,避免手动拼接,支持嵌套对象、数组,以及任何 Apex 对象的直接序列化。希望今天的讲解能帮你轻松上手,以后构建 JSON 就用它吧。
好,同学们,咱们今天来看一看 Apex 里怎么解析 JSON 数据。JSON 这种格式大家现在应该不陌生了,前后端交互、调用外部 API,响应回来的很多都是 JSON。Salesforce 给咱们提供了一个特别好用的工具,叫 JSONParser。 这个 JSONParser 呢,主要支持两种解析思路,你可以根据场景来选。 第一种,我叫它“逐个令牌”的方式,也就是 token-by-token。怎么做呢?你先用 nextToken() 方法来初始化解析器,它会指向 JSON 里的第一个令牌。然后你可以在循环里,根据 JSONToken 这个枚举,检查当前令牌是什么类型——比如是不是字段名、是不是值、是不是一个数组的开始。用 getCurrentToken() 一看就知道了。如果当前令牌正好是字段名,那咱们再用 getText() 把字段名和它对应的值提取出来。这种方式很灵活,特别适合处理那些字段顺序可能不确定、或者你只需要部分数据的情况。而且它对字段名称的匹配也做得非常优雅,你代码写起来会很清爽。 第二种方法更直接,叫 readValueAs。你只要给它一个目标类型,它就能把一整个 JSON 对象直接反序列化成一个 Apex 类的实例。比如你有一个定义好属性的类,结构跟要解析的 JSON 对象正好对得上,那你一行代码就能把 JSON 变成对象。但前提是类的结构必须和 JSON 完全匹配,字段名、层级都得对应好,不然会报错。 咱们再讲一个很实用的小技巧:skipChildren()。当你在遍历一个 JSON 数组,读完一个完整的对象之后,如果这个对象里面还嵌套着子数组或者子对象,你不想一层层手动解析,skipChildren() 就能帮你直接跳过这些子元素,让你快速定位到下一个同级元素,这在处理对象数组的时候特别省事。 最后我举一个例子。假设你调用了一个收件箱相关的 API,返回的 JSON 里有很多版本信息。要求你从这个响应里提取出 API 版本号,构建出一个从版本号到发布标签的 Map。那你就可以用 token-by-token 的方式,定位到版本号和标签字段,灵活地把数据塞进 Map 里。或者如果你定义好了对应的 Apex 类,直接用 readValueAs 把整个响应映射成对象也挺省心。 所以,掌握好这两种方法,再加上 skipChildren() 的灵活运用,你处理绝大多数的 JSON 解析场景都会游刃有余。好,这部分就讲到这儿,大家有什么问题吗?
同学们,今天我们来看看在Salesforce Apex里怎么用流的方式去读写XML。你平常处理数据的时候,遇到XML结构不想一股脑全装进内存,就可以用这两个类:XmlStreamReader和XmlStreamWriter。 先讲XmlStreamReader,它就像一个只能向前翻页的电纸书,你只能一页一页往后读,不能回头,这就是“前向只读”。它通过事件来告诉你读到了什么,比如START_ELEMENT表示一个标签开始了,END_ELEMENT表示标签结束,CHARACTERS表示遇到了一段文本,还有ATTRIBUTE这些事件。你在用的时候一定要养成一个好习惯:每次调用next()跳到下一个事件之前,先用hasNext()检查一下是不是已经读到结尾了,不然就越界报错,就像翻书翻过最后一页一样。要拿到标签的名字,我们用getLocalName();如果想取属性值,就调getAttributeValue();遇到元素里面的文字内容,就用getText()把它们拿出来。这些方法都很直接,一看名字就知道是做什么的。 再说XmlStreamWriter,它正好相反,是用来“盖房子”,一步一步把XML结构搭出来。你得按顺序来:先writeStartDocument,表示文档开始;然后如果用到命名空间,就用带命名空间的writeStartElement写开始标签;接着如果有属性就writeAttribute,有文字内容就writeCharacters;最后别忘了writeEndElement结束这个标签,以及整个文档结束的writeEndDocument。这样一套流程走下来,一个完整的XML就生成好了。这个类还支持写处理指令、DTD、注释,甚至可以直接写空元素。 最后记住一个小限制:Apex的流式XML处理最多只能支持50层节点的嵌套,如果你写的XML层层套得太深,超过50层就会报错,所以在设计结构时要留心。 好啦,这就是XmlStreamReader和XmlStreamWriter的基本用法,理解了事件的顺序和这些方法的职责,你就能轻松地在Apex里流式处理XML了。
我们这节课来聊聊 Apex 里一个很实用的工具——Dom 类,全称是 Document Object Model,用它可以解析和生成完整的 HTML 或者 XML 文档。这在做接口集成、处理 HTTP 响应时特别有用。 我们先看怎么解析。比如你通过 HTTP 请求拿回了一段 HTML,怎么把它变成程序能懂的结构呢?你可以用 `getBodyDocument()` 方法,直接从响应里提取出文档对象,这就是整棵节点树的入口。拿到文档后,你会得到一个根元素,比如 `<html>`,然后就可以通过它的名字,或者带命名空间的方式,一层层访问子元素了。注意,标签之间的文本在 DOM 里并不是直接挂在元素上的,而是一个单独的文本节点,你要拿文本内容的话,需要用相应的方法把它取出来。 再说创建。如果你想自己拼装一个 HTML 文档,比如要作为 HTTP 请求的 body 发出去,就得用构造器新建一个 `Dom.Document` 对象,然后创建根元素。这里有个小细节,如果涉及命名空间,建根元素的时候最好带上命名空间的 URI 和前缀,这样能保证唯一性。接着,你可以用 `addChildElement()` 方法给它加子元素,一层套一层,形成嵌套结构。想放文字内容的话,就用 `addTextNode()` 来添加一个文本节点。整个文档建好之后,可以通过 `setBodyDocument()` 把它塞到一个 HTTP 请求里,再发出去。 这里我再强调一下节点和元素的区别:元素是那些带尖括号的标签,比如 `<p>`,而它里面的文字是另一个节点,叫文本节点。它们共同构成一棵树,解析或生成的时候要分清这两种节点。 另外,Apex 里对多层嵌套的支持是有限制的,最多支持 50 层深度的嵌入式标记。也就是说,你不能无限地 `<div>` 套 `<div>`,超过 50 层就会报错,在设计结构时要注意这一点。 好了,这就是今天的内容,Dom 类让我们能直接操作 HTML 文档结构,非常灵活。你可以试着用它处理一些简单的 XML 解析或者动态生成 HTML 的场景,会很有帮助。
同学们,今天我们来聊聊Apex里处理压缩和加密的两个工具,分别是 Compression 命名空间和 Crypto 类。别担心,它们听起来复杂,其实用起来很直接。 先看压缩,就是那个 ZIP 文件。很多时候我们接收到的数据是打包好的 ZIP 存档,如果只想拿里面某一个文件,难道要把整个包都解压吗?不用。Apex 提供了 ZipReader 类,你可以把它想象成一个能直接阅读 ZIP 文件的读卡器。我们把 ZIP 文件内容作为 Blob 数据交给 ZipReader,然后调用 getEntry 方法,写下你想找的文件名,它就能定位到那个具体的文件。再调用 extractEntry 方法,就能把那个文件的原始数据取出来,剩下的文件根本不用动,既省时间又省资源。记住,三个步骤:用 Blob 创建 ZipReader,getEntry 找到文件,extractEntry 取出数据。 接下来是加密相关的 Crypto 类。它把几种安全算法都准备好了,我们直接调用就行。第一类是生成摘要,也叫做散列,可以用来验证数据有没有被篡改。用 generateDigest 方法,指定算法,比如 SHA1、SHA256 或者 MD5,传入原始数据,就能得到一个独一无二的指纹串。第二类是消息认证码,也就是 HMAC,在散列的基础上加了一把钥匙,既验证完整性又验证来源。用 generateMac 方法,算法可以选 HMacSHA1 或 HMacSHA256,同时提供密钥和数据,它就会算出认证码。第三类是对称加密 AES,用来保护数据的机密性。加密用 encryptWithManagedIV,解密用 decryptWithManagedIV,支持 128、192 和 256 位的密钥长度,它会自动帮你处理初始化向量,让操作更安全也更简单。 举个实际场景:AWS 集成的时候,我们需要对请求进行签名,用的就是 HMacSHA1。拿请求里的关键信息和密钥一起算出一个 HMAC 签名,放到请求头里,AWS 那边就能凭同样的算法验证是你发送的请求,没被篡改过。 最后提醒一下,在做单元测试的时候,一定要验证加密和解密能不能正确往返——也就是加密后再解密,应该得到原始数据。同时还要故意传一个错误的密钥长度,看看系统会不会抛出 SecurityException,这样就能确保我们的代码对密钥长度有正确的校验。 好了,今天关于压缩和加密的工具就讲到这里。大家可以在开发者文档里多看看这些方法的示例,动手试一试。
同学们,咱们今天聊聊Apex里一个非常实用的小工具类,叫EncodingUtil。你别看它名字里带着“编码”,就觉得难懂,其实它特别简单,就是帮我们把数据转换成各种各样的格式,方便我们和外部系统打交道。 那它到底能做什么呢?主要有三类操作。 第一类,URL编码和解码。你上网的时候,网址里有些特殊字符,比如空格、问号、等于号,如果直接放在URL里,可能会被误解。这时候,我们就需要用“URL编码”把它们变成安全的格式,比如空格变成%20。在Apex里,如果你想对一个查询参数进行编码,比如要传一个时间戳,你就可以用EncodingUtil.urlEncode,指定编码方式,比如UTF-8,轻松搞定。 第二类,Base64编码和解码。Base64是一种把二进制数据转成纯文本的方式,在网络传输中特别常用。比如,你跟AWS服务集成,需要给请求头加一个HMAC签名,这个签名通常就是二进制数据,但HTTP头只能放文本,怎么办呢?直接用EncodingUtil.base64Encode,把签名转成Base64字符串,就可以放进Authorization头里了。反过来,收到Base64数据,也可以用base64Decode解码回原来的二进制。 第三类,十六进制转换。有时候我们需要把数据变成十六进制字符串,比如在实现HTTP Digest身份验证时,根据RFC 2617规范,我们需要计算SHA-1哈希值,然后把它转成十六进制摘要。EncodingUtil的convertToHex方法就能直接把二进制哈希值转成十六进制字符串,非常方便。当然,也有convertFromHex,把十六进制转回二进制。 其实总结下来,EncodingUtil这个类就是专门用来处理这些“转换杂务”的,尤其当你的代码需要调用外部Web服务,要传编码参数、身份验证凭证时,这些方法简直是救命稻草。你不用自己去写复杂的编码逻辑,直接用现成的,安全又高效。 好了,关于EncodingUtil咱们就讲这么多。下一节我们来看看怎么用这些方法和外部API做具体的集成。有什么问题可以随时打断我。
同学们,今天我们来讲一下Apex里的正则表达式,其实就是模式匹配。这个知识点呢,是直接从Java的正则表达式照搬过来的,所以如果你用过Java,就很容易上手。 首先,我们要用到一个叫Pattern的类。怎么创建一个Pattern呢?很简单,用Pattern.compile方法,把你想匹配的正则表达式作为字符串传进去,就得到一个编译好的模式对象了。有了它之后,再用这个模式的matcher方法,把你想要搜索的字符串,也就是输入,传进去,就会得到一个Matcher对象。 这个Matcher对象就是我们的匹配器,它有三种最常用的匹配方式: 第一种叫matches,它会检查整个输入字符串,是不是完完全全符合我们定义的模式,从开头到结尾,一个字符都不能多。 第二种叫lookingAt,它只检查输入的开头部分,是不是匹配这个模式,后面有没有多余字符都没关系。 第三种叫find,它会在整个输入里反复扫描,找到每一个匹配的子串,就像拿一个探照灯,逐个点亮匹配的位置。 当你用find或者lookingAt找到匹配后,就可以用三个方法提取详细信息:start方法返回匹配到的子串的起始索引,end方法返回结束索引,group方法返回匹配到的文本内容。 另外,Matcher还支持区域限制。你可以给匹配器指定一个输入字符串的子区域,只在这个区域内搜索,外边的部分就自动忽略了。这样做可以提高效率,也能精准控制匹配范围。 说到区域边界,还有一个边界行为的概念。排队边界就是默认的——在区域的起始和结束位置,像一堵墙一样,不能跨边界匹配。但你可以设置成透明的边界,这样就允许匹配时查看区域外边的字符,方便某些需要上下文的正则条件。 最后,我们再简单说一下捕获组。正则表达式里用小括号包起来的部分就是捕获组。组的编号是从左到右,根据左括号出现的顺序来数的。特别注意,组0总是代表整个表达式匹配到的全部内容,第一个左括号是组1,依次类推。 好了,今天关于Apex中的Pattern和Matchers就讲到这里。记住这几个操作和方法,就足够应付日常的正则匹配需求了。大家下课!
今天我们来聊聊 Apex 里处理电子邮件的一个实用例子——怎么用正则表达式把乱七八糟的邮箱地址整理成统一的格式,并且还能找出重复的记录。这个例子虽然不长,但里头的门道不少,我们一步步拆开来讲。 首先,想象一下,有些客户填写的公司邮箱可能带着各种奇怪的子域名,比如 john@*.smithco.com,这种格式其实不太标准。我们想要把这种地址简化成 john@smithco.com。怎么做到呢?就用到了带有,命名捕获组,的正则表达式。 这个正则大致会分成两部分:一个是用户名,一个是域名。我们用命名捕获组把这两块分别抓出来,比如给用户名起名叫 `user`,域名叫 `domain`。然后,在匹配的过程中,我们还能对捕获到的域名部分做进一步的清洗,去掉那些多余的星号、点号之类的,只保留 `smithco.com`。 具体代码里,我们会调用 `matcher.replaceAll('')` 这样一个方法。它的作用是把每个匹配到的邮箱地址替换成我们规范化后的格式。这里头其实隐含了一个逻辑:如果你在替换的字符串里引用命名捕获组,就可以重新组合出干净的邮箱。 好,规范化这一步做完了,接下来要在批处理里检测重复的邮箱。这里的“批内”是指我们可能一次性处理一批数据,比如几百条联系人记录。为了防止同一批数据里有重复的规范化邮箱地址,我们可以建一个 Map,Key 就是规范化后的邮箱,Value 是记录本身。这样,当发现 Key 已经存在的时候,就知道遇到了重复,可以进行相应的处理,比如标记或者跳过。 相比批内去重,更关键的是要和数据库里已有的数据比对。Apex 里通常会有一个自定义字段,比如叫 `duplicate_key__c`,专门存储规范化后的邮箱。这样我们就可以直接用 SOQL 查询:“找出所有 `duplicate_key__c` 等于这个新邮箱的现有记录”。如果查到了,就说明老记录已经存在,我们就能做出防重逻辑。 以上是实际业务里和 Salesforce 平台结合的做法。现在再来看一个更基础的正则匹配小例子,帮助大家理解 `Matcher` 的一些方法。 假设我们用一个简单的正则,去匹配一个字符串,并且带了一个捕获组。例如,匹配字母 “a”,并且把它捕获。当你调用 `matcher.find()` 之后,可以用 `matcher.group(0)` 得到整个匹配到的字符串,用 `matcher.group(1)` 得到第一个捕获组的内容。这里注意,`group(0)` 总是代表整个匹配,不管你有没有显式的括号。 还有一个 `end()` 方法,它返回的是最后一个匹配字符后面的那个位置的索引偏移量。这个索引是从 0 开始数的,所以如果整个匹配占了字符串的前几个字符,`end()` 得到的数字其实就是匹配部分的长度,因为它是最后一个字符后面的位置。比如匹配到的部分是从索引 0 到 2,那 `end()` 就是 3。 理解这些小细节,对于后面编写更复杂的正则替换逻辑非常有帮助。 总结一下这一页 Slide 的核心:我们学会了用正则表达式抓取邮箱的用户和域,清洗域名,再用 `replaceAll` 格式化。同时,在批处理内部用 Map 去重,并且利用数据库字段做跨记录比对,防止数据重复。最后还温习了 Matcher 里的 `group` 和 `end` 方法,为实战打下基础。 这样讲,是不是清晰多了?有疑问随时提。