Go语言入门实战:从零实现分类统计Demo
1. 从零拆解一个Go语言入门Demo的设计思路1.1 为什么选择用“分类”场景来练手Go很多人第一次接触Go语言最头疼的不是语法本身而是不知道拿它来做什么。语法书翻完一遍变量、循环、函数都认识了但真让自己从零写一个能跑起来、能看到结果的小项目脑子里一片空白。我当初也是这样过来的后来发现一个特别适合新手的切入场景——分类统计。分类统计这件事本质上就是“把一堆杂乱的数据按照某种规则归到不同的类别里然后数一数每类有多少个”。听起来简单但它几乎涵盖了Go语言入门阶段所有需要掌握的核心知识点变量声明、切片操作、map的使用、循环遍历、函数封装、条件判断、字符串处理、甚至简单的文件读写。你把这一个Demo吃透了Go的基础就算真正过关了。而且分类这个场景特别贴近实际。比如你有一堆商品名称想按首字母分类你有一堆日志行想按日志级别分类你有一堆文件路径想按扩展名分类。这些需求在真实工作中随处可见不是那种为了教学硬编出来的“计算1加到100”的假例子。用真实需求驱动学习效率比干啃语法高得多。提示新手选练手项目最重要的标准是“能立刻看到结果”。分类统计Demo跑完能直接在终端打印出分类结果这种即时反馈对建立信心非常关键。1.2 这个Demo到底要解决什么问题我们把需求说具体一点。假设你手里有一个文本文件里面每行是一个商品名称比如“苹果”“香蕉”“橙子”“菠菜”“白菜”等等。现在需要写一个Go程序把这些商品按照首字符的拼音首字母或者预设的类别映射进行归类最后输出每个类别下有哪些商品、各有多少个。这个需求拆解下来包含以下几个子任务读取数据源可以是硬编码的切片也可以是外部文件定义分类规则首字母映射表或者关键词匹配表遍历数据逐个判断归属类别用map结构存储分类结果格式化输出统计信息每一步都不复杂但串起来就是一个完整的程序。而且这个结构可以无限扩展——数据源可以从切片换成文件、换成数据库、换成网络接口分类规则可以从首字母换成关键词、换成正则、换成机器学习模型输出可以从终端打印换成写入文件、换成HTTP接口返回JSON。这就是一个“最小可扩展原型”的价值。1.3 适合什么基础的人来跟着做这个Demo我推荐给两类人。第一类是完全零基础但想快速上手Go的朋友你不需要懂什么并发、不需要懂什么接口设计只要会基本的变量和循环就能跟着写下来。第二类是从其他语言转Go的开发者你可能Python或Java写得很溜但想快速感受一下Go的编码风格和工具链这个Demo体量刚好半小时能写完写完就能跑。如果你已经能熟练写Go的Web服务了这个Demo对你来说太简单可以跳过。但如果你连go mod init都没用过连go run都没敲过那这个Demo就是为你准备的。2. 环境准备与项目骨架搭建2.1 Go环境安装的两种方式与选择建议在开始写代码之前得先把Go环境装好。目前主流的方式有两种官方安装包和压缩包手动配置。两种方式我都用过各有适用场景。官方安装包Windows的.msi、macOS的.pkg的好处是傻瓜式双击下一步就完事了环境变量自动配好装完直接在终端敲go version就能看到版本号。缺点是安装路径固定想换版本稍微麻烦一点。压缩包方式下载.zip或.tar.gz的好处是灵活你想装几个版本就装几个版本解压到不同目录切换的时候改一下环境变量就行。缺点是得手动配置GOROOT和PATH新手容易在这里卡住。我个人的建议是如果你只是学习用直接下官方安装包省事。如果你以后可能要同时维护多个项目、需要切换Go版本那就用压缩包方式一开始麻烦一点后面省心。安装完成后验证步骤不能省。打开终端依次执行go version go env GOROOT go env GOPATHgo version输出类似go version go1.22.0 darwin/amd64就说明安装成功了。GOROOT是Go的安装目录GOPATH是你的工作目录默认在用户目录下的go文件夹。这两个路径记一下后面找依赖包的时候会用到。注意Windows用户如果敲go提示“不是内部或外部命令”八成是PATH没配好。检查一下Go的bin目录有没有加到系统环境变量里。这个坑我见过太多人踩了。2.2 用go mod初始化项目的完整流程Go 1.11之后引入了Go Modules这是现在管理依赖的标准方式。以前那种必须把代码放在GOPATH/src下面的日子已经过去了现在你在任何目录下都能建项目。初始化项目的命令很简单mkdir go-classify-demo cd go-classify-demo go mod init classify-demo执行完你会看到一个go.mod文件内容大概长这样module classify-demo go 1.22这个文件就是项目的“身份证”记录了模块名称和Go版本。后面如果你引入了第三方依赖也会自动写到这里面。这里有个细节值得说一下模块名称classify-demo你可以随便起但建议跟项目目录名保持一致方便识别。如果你以后要把这个项目推到代码托管平台模块名通常会写成域名/用户名/项目名的格式比如example.com/user/classify-demo。本地练习的话简单点就行。2.3 目录结构设计与文件划分虽然是个小Demo但目录结构还是要有讲究。我见过很多新手把所有代码都塞在一个main.go里几百行堆在一起找个函数要翻半天。哪怕是小项目也建议按职责拆分文件。我推荐的目录结构是这样的go-classify-demo/ ├── go.mod ├── main.go ├── data/ │ └── items.txt └── internal/ └── classifier/ └── classifier.gomain.go是程序入口负责读取数据、调用分类逻辑、打印结果。data/items.txt存放待分类的原始数据。internal/classifier/classifier.go封装分类的核心逻辑。为什么用internal目录这是Go的一个约定internal下面的包只能被同一个项目内的代码引用外部项目无法导入。对于Demo来说这不是必须的但养成这个习惯有好处——你以后写正式项目的时候哪些代码是内部实现、哪些是对外暴露的心里会有数。3. 核心分类逻辑的逐层实现3.1 数据源读取从硬编码到文件读取最开始我们可以把数据直接硬编码在代码里方便快速验证逻辑items : []string{ 苹果, 香蕉, 橙子, 菠菜, 白菜, 梨, 葡萄, 芹菜, 西瓜, 韭菜, }这样写的好处是简单直接不用处理文件读写的错误。但实际项目中数据几乎不可能硬编码在代码里所以第二步我们要把它改成从文件读取。读取文件的代码用标准库的os和bufio就够了func readItemsFromFile(path string) ([]string, error) { file, err : os.Open(path) if err ! nil { return nil, fmt.Errorf(打开文件失败: %w, err) } defer file.Close() var items []string scanner : bufio.NewScanner(file) for scanner.Scan() { line : strings.TrimSpace(scanner.Text()) if line ! { items append(items, line) } } if err : scanner.Err(); err ! nil { return nil, fmt.Errorf(读取文件内容失败: %w, err) } return items, nil }这段代码有几个细节值得展开说。第一defer file.Close()确保文件句柄一定会被释放哪怕后面出了错也不会泄漏。第二strings.TrimSpace去掉每行首尾的空白字符防止因为空格导致分类错误。第三空行直接跳过避免把空字符串当成一个商品。第四scanner.Err()检查扫描过程中是否出错这个容易被忽略但文件很大的时候可能会触发。提示fmt.Errorf里的%w是Go 1.13引入的错误包装语法它保留了原始错误链方便上层用errors.Is或errors.As做判断。新手可以先用%v但养成用%w的习惯更好。3.2 分类规则的抽象与映射表设计分类规则是整个Demo的核心。最简单的做法是按首字符分类但中文首字符直接取第一个字节会乱码因为UTF-8编码下一个中文字符占3个字节。正确的做法是用[]rune转换func firstRune(s string) rune { runes : []rune(s) if len(runes) 0 { return 0 } return runes[0] }但按首字符分类对中文来说意义不大因为“苹”和“葡”都是不同的字。更合理的做法是预设类别映射表把商品名称映射到类别var categoryRules map[string]string{ 苹果: 水果, 香蕉: 水果, 橙子: 水果, 梨: 水果, 葡萄: 水果, 西瓜: 水果, 菠菜: 蔬菜, 白菜: 蔬菜, 芹菜: 蔬菜, 韭菜: 蔬菜, }这个映射表就是我们的“知识库”。实际项目中这个映射可能来自配置文件、数据库或者外部接口。为了Demo的简洁性先用硬编码的map。但硬编码有个问题如果数据里出现了映射表中没有的词怎么办这就需要兜底策略。我通常的做法是归到“其他”类别func classifyItem(item string, rules map[string]string) string { if category, ok : rules[item]; ok { return category } return 其他 }ok这个布尔值用来判断key是否存在这是Go map的经典用法比先判断再取值更高效因为只查一次。3.3 用map聚合分类结果的实现细节有了分类规则接下来就是遍历所有商品把它们归到对应的类别里。这里用map[string][]string来存储key是类别名value是该类别下的商品列表func groupItems(items []string, rules map[string]string) map[string][]string { result : make(map[string][]string) for _, item : range items { category : classifyItem(item, rules) result[category] append(result[category], item) } return result }这段代码看起来简单但有一个容易踩的坑result[category] append(result[category], item)这行如果category这个key还不存在result[category]返回的是nil切片append会创建一个新切片。这个行为是Go语言保证的可以放心用。另一个细节是map的遍历顺序是不确定的。如果你直接for category : range result打印每次运行输出的类别顺序可能不一样。如果需要固定顺序得先把key取出来排序categories : make([]string, 0, len(result)) for category : range result { categories append(categories, category) } sort.Strings(categories)make的时候指定容量len(result)是个好习惯避免切片扩容带来的性能损耗。虽然Demo数据量小无所谓但养成这个习惯对以后写高性能代码有帮助。3.4 格式化输出的几种方案对比输出环节看似简单但要输出得清晰好看也有讲究。我试过几种方案方案一直接打印mapfmt.Println(result)输出是map[水果:[苹果 香蕉 橙子] 蔬菜:[菠菜 白菜]]这种形式。优点是代码最少缺点是格式难看而且map顺序不确定。方案二遍历打印for category, items : range result { fmt.Printf(%s (%d个): %s\n, category, len(items), strings.Join(items, , )) }输出类似水果 (6个): 苹果, 香蕉, 橙子, 梨, 葡萄, 西瓜。比方案一好看多了但顺序还是不确定。方案三排序后打印sort.Strings(categories) for _, category : range categories { items : result[category] fmt.Printf(%s (%d个): %s\n, category, len(items), strings.Join(items, , )) }这是我最推荐的方案输出稳定、格式清晰。strings.Join把切片拼成字符串比手动循环拼接简洁得多。如果你想让输出更“专业”一点可以用text/tabwriter做对齐w : tabwriter.NewWriter(os.Stdout, 0, 0, 2, , 0) for _, category : range categories { items : result[category] fmt.Fprintf(w, %s\t%d个\t%s\n, category, len(items), strings.Join(items, , )) } w.Flush()这样输出的每一列都是对齐的看起来像表格一样整齐。4. 完整代码串联与运行验证4.1 main.go的完整实现把前面所有片段串起来main.go的完整代码如下package main import ( bufio fmt os sort strings ) var categoryRules map[string]string{ 苹果: 水果, 香蕉: 水果, 橙子: 水果, 梨: 水果, 葡萄: 水果, 西瓜: 水果, 菠菜: 蔬菜, 白菜: 蔬菜, 芹菜: 蔬菜, 韭菜: 蔬菜, } func main() { items, err : readItemsFromFile(data/items.txt) if err ! nil { fmt.Fprintf(os.Stderr, 读取数据失败: %v\n, err) os.Exit(1) } result : groupItems(items, categoryRules) printResult(result) } func readItemsFromFile(path string) ([]string, error) { file, err : os.Open(path) if err ! nil { return nil, fmt.Errorf(打开文件失败: %w, err) } defer file.Close() var items []string scanner : bufio.NewScanner(file) for scanner.Scan() { line : strings.TrimSpace(scanner.Text()) if line ! { items append(items, line) } } if err : scanner.Err(); err ! nil { return nil, fmt.Errorf(读取文件内容失败: %w, err) } return items, nil } func classifyItem(item string, rules map[string]string) string { if category, ok : rules[item]; ok { return category } return 其他 } func groupItems(items []string, rules map[string]string) map[string][]string { result : make(map[string][]string) for _, item : range items { category : classifyItem(item, rules) result[category] append(result[category], item) } return result } func printResult(result map[string][]string) { categories : make([]string, 0, len(result)) for category : range result { categories append(categories, category) } sort.Strings(categories) total : 0 for _, category : range categories { items : result[category] total len(items) fmt.Printf(%s (%d个): %s\n, category, len(items), strings.Join(items, , )) } fmt.Printf(总计: %d个商品\n, total) }data/items.txt的内容苹果 香蕉 橙子 菠菜 白菜 梨 葡萄 芹菜 西瓜 韭菜 西红柿注意我故意加了一个“西红柿”它不在映射表里用来验证兜底逻辑是否生效。4.2 运行结果与输出解读在项目根目录下执行go run main.go输出结果其他 (1个): 西红柿 水果 (6个): 苹果, 香蕉, 橙子, 梨, 葡萄, 西瓜 蔬菜 (4个): 菠菜, 白菜, 芹菜, 韭菜 总计: 11个商品可以看到“西红柿”被正确归到了“其他”类别说明兜底逻辑生效了。水果6个、蔬菜4个、其他1个加起来11个跟输入数据的总数一致说明没有遗漏也没有重复。这个输出格式清晰明了类别按字母顺序排列每个类别后面标注了数量最后还有总计。如果你把这个结果重定向到文件里就是一个简单的分类报告。4.3 用go build生成可执行文件go run适合开发阶段快速验证但如果你想把程序分享给别人用最好编译成可执行文件go build -o classify-demo main.go执行完会在当前目录生成一个classify-demoWindows下是classify-demo.exe文件直接双击或者命令行运行就行不需要对方装Go环境。如果你要跨平台编译比如在Mac上编译Windows的可执行文件GOOSwindows GOARCHamd64 go build -o classify-demo.exe main.goGOOS指定目标操作系统GOARCH指定目标架构。这个特性在部署的时候特别有用我经常在本地Mac上编译好Linux的二进制文件直接传到服务器上跑。注意跨平台编译的时候如果代码里用了cgo调用了C语言库可能会失败。纯Go代码没有这个问题。这个Demo是纯Go的可以放心跨平台编译。5. 常见问题排查与避坑经验5.1 文件路径问题导致程序找不到数据新手最容易遇到的错误就是open data/items.txt: no such file or directory。原因通常是运行目录不对。go run main.go默认在当前工作目录下找文件如果你在别的目录执行相对路径就失效了。解决办法有两个。一是确保在项目根目录下执行命令。二是用绝对路径或者基于可执行文件位置计算路径exePath, _ : os.Executable() baseDir : filepath.Dir(exePath) dataPath : filepath.Join(baseDir, data, items.txt)这样不管从哪里执行都能找到正确的文件。不过对于Demo来说最简单的办法就是cd到项目根目录再运行。5.2 中文乱码与编码问题的处理如果你在Windows上创建items.txt默认可能是GBK编码而Go读取文件时按UTF-8处理就会出现乱码。表现是分类结果里出现一堆问号或者奇怪的字符。解决办法是确保文件保存为UTF-8编码。用VS Code或者Notepad打开文件在编码菜单里选择“UTF-8”再保存。Linux和macOS默认就是UTF-8一般不会有这个问题。另一个容易忽略的点是终端编码。Windows的cmd默认可能是GBK即使文件是UTF-8打印出来也可能乱码。可以执行chcp 65001切换到UTF-8编码或者直接用Windows Terminal。5.3 map遍历顺序不确定带来的输出波动前面提到过Go的map遍历顺序是随机的。如果你不排序直接打印每次运行的结果顺序可能不一样。这在调试的时候会让人困惑以为程序有bug。我一开始也踩过这个坑明明代码没改输出顺序却变了。后来查了文档才知道这是Go的刻意设计目的是防止开发者依赖map的顺序。解决办法就是前面说的把key取出来排序后再遍历。5.4 常见问题速查表问题现象可能原因解决方法go: command not foundPATH未配置把Go的bin目录加入系统PATHno such file or directory运行目录不对cd到项目根目录再运行中文显示为乱码文件或终端编码不是UTF-8文件存为UTF-8终端切UTF-8输出顺序每次不同map遍历顺序随机取出key排序后再遍历cannot find modulego.mod未初始化执行go mod init编译报错undefined函数名拼写错误或未导入包检查拼写和import5.5 几个提升代码质量的小技巧第一个技巧是用gofmt格式化代码。Go自带格式化工具执行gofmt -w .会自动把代码格式化成官方风格。不用纠结缩进用tab还是空格gofmt帮你决定。第二个技巧是用go vet做静态检查。它能发现一些编译器不报但确实有问题的代码比如fmt.Printf的参数个数不匹配。执行go vet ./...就行。第三个技巧是给函数写注释。Go的注释规范是以函数名开头比如// readItemsFromFile 从指定路径读取商品列表。这样go doc命令能生成漂亮的文档。第四个技巧是错误处理不要忽略。新手经常写result, _ : someFunc()把错误丢给下划线。Demo里可能没事但正式项目里忽略错误是事故的根源。每个error都要处理哪怕只是打印出来。6. 从Demo到实用工具的扩展方向6.1 支持从命令行参数指定文件路径现在的程序把文件路径写死在代码里不够灵活。可以用flag包支持命令行参数func main() { dataPath : flag.String(data, data/items.txt, 数据文件路径) flag.Parse() items, err : readItemsFromFile(*dataPath) // ... }这样运行时可以go run main.go -data /path/to/other.txt指定不同的数据文件。flag包是标准库自带的不需要额外依赖。6.2 把分类规则外置为配置文件硬编码的映射表改起来要重新编译不方便。可以把规则放到一个JSON文件里{ 苹果: 水果, 香蕉: 水果, 菠菜: 蔬菜 }然后用encoding/json读取func loadRules(path string) (map[string]string, error) { data, err : os.ReadFile(path) if err ! nil { return nil, err } var rules map[string]string if err : json.Unmarshal(data, rules); err ! nil { return nil, err } return rules, nil }这样修改分类规则就不用动代码了非技术人员也能维护。6.3 输出为JSON格式供其他程序调用如果这个分类结果要给别的程序用输出JSON比纯文本更合适type CategoryResult struct { Category string json:category Count int json:count Items []string json:items } func toJSON(result map[string][]string) ([]byte, error) { var output []CategoryResult for category, items : range result { output append(output, CategoryResult{ Category: category, Count: len(items), Items: items, }) } return json.MarshalIndent(output, , ) }json.MarshalIndent会生成带缩进的JSON方便人阅读。如果追求紧凑可以用json.Marshal。6.4 用goroutine加速大批量数据处理当数据量很大的时候单线程处理可能比较慢。可以用goroutine并发处理func groupItemsConcurrent(items []string, rules map[string]string) map[string][]string { result : make(map[string][]string) var mu sync.Mutex var wg sync.WaitGroup for _, item : range items { wg.Add(1) go func(it string) { defer wg.Done() category : classifyItem(it, rules) mu.Lock() result[category] append(result[category], it) mu.Unlock() }(item) } wg.Wait() return result }这里用sync.Mutex保护map的并发写入因为Go的map不是并发安全的。sync.WaitGroup等待所有goroutine完成。这个版本在数据量小的时候反而比单线程慢因为goroutine的创建和锁的开销超过了并行带来的收益。但对于几十万条数据并发版本的优势就体现出来了。提示并发编程是Go的强项但也是坑最多的地方。新手建议先把单线程版本写熟再尝试并发。并发版本一定要用go test -race做竞态检测确保没有数据竞争。6.5 后续可以继续深挖的方向这个Demo虽然小但延伸出去可以覆盖很多Go的核心知识点。比如你可以把它改造成一个HTTP服务接收POST请求里的商品列表返回分类结果这就涉及net/http和JSON序列化。你可以把它接上数据库从MySQL或SQLite读取商品数据这就涉及database/sql和驱动使用。你可以给它加单元测试用testing包写测试用例这就涉及Go的测试规范。你还可以用embed包把数据文件嵌入二进制让程序变成一个独立的可执行文件。我个人在实际操作中的体会是学Go最快的路径不是看书而是找一个像这样的小需求用Go把它实现出来然后不断加需求、不断重构。每加一个需求你就会被迫去学一个新的知识点而且是有场景、有目的的学习记忆特别深刻。这个分类Demo我前后改过七八个版本从最初的硬编码到后来的HTTP服务每次改造都让我对Go的理解更深一层。

相关新闻

信息不是数据:从香农熵到工程实践的七层认知

信息不是数据:从香农熵到工程实践的七层认知

1. 信息不是“数据”的同义词:从一次课堂提问说起我带过几届某高校的计算机基础课,每次讲到“信息”这个概念,总有个学生会举手问:“老师,U盘里存的电影文件,是数据还是信息?”这个问题看似简单…

2026/10/9 21:30:18 阅读更多 →
C语言动态内存管理:malloc、calloc、realloc与free详解

C语言动态内存管理:malloc、calloc、realloc与free详解

1. 堆区内存分配的整体设计思路很多刚接触 C 语言的朋友,写程序时习惯用数组来存数据,比如int arr[100]。这在数据量确定、规模很小的场景下没问题,但一旦遇到“运行时才知道要存多少条记录”的需求,数组就抓瞎了——开小了不够用…

2026/10/9 21:30:18 阅读更多 →
AADL与OSATE2实战指南:从架构建模到可调度性分析

AADL与OSATE2实战指南:从架构建模到可调度性分析

1. 为什么我们需要认真聊聊 AADL 和 OSATE2如果你在嵌入式、航空航天、汽车电子或者任何高安全等级系统领域待过一段时间,大概率听过AADL这个名字。全称是 Architecture Analysis and Design Language,翻译过来叫架构分析与设计语言。名字听起来很学术&a…

2026/10/9 21:29:17 阅读更多 →

最新新闻

游戏测试实习面试全攻略:高频考点与答题框架

游戏测试实习面试全攻略:高频考点与答题框架

1. 拆解这场测试岗面试的真实考察逻辑1.1 为什么游戏测试实习的面试比想象中难很多人对游戏测试工程师这个岗位有误解,觉得就是“玩游戏找bug”,面试应该很水。我当年也是这么想的,结果第一次模拟面试就被问懵了。后来复盘才发现,…

2026/10/9 22:01:51 阅读更多 →
AST反混淆JS还原工具2.0:从原理到实践,把天书代码恢复成人话

AST反混淆JS还原工具2.0:从原理到实践,把天书代码恢复成人话

简介:面向JavaScript逆向与爬虫工程师的AST反混淆js还原工具2.0压缩包,深度适配目前最新的obfuscator.io混淆规则,可处理截至2022年4月20日的新式混淆代码。该版本基于丁仔大佬的还原工具二次开发,新增十余项功能,优化…

2026/10/9 22:01:51 阅读更多 →
UG建模思维训练:草图约束、特征依赖与参数化骨架实战

UG建模思维训练:草图约束、特征依赖与参数化骨架实战

简介:本资源是一套面向UG(Unigraphics NX)初学者与机械设计入门者的建模实战练习题集,聚焦零件级三维建模核心能力训练,适用于高职院校CAD/CAM课程实训、企业新员工岗前技能强化及自学提升者。PDF文档共1个文件&#x…

2026/10/9 22:01:51 阅读更多 →
AI客户服务平台性能测试实战:从指标设计到压测落地

AI客户服务平台性能测试实战:从指标设计到压测落地

1. 先从业务说起:为什么客户AI服务平台比普通系统更难测性能很多人一听到“性能测试”,第一反应就是压测工具、并发数、TPS这些技术名词。但当你真正面对一个智能客户AI服务平台时,如果还抱着传统Web系统的测试思路去搞,基本会踩到…

2026/10/9 22:01:51 阅读更多 →
用PMAT给Java老工程做依赖分析与架构建模实战指南

用PMAT给Java老工程做依赖分析与架构建模实战指南

简介:面向Java垃圾回收性能分析的IBM GA工具,由国际商业机器公司推出,服务Java开发者、性能优化工程师与系统管理员,用于解析Java虚拟机垃圾回收日志,识别长暂停、内存泄漏征兆与不合理的堆分配,为调整JVM参…

2026/10/9 22:01:51 阅读更多 →
FastAdmin+Shopro+uni-app分销商城二次开发:团队业绩与区域分红实践

FastAdmin+Shopro+uni-app分销商城二次开发:团队业绩与区域分红实践

FastAdmin Shopro uni-app分销商城功能深度定制与二次开发实践做商城二开这行,最常被问的一句话就是:“能不能帮我在分销商城上加个XXX功能?”这里说的分销商城,十有八九就是Shopro。FastAdmin Shopro uni-app这套组合&#xff…

2026/10/9 22:00:51 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →