【仓颉纪元】仓颉学习深度实践:30 天从零基础到独立开发
文章目录
- 前言
- 一、环境搭建(5 分钟)
- 1.1、开发工具选择与安装
- 1.2、Hello World 程序实战
- 二、基础语法(10 分钟)
- 2.1、变量声明与作用域
- 2.2、基本数据类型详解
- 2.3、运算符与表达式
- 2.4、控制流语句
- 2.4.1、if-else 条件语句
- 2.4.2、match 模式匹配
- 2.4.3、循环语句
- 三、函数(5 分钟)
- 3.1、函数定义与参数
- 3.2、Lambda 表达式与闭包
- 四、数据结构(5 分钟)
- 4.1、数组基础操作
- 4.2、ArrayList 动态数组
- 4.3、HashMap 键值对存储
- 五、面向对象(5 分钟)
- 5.1、类与对象基础
- 5.2、继承与多态
- 5.3、接口与抽象
- 六、可空类型与错误处理(3 分钟)
- 6.1、可空类型与空安全
- 6.2、Result 类型错误处理
- 6.3、异常捕获与处理
- 七、实用示例(2 分钟)
- 7.1、简易计算器实现
- 7.2、学生成绩管理系统
- 7.3、待办事项管理应用
- 八、常见问题与技巧
- 8.1、字符串操作技巧
- 8.2、数组高级操作
- 8.3、类型别名与泛型
- 九、学习路线建议
- 9.1、初学者学习路线
- 9.2、进阶开发路线
- 9.3、学习资源推荐
- 十、快速参考卡片
- 10.1、语法快速参考
- 10.2、常用 API 参考
- 十一、关于作者与学习资源
- 11.1、作者简介
- 11.2、学习资源
- 总结
前言
2024 年 10 月底华为发布仓颉编程语言,作为大数据开发工程师和 CSDN 成都站主理人,我意识到这是抓住新语言红利期的机会。HDC 2024 大会上仓颉的性能和安全性演示让我印象深刻,社区成员对仓颉学习需求很大,每周都有人问我如何入门。11 月 4 日开始学习之旅,两个月里我不仅系统学习了仓颉,还帮助 15 位朋友成功入门,在这个过程中总结出一套高效的学习方法。本文目标是让开发者在 30 天内从零基础到能够独立开发简单应用,通过实际代码示例而非枯燥理论讲解,配合详细注释和常见问题解答,采用循序渐进的方式从基础语法到面向对象再到实战项目,帮助你快速建立对这门语言的整体认知并掌握核心开发技能,为深入学习和实战开发打下坚实基础。
声明:本文由作者“白鹿第一帅”于 CSDN 社区原创首发,未经作者本人授权,禁止转载!爬虫、复制至第三方平台属于严重违法行为,侵权必究。亲爱的读者,如果你在第三方平台看到本声明,说明本文内容已被窃取,内容可能残缺不全,强烈建议您移步“白鹿第一帅” CSDN 博客查看原文,并在 CSDN 平台私信联系作者对该第三方违规平台举报反馈,感谢您对于原创和知识产权保护做出的贡献!
文章作者:白鹿第一帅,作者主页:https://blog.csdn.net/qq_22695001,未经授权,严禁转载,侵权必究!
一、环境搭建(5 分钟)
我的环境搭建经历(Day 1,11 月 4 日),学习任何编程语言的第一步都是搭建开发环境。我在 11 月 4 日开始学习仓颉时,环境搭建花了我整整一个下午。不是因为难,而是因为选择太多,不知道该用哪种方式。选择困难症:IDE 还是命令行?
开发工具对比
| 特性 | DevEco Studio | 命令行工具 | 推荐人群 |
|---|---|---|---|
| 学习曲线 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 初学者 vs 老手 |
| 代码提示 | ✅ 完善 | ❌ 无 | 需要提示的开发者 |
| 调试功能 | ✅ 可视化 | ⚠️ 命令行 | 需要调试的项目 |
| 启动速度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 快速验证代码 |
| 项目管理 | ✅ 完善 | ⚠️ 手动 | 大型项目 |
| 资源占用 | 2GB+ | <100MB | 配置较低的电脑 |
| 适合场景 | 完整项目开发 | 快速脚本验证 | - |
最初我纠结于两个选择:
- 使用 DevEco Studio(图形化 IDE,功能强大)
- 使用命令行工具(轻量级,适合老手)
作为一个习惯用 VSCode 的开发者,我最初想用命令行工具。但尝试后发现,DevEco Studio 的代码提示、调试功能、项目管理都很完善,特别适合初学者。最终我选择了 DevEco Studio,这个决定让我后续的学习效率提升了很多。
给初学者的建议:
- 如果你是编程初学者,强烈推荐 DevEco Studio
- 如果你有丰富的编程经验,可以尝试命令行工具
- 两种方式可以并存,根据场景选择
1.1、开发工具选择与安装
方式一:使用 DevEco Studio(推荐初学者),DevEco Studio 是华为官方提供的集成开发环境,专为鸿蒙和仓颉开发设计。它提供了代码补全、语法高亮、调试工具、项目管理等功能,大大提升开发效率。
安装步骤:
- 访问华为开发者官网,下载 DevEco Studio
- 安装完成后,打开 IDE,在插件市场搜索“Cangjie”
- 安装仓颉插件,重启 IDE
- 创建新项目:File -> New -> Cangjie Project
我的安装经验:
| 步骤 | 时间 | 注意事项 |
|---|---|---|
| 下载安装包 | 10-30 分钟 | 约 2GB,需要预留足够空间 |
| 安装程序 | 5-10 分钟 | 选择合适的安装路径 |
| 首次启动 | 2-3 分钟 | 会进行初始化配置 |
| 下载 SDK | 10-15 分钟 | 建议配置国内镜像源 |
| 安装插件 | 3-5 分钟 | 搜索“Cangjie”插件 |
| 总计 | 30-60 分钟 | 一次性投入,后续高效 |
- 下载大小约 2GB,需要预留足够空间
- 首次启动会下载 SDK,需要等待 10-15 分钟
- 建议配置国内镜像源,加快下载速度
方式二:使用命令行工具(适合有经验的开发者),命令行工具轻量级,启动快,适合快速验证代码。如果你习惯用 Vim、Emacs 或 VSCode,可以选择这种方式。
# 安装仓颉编译器(通过DevEco Studio)
# 请访问华为开发者官网下载DevEco Studio
# https://developer.huawei.com/consumer/cn/deveco-studio/# 验证安装
cjc --version# 创建新项目
cjc new hello-cangjie
cd hello-cangjie
1.2、Hello World 程序实战
Hello World 的意义(Day 1 下午),写第一个程序时,我很激动。虽然只是简单的“Hello World”,但它标志着学习的开始。很多人觉得 Hello World 没意义,但我认为它很重要:
- 验证环境搭建是否成功
- 熟悉编译和运行流程
- 建立信心,迈出第一步
我的第一次尝试,创建 main.cj 文件:
// 这是注释
main() {println("你好,仓颉!")println("Hello, Cangjie!")
}
代码说明:
main():程序的入口函数,类似 Java 的 main 方法println():打印函数,会自动换行//:单行注释,编译器会忽略- 字符串用双引号包裹,支持中文
运行程序:
cjc run main.cj
输出:
你好,仓颉!
Hello, Cangjie!
第一次运行的感受,当我看到输出时,虽然只是两行文字,但心里很有成就感。这说明环境搭建成功了,我可以开始真正的学习了。
常见问题:
- 如果提示“cjc: command not found”,说明环境变量没配置好
- 如果提示编译错误,检查代码是否有拼写错误
- 如果中文显示乱码,检查文件编码是否为 UTF-8
二、基础语法(10 分钟)
学习方法建议(Day 2-5,11 月 5-8 日),基础语法是学习的重点,也是最容易放弃的阶段。很多人觉得语法枯燥,看了就忘。我的经验是:不要死记硬背,要通过实践来学习。
我的学习方法:
- 看一个语法点,立即写代码验证
- 不理解的地方,写多个例子尝试
- 每天学习 2-3 个语法点,不贪多
- 第二天复习前一天的内容
学习效果对比
| 学习方式 | 记忆效果 | 理解深度 | 时间投入 | 推荐度 |
|---|---|---|---|---|
| 只看不练 | ⭐⭐ | ⭐⭐ | 1 小时/天 | ❌ |
| 看完就练 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 2 小时/天 | ✅ |
| 大量练习 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 3 小时/天 | ✅✅ |
这个方法让我在 4 天内掌握了基础语法,而且记得很牢。
2.1、变量声明与作用域
变量是什么?(Day 2 上午),变量就是存储数据的容器。学习变量时,我最初不理解 let 和 var 的区别,写了很多代码才明白:
let:声明不可变变量,一旦赋值就不能改变var:声明可变变量,可以多次赋值
为什么要区分可变和不可变? 这是我学习仓颉时的第一个困惑。后来我理解了:
- 不可变变量更安全,不会被意外修改
- 不可变变量便于编译器优化
- 函数式编程推荐使用不可变变量
在实际开发中,我发现 80% 的变量都可以用 let,只有需要修改的才用 var。这让代码更安全、更易维护。
main() {// 使用 let 声明不可变变量(推荐)let name: String = "张三"let age: Int64 = 25// 类型推断(编译器自动推断类型)let city = "北京" // 自动推断为 Stringlet score = 95 // 自动推断为 Int64// 使用 var 声明可变变量var count = 0count = count + 1 // 可以修改println("姓名: ${name}, 年龄: ${age}")println("城市: ${city}, 分数: ${score}")println("计数: ${count}")
}
关键点:
let声明不可变变量(类似 Java 的 final)var声明可变变量- 支持类型推断,可以省略类型声明
- 使用
${变量}进行字符串插值
2.2、基本数据类型详解
数据类型的重要性(Day 2 下午),学习数据类型时,我最初觉得很无聊:为什么要区分 Int8、Int16、Int32、Int64?直接用一个 Int 不行吗?
后来我理解了:不同的数据类型占用不同的内存空间。Int8 只占 1 字节,Int64 占 8 字节。在处理大量数据时,选择合适的类型可以节省内存。
在我的大数据项目中,有一个字段存储年龄(0-120)。如果用 Int64,每个值占 8 字节;如果用 Int8,只占 1 字节。处理 1 亿条数据时,可以节省 700MB 内存!
类型选择建议:
| 数据类型 | 内存占用 | 数值范围 | 适用场景 | 示例 |
|---|---|---|---|---|
| Int8 | 1 字节 | -128~127 | 年龄、等级 | 年龄: 25 |
| Int16 | 2 字节 | -32768~32767 | 端口号、计数 | 端口: 8080 |
| Int32 | 4 字节 | -21 亿~21 亿 | ID、数量 | 用户 ID |
| Int64 | 8 字节 | 超大范围 | 默认整数 | 时间戳 |
| Float32 | 4 字节 | 7 位精度 | 一般小数 | 温度: 25.5 |
| Float64 | 8 字节 | 15 位精度 | 默认小数 | 科学计算 |
| Bool | 1 字节 | true/false | 开关状态 | 是否登录 |
| String | 可变 | 任意文本 | 文本数据 | 用户名 |
main() {// 整数类型let int8: Int8 = 127 // 8位整数let int16: Int16 = 32767 // 16位整数let int32: Int32 = 2147483647 // 32位整数let int64: Int64 = 9223372036854775807 // 64位整数(默认)// 浮点类型let float32: Float32 = 3.14let float64: Float64 = 2.718281828 // 默认浮点类型// 布尔类型let isStudent: Bool = truelet isGraduated: Bool = false// 字符和字符串let char: Rune = 'A' // 单个字符(Unicode)let text: String = "仓颉语言"// 类型转换let num = 42let numFloat = Float64(num) // 显式转换let numStr = num.toString() // 转为字符串println("整数: ${int64}")println("浮点: ${float64}")println("布尔: ${isStudent}")println("字符串: ${text}")
}
2.3、运算符与表达式
main() {// 算术运算符let a = 10let b = 3println("加法: ${a + b}") // 13println("减法: ${a - b}") // 7println("乘法: ${a * b}") // 30println("除法: ${a / b}") // 3println("取模: ${a % b}") // 1// 比较运算符println("相等: ${a == b}") // falseprintln("不等: ${a != b}") // trueprintln("大于: ${a > b}") // trueprintln("小于: ${a < b}") // falseprintln("大于等于: ${a >= b}") // trueprintln("小于等于: ${a <= b}") // false// 逻辑运算符let x = truelet y = falseprintln("与: ${x && y}") // falseprintln("或: ${x || y}") // trueprintln("非: ${!x}") // false// 位运算符let m = 5 // 二进制: 0101let n = 3 // 二进制: 0011println("按位与: ${m & n}") // 1 (0001)println("按位或: ${m | n}") // 7 (0111)println("按位异或: ${m ^ n}") // 6 (0110)println("左移: ${m << 1}") // 10 (1010)println("右移: ${m >> 1}") // 2 (0010)
}
2.4、控制流语句
控制流语句决定程序的执行顺序,是编程的核心概念。仓颉提供了丰富的控制流语句,包括条件判断、循环、跳转等。
控制流的重要性:
- 逻辑控制:根据条件执行不同的代码分支
- 重复执行:使用循环处理批量数据
- 程序结构:让程序逻辑更清晰
- 效率提升:避免重复编写相似代码
2.4.1、if-else 条件语句
main() {let score = 85// 基本 if-elseif (score >= 90) {println("优秀")} else if (score >= 80) {println("良好")} else if (score >= 60) {println("及格")} else {println("不及格")}// if 表达式(可以返回值)let grade = if (score >= 60) { "通过" } else { "未通过" }println("结果: ${grade}")// 简洁写法let status = if (score >= 60) "及格" else "不及格"println("状态: ${status}")
}
2.4.2、match 模式匹配
main() {let day = 3// 基本 matchmatch (day) {case 1 => println("星期一")case 2 => println("星期二")case 3 => println("星期三")case 4 => println("星期四")case 5 => println("星期五")case 6 | 7 => println("周末") // 多个值case _ => println("无效") // 默认分支}// match 表达式let dayName = match (day) {case 1 => "Monday"case 2 => "Tuesday"case 3 => "Wednesday"case 4 => "Thursday"case 5 => "Friday"case 6 | 7 => "Weekend"case _ => "Invalid"}println("Day: ${dayName}")
}
2.4.3、循环语句
main() {// for 循环(范围)println("=== for 循环 ===")for (i in 0..5) { // 0 到 4println("i = ${i}")}// for 循环(包含结束值)for (i in 0..=5) { // 0 到 5println("i = ${i}")}// for 循环(步长)for (i in 0..10 step 2) { // 0, 2, 4, 6, 8println("i = ${i}")}// 遍历数组let fruits = ["苹果", "香蕉", "橙子"]for (fruit in fruits) {println("水果: ${fruit}")}// while 循环println("=== while 循环 ===")var count = 0while (count < 3) {println("count = ${count}")count += 1}// do-while 循环println("=== do-while 循环 ===")var num = 0do {println("num = ${num}")num += 1} while (num < 3)// break 和 continueprintln("=== break 和 continue ===")for (i in 0..10) {if (i == 3) {continue // 跳过 3}if (i == 7) {break // 在 7 处停止}println("i = ${i}")}
}
三、函数(5 分钟)
函数的本质(Day 6-7,11月 9-10 日),学习函数时,我终于理解了编程的精髓:复用。函数就是把重复的代码封装起来,需要时调用,避免重复编写。
代码对比:重复 vs 函数
Day 6 上午,我写了一个计算器程序,代码重复严重:
// 重复的代码
let result1 = 10 + 20
println("10 + 20 = ${result1}")let result2 = 30 + 40
println("30 + 40 = ${result2}")let result3 = 50 + 60
println("50 + 60 = ${result3}")
Day 6 下午,我学会了函数,代码简洁多了:
func printSum(a: Int64, b: Int64) {println("${a} + ${b} = ${a + b}")
}printSum(10, 20)
printSum(30, 40)
printSum(50, 60)
这个对比让我深刻理解了函数的价值。函数的好处:
| 对比项 | 不使用函数 | 使用函数 | 改进 |
|---|---|---|---|
| 代码行数 | 15 行 | 7 行 | ⬇️ 53% |
| 重复代码 | 3 处 | 0 处 | ⬇️ 100% |
| 维护成本 | 修改 3 处 | 修改 1 处 | ⬇️ 67% |
| 可读性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⬆️ 150% |
| 可测试性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⬆️ 150% |
3.1、函数定义与参数
函数的组成部分,一个完整的函数包含:
- 函数名:描述函数的功能
- 参数列表:函数需要的输入
- 返回类型:函数的输出类型
- 函数体:具体的实现逻辑
命名建议:
| 规范 | 好的示例 | 不好的示例 | 说明 |
|---|---|---|---|
| 动词开头 | getUserName() | userName() | 表明这是一个动作 |
| 有意义 | calculateTotalScore() | calc() | 清晰表达功能 |
| 驼峰命名 | printStudentInfo() | print_student_info() | 遵循语言规范 |
| 避免缩写 | getMaximumValue() | getMaxVal() | 完整单词更清晰 |
- 使用动词开头:get、set、calculate、print
- 名称要有意义:getUserName 而不是 getName
- 遵循驼峰命名:calculateTotalScore
// 基本函数
func greet(name: String): Unit {println("你好, ${name}!")
}// 带返回值的函数
func add(a: Int64, b: Int64): Int64 {return a + b
}// 表达式函数(单行)
func multiply(a: Int64, b: Int64): Int64 = a * b// 默认参数
func power(base: Int64, exponent: Int64 = 2): Int64 {var result: Int64 = 1for (_ in 0..exponent) {result *= base}return result
}// 可变参数
func sum(numbers: Int64...): Int64 {var total: Int64 = 0for (num in numbers) {total += num}return total
}main() {greet("张三")let result1 = add(10, 20)println("10 + 20 = ${result1}")let result2 = multiply(5, 6)println("5 * 6 = ${result2}")println("2^2 = ${power(2)}") // 使用默认参数println("2^3 = ${power(2, 3)}") // 指定参数println("总和: ${sum(1, 2, 3, 4, 5)}")
}
3.2、Lambda 表达式与闭包
main() {// Lambda 表达式let square = { x: Int64 => x * x }println("5的平方: ${square(5)}")// 多参数 Lambdalet add = { x: Int64, y: Int64 => x + y }println("3 + 7 = ${add(3, 7)}")// 高阶函数func applyOperation(a: Int64, b: Int64, op: (Int64, Int64) -> Int64): Int64 {return op(a, b)}let result1 = applyOperation(10, 5, { x, y => x + y })let result2 = applyOperation(10, 5, { x, y => x - y })let result3 = applyOperation(10, 5, { x, y => x * y })println("10 + 5 = ${result1}")println("10 - 5 = ${result2}")println("10 * 5 = ${result3}")
}
四、数据结构(5 分钟)
4.1、数组基础操作
main() {// 创建数组let numbers = [1, 2, 3, 4, 5]let fruits = ["苹果", "香蕉", "橙子"]// 访问元素println("第一个数字: ${numbers[0]}")println("第二个水果: ${fruits[1]}")// 数组长度println("数组长度: ${numbers.size}")// 修改元素(需要 var)var mutableArray = [10, 20, 30]mutableArray[0] = 15println("修改后: ${mutableArray[0]}")// 数组方法let doubled = numbers.map({ x => x * 2 })println("翻倍: ${doubled}")let evens = numbers.filter({ x => x % 2 == 0 })println("偶数: ${evens}")let sum = numbers.reduce(0, { acc, x => acc + x })println("总和: ${sum}")
}
4.2、ArrayList 动态数组
main() {// 创建 ArrayListvar list = ArrayList<Int64>()// 添加元素list.append(10)list.append(20)list.append(30)println("列表: ${list}")println("大小: ${list.size}")// 插入元素list.insert(1, 15) // 在索引1处插入15println("插入后: ${list}")// 删除元素list.remove(2)println("删除后: ${list}")// 遍历for (item in list) {println("元素: ${item}")}
}
4.3、HashMap 键值对存储
main() {// 创建 HashMapvar scores = HashMap<String, Int64>()// 添加键值对scores["张三"] = 95scores["李四"] = 87scores["王五"] = 92// 获取值if (let score = scores["张三"]) {println("张三的分数: ${score}")}// 检查键是否存在if (scores.containsKey("李四")) {println("李四的成绩已录入")}// 遍历for ((name, score) in scores) {println("${name}: ${score}分")}// 删除scores.remove("王五")println("删除后大小: ${scores.size}")
}
五、面向对象(5 分钟)
面向对象的顿悟时刻(Day 11-13,11 月 14-16 日),学习面向对象时,我经历了从困惑到顿悟的过程。最初我不理解:为什么要用类?函数不够用吗?
我的理解过程:
Day 11 上午,我写了一个学生管理程序,用函数实现:
var studentNames = ["张三", "李四", "王五"]
var studentAges = [20, 21, 19]
var studentScores = [95, 87, 92]func printStudent(index: Int64) {println("${studentNames[index]}, ${studentAges[index]}岁, ${studentScores[index]}分")
}
这个代码的问题分析
| 问题 | 描述 | 影响 | 严重程度 |
|---|---|---|---|
| 数据分散 | 三个数组分别存储 | 难以管理,容易遗漏 | ⭐⭐⭐⭐ |
| 同步困难 | 三个数组要保持一致 | 容易出现索引错位 | ⭐⭐⭐⭐⭐ |
| 扩展困难 | 添加字段要改多处 | 维护成本高 | ⭐⭐⭐⭐ |
| 可读性差 | 数据关系不明确 | 理解困难 | ⭐⭐⭐ |
Day 11 下午,我学会了类,代码清晰多了:
class Student {var name: Stringvar age: Int64var score: Int64func print() {println("${name}, ${age}岁, ${score}分")}
}let students = [Student("张三", 20, 95),Student("李四", 21, 87),Student("王五", 19, 92)
]
这个对比让我理解了面向对象的价值:把相关的数据和操作封装在一起,代码更清晰、更易维护。
什么时候用类?
- 数据和操作相关时(如学生的信息和操作)
- 需要创建多个相似对象时(如多个学生)
- 需要继承和多态时(如动物类的不同子类)
5.1、类与对象基础
类和对象的关系,类是模板,对象是实例。就像:
- 类是“学生”这个概念
- 对象是具体的“张三”、“李四”
类的组成:
- 属性(数据):描述对象的特征
- 方法(操作):描述对象的行为
- 构造函数:创建对象时的初始化逻辑
// 定义类
class Person {// 属性var name: Stringvar age: Int64// 构造函数init(name: String, age: Int64) {this.name = namethis.age = age}// 方法func introduce(): Unit {println("我叫${name},今年${age}岁")}func haveBirthday(): Unit {age += 1println("${name}过生日了,现在${age}岁")}
}main() {// 创建对象let person1 = Person("张三", 25)let person2 = Person("李四", 30)// 调用方法person1.introduce()person2.introduce()person1.haveBirthday()
}
5.2、继承与多态
// 基类
class Animal {var name: Stringinit(name: String) {this.name = name}func makeSound(): Unit {println("${name}发出声音")}
}// 派生类
class Dog <: Animal {var breed: Stringinit(name: String, breed: String) {super.init(name)this.breed = breed}// 重写方法override func makeSound(): Unit {println("${name}(${breed})汪汪叫")}func fetch(): Unit {println("${name}去捡球")}
}class Cat <: Animal {init(name: String) {super.init(name)}override func makeSound(): Unit {println("${name}喵喵叫")}
}main() {let dog = Dog("旺财", "金毛")let cat = Cat("咪咪")dog.makeSound()dog.fetch()cat.makeSound()
}
5.3、接口与抽象
// 定义接口
interface Drawable {func draw(): Unit
}interface Movable {func move(x: Int64, y: Int64): Unit
}// 实现接口
class Circle <: Drawable, Movable {var x: Int64var y: Int64var radius: Int64init(x: Int64, y: Int64, radius: Int64) {this.x = xthis.y = ythis.radius = radius}func draw(): Unit {println("在(${x}, ${y})绘制半径为${radius}的圆")}func move(newX: Int64, newY: Int64): Unit {x = newXy = newYprintln("圆移动到(${x}, ${y})")}
}main() {let circle = Circle(10, 20, 5)circle.draw()circle.move(30, 40)circle.draw()
}
六、可空类型与错误处理(3 分钟)
6.1、可空类型与空安全
main() {// 可空类型声明let name: String? = getUserName() // 可能为空// 安全调用if (let actualName = name) {println("用户名: ${actualName}")} else {println("用户名为空")}// 空值合并运算符let displayName = name ?? "访客"println("显示名称: ${displayName}")// 链式调用let length = name?.length ?? 0println("名称长度: ${length}")
}func getUserName(): String? {// 模拟可能返回空值的函数let random = Math.random()if (random > 0.5) {return Some("张三")} else {return None}
}
6.2、Result 类型错误处理
// Result 类型用于错误处理
enum Result<T, E> {| Success(T)| Failure(E)
}func divide(a: Int64, b: Int64): Result<Int64, String> {if (b == 0) {return Result.Failure("除数不能为零")}return Result.Success(a / b)
}main() {let result1 = divide(10, 2)match (result1) {case Success(value) => println("结果: ${value}")case Failure(error) => println("错误: ${error}")}let result2 = divide(10, 0)match (result2) {case Success(value) => println("结果: ${value}")case Failure(error) => println("错误: ${error}")}
}
6.3、异常捕获与处理
func readFile(path: String): String {try {// 尝试读取文件let content = FileSystem.read(path)return content} catch (e: FileNotFoundException) {println("文件不存在: ${e.message}")return ""} catch (e: IOException) {println("IO错误: ${e.message}")return ""} finally {println("清理资源")}
}main() {let content = readFile("data.txt")println("文件内容: ${content}")
}
七、实用示例(2 分钟)
实战项目的重要性(Day 20-25,11 月 23-28 日),学完基础语法后,我开始做实战项目。这是学习过程中最重要的阶段,也是最有成就感的阶段。为什么要做实战项目?
- 巩固知识:理论和实践结合,记得更牢
- 发现问题:实战中会遇到各种问题,解决问题的过程就是学习
- 建立信心:完成一个项目,会有很大的成就感
- 积累经验:实战经验比理论知识更有价值
我的实战经历:
Day 20-21:做了一个计算器程序,学会了函数和错误处理
Day 22-23:做了一个学生成绩管理系统,学会了类和数据结构
Day 24-25:做了一个待办事项列表,学会了枚举和状态管理
这三个项目让我对仓颉有了全面的理解,也让我有信心开发更复杂的应用。
项目选择建议:
- 从简单开始:计算器、猜数字游戏
- 逐步提升:学生管理、图书管理
- 挑战自己:天气应用、聊天程序
7.1、简易计算器实现
项目背景:计算器是一个经典的入门项目。它涉及函数、条件判断、错误处理等核心概念,非常适合练习。
功能需求:
- 支持加减乘除四则运算
- 处理除零错误
- 返回计算结果或错误信息
设计思路:
- 定义一个 calculator 函数,接受两个数和一个运算符
- 使用 match 模式匹配不同的运算符
- 使用可空类型处理错误情况
实现要点:
- 使用 Float64 支持小数运算
- 使用可空类型(Float64?)表示可能失败的结果
- 除法要检查除数是否为零
func calculator(a: Float64, b: Float64, operator: String): Float64? {match (operator) {case "+" => Some(a + b)case "-" => Some(a - b)case "*" => Some(a * b)case "/" => {if (b != 0.0) {Some(a / b)} else {None}}case _ => None}
}main() {println("=== 简易计算器 ===")let operations = [(10.0, 5.0, "+"), (10.0, 5.0, "-"), (10.0, 5.0, "*"), (10.0, 5.0, "/"),(10.0, 0.0, "/")]for ((a, b, op) in operations) {if (let result = calculator(a, b, op)) {println("${a} ${op} ${b} = ${result}")} else {println("${a} ${op} ${b} = 错误")}}
}
7.2、学生成绩管理系统
class Student {var name: Stringvar scores: HashMap<String, Int64>init(name: String) {this.name = namethis.scores = HashMap()}func addScore(subject: String, score: Int64): Unit {scores[subject] = score}func getAverage(): Float64 {if (scores.size == 0) {return 0.0}var total: Int64 = 0for ((_, score) in scores) {total += score}return Float64(total) / Float64(scores.size)}func printReport(): Unit {println("=== ${name}的成绩单 ===")for ((subject, score) in scores) {println("${subject}: ${score}分")}println("平均分: ${getAverage()}")}
}main() {let student = Student("张三")student.addScore("语文", 95)student.addScore("数学", 88)student.addScore("英语", 92)student.addScore("物理", 85)student.printReport()
}
7.3、待办事项管理应用
enum TaskStatus {| Pending| InProgress| Completedfunc toString(): String {match (this) {case Pending => "待办"case InProgress => "进行中"case Completed => "已完成"}}
}class Task {var id: Int64var title: Stringvar status: TaskStatusinit(id: Int64, title: String) {this.id = idthis.title = titlethis.status = TaskStatus.Pending}func start(): Unit {status = TaskStatus.InProgress}func complete(): Unit {status = TaskStatus.Completed}
}class TodoList {var tasks: ArrayList<Task>var nextId: Int64init() {this.tasks = ArrayList()this.nextId = 1}func addTask(title: String): Unit {let task = Task(nextId, title)tasks.append(task)nextId += 1println("已添加任务: ${title}")}func startTask(id: Int64): Unit {for (task in tasks) {if (task.id == id) {task.start()println("任务 ${id} 开始执行")return}}println("未找到任务 ${id}")}func completeTask(id: Int64): Unit {for (task in tasks) {if (task.id == id) {task.complete()println("任务 ${id} 已完成")return}}println("未找到任务 ${id}")}func listTasks(): Unit {println("=== 待办事项列表 ===")for (task in tasks) {println("[${task.id}] ${task.title} - ${task.status.toString()}")}}
}main() {let todoList = TodoList()todoList.addTask("学习仓颉语言")todoList.addTask("开发鸿蒙应用")todoList.addTask("写技术文章")todoList.listTasks()println("\n开始执行任务...")todoList.startTask(1)todoList.completeTask(1)println()todoList.listTasks()
}
八、常见问题与技巧
8.1、字符串操作技巧
main() {let text = "Hello, Cangjie!"// 字符串长度println("长度: ${text.length}")// 字符串拼接let greeting = "你好" + "," + "世界"println(greeting)// 字符串插值let name = "张三"let age = 25println("${name}今年${age}岁")// 字符串方法println("大写: ${text.toUpperCase()}")println("小写: ${text.toLowerCase()}")println("包含: ${text.contains("Cangjie")}")println("开头: ${text.startsWith("Hello")}")println("结尾: ${text.endsWith("!")}")// 字符串分割let parts = text.split(", ")for (part in parts) {println("部分: ${part}")}// 字符串替换let replaced = text.replace("Cangjie", "World")println("替换后: ${replaced}")
}
8.2、数组高级操作
main() {let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]// map: 转换每个元素let squared = numbers.map({ x => x * x })println("平方: ${squared}")// filter: 过滤元素let evens = numbers.filter({ x => x % 2 == 0 })println("偶数: ${evens}")// reduce: 归约let sum = numbers.reduce(0, { acc, x => acc + x })println("总和: ${sum}")// any: 是否存在满足条件的元素let hasLarge = numbers.any({ x => x > 5 })println("有大于5的数: ${hasLarge}")// all: 是否所有元素都满足条件let allPositive = numbers.all({ x => x > 0 })println("都是正数: ${allPositive}")// find: 查找第一个满足条件的元素if (let found = numbers.find({ x => x > 5 })) {println("第一个大于5的数: ${found}")}// 链式调用let result = numbers.filter({ x => x % 2 == 0 }).map({ x => x * x }).reduce(0, { acc, x => acc + x })println("偶数平方和: ${result}")
}
8.3、类型别名与泛型
// 定义类型别名
type UserId = Int64
type UserName = String
type Score = Int64class User {var id: UserIdvar name: UserNamevar score: Scoreinit(id: UserId, name: UserName, score: Score) {this.id = idthis.name = namethis.score = score}
}main() {let user = User(1001, "张三", 95)println("用户 ${user.name} (ID: ${user.id}) 得分: ${user.score}")
}
九、学习路线建议
我的 30 天学习计划(Day 26-30,11 月 29-12 月 3 日)
在帮助 15 位朋友学习仓颉的过程中,我总结出了一套高效的学习路线。这个路线经过实践验证,80% 的人都能在 30 天内从零基础到独立开发。学习的三个阶段:
三个阶段对比
| 阶段 | 时间 | 目标 | 学习重点 | 学习方法 | 成果 |
|---|---|---|---|---|---|
| 基础阶段 | 第 1-10 天 | 掌握语法 | 变量、函数、类 | 多练习,不求甚解 | 能写简单程序 |
| 进阶阶段 | 第 11-20 天 | 理解原理 | 内存、并发、优化 | 多思考,理解原理 | 能解决问题 |
| 实战阶段 | 第 21-30 天 | 独立开发 | 项目实战 | 多实践,解决问题 | 能做完整项目 |
每个阶段的重点不同:
- 基础阶段:多练习,不求甚解
- 进阶阶段:多思考,理解原理
- 实战阶段:多实践,解决问题
9.1、初学者学习路线
第 1-3 天:基础语法(Day 1-3),这三天是最关键的,也是最容易放弃的。很多人觉得语法枯燥,看了就忘。我的建议是:
学习建议
| 建议 | 说明 | 效果 |
|---|---|---|
| 每天 2-3 个语法点 | 不贪多,求精通 | ⭐⭐⭐⭐⭐ |
| 立即写代码验证 | 实践出真知 | ⭐⭐⭐⭐⭐ |
| 多写例子 | 加深理解 | ⭐⭐⭐⭐ |
| 第二天复习 | 巩固记忆 | ⭐⭐⭐⭐⭐ |
- 每天学习 2-3 个语法点,不贪多
- 每个语法点都要写代码验证
- 不理解的地方,多写几个例子
- 第二天复习前一天的内容
学习内容:
- Day 1:变量、数据类型、运算符
- Day 2:控制流(if、match)
- Day 3:循环(for、while)
学习方法:
- 看一个语法点,立即写代码
- 不要死记硬背,要理解原理
- 遇到问题,先自己思考,再查文档
第 4-5 天:函数和数据结构(Day 4-5),函数是编程的核心,一定要掌握好。数据结构是存储数据的方式,也很重要。
学习内容:
- Day 4:函数定义、参数、返回值、Lambda 表达式
- Day 5:数组、ArrayList、HashMap
学习重点:
- 理解函数的作用:代码复用
- 掌握数据结构的选择:什么时候用数组,什么时候用 HashMap
- 练习高阶函数:map、filter、reduce
第 6-7 天:面向对象(Day 6-7),面向对象是编程思想的转变,从面向过程到面向对象。这个转变需要时间,不要着急。
学习内容:
- Day 6:类和对象、属性和方法
- Day 7:继承、多态、接口
学习难点:
- 理解类和对象的关系
- 理解继承的作用
- 理解多态的意义
第 8-10 天:进阶特性(Day 8-10),进阶特性是仓颉的亮点,也是区别于其他语言的地方。
学习内容:
- Day 8:可空类型、空安全
- Day 9:错误处理、Result 类型
- Day 10:泛型基础
学习建议:
- 可空类型是仓颉的特色,一定要掌握
- 错误处理要用 Result 类型,不要用异常
- 泛型可以先了解,不需要深入
第 11-14 天:实战练习(Day 11-14)
实战是检验学习成果的最好方式。通过做项目,可以发现自己的不足,也可以巩固知识。
项目建议:
| 天数 | 项目 | 难度 | 练习重点 | 预计时间 |
|---|---|---|---|---|
| Day 11-12 | 计算器程序 | ⭐⭐⭐ | 函数、错误处理 | 4-6 小时 |
| Day 13 | 学生成绩管理 | ⭐⭐⭐⭐ | 类、数据结构 | 3-4 小时 |
| Day 14 | 待办事项列表 | ⭐⭐⭐⭐ | 枚举、状态管理 | 3-4 小时 |
实战开发流程
实战技巧:
- 先设计后编码:想清楚要做什么,再开始写代码
- 遇到问题先思考:不要一遇到问题就查答案
- 完成后优化:先实现功能,再优化代码
9.2、进阶开发路线
第 15-20 天:深入学习(Day 15-20)
这个阶段要深入理解仓颉的核心特性,不仅要知道怎么用,还要知道为什么这样设计。
学习内容与时间分配
| 天数 | 主题 | 学习内容 | 学习方法 | 时间投入 |
|---|---|---|---|---|
| Day 15-16 | 标准库源码 | ArrayList、HashMap | 阅读源码、写笔记 | 4 小时/天 |
| Day 17-18 | 内存管理 | 所有权、借用 | 实验验证、画图理解 | 3 小时/天 |
| Day 19 | 并发编程 | 协程、async/await | 写并发程序 | 4 小时 |
| Day 20 | 性能优化 | 分析工具、优化技巧 | 性能测试 | 4 小时 |
学习方法:
- 阅读官方文档,理解设计理念
- 阅读标准库源码,学习最佳实践
- 做性能测试,理解优化原理
第 21-30 天:实战项目(Day 21-30)
这个阶段要做一个完整的项目,从需求分析到架构设计,从编码实现到测试部署。
项目建议:
| 项目 | 难度 | 技术栈 | 开发时间 | 适合人群 |
|---|---|---|---|---|
| 天气应用 | ⭐⭐⭐ | 网络、UI、缓存 | 7-9 天 | 初学者 |
| 笔记应用 | ⭐⭐⭐⭐ | 持久化、同步 | 8-10 天 | 有基础 |
| 聊天应用 | ⭐⭐⭐⭐⭐ | 并发、实时通信 | 9-10 天 | 进阶者 |
项目要求:
- 功能完整:核心功能都要实现
- 代码规范:遵循最佳实践
- 有测试:至少有单元测试
- 有文档:README 要写清楚
9.3、学习资源推荐
官方资源(必看):
| 资源 | 类型 | 重要性 | 更新频率 | 链接 |
|---|---|---|---|---|
| 仓颉语言官方文档 | 文档 | ⭐⭐⭐⭐⭐ | 每周 | 最权威的学习资料 |
| HarmonyOS 开发者社区 | 社区 | ⭐⭐⭐⭐⭐ | 每天 | 有很多实战案例 |
| DevEco Studio 教程 | 教程 | ⭐⭐⭐⭐ | 每月 | 学习 IDE 的使用 |
实践项目(必做):
- 命令行工具:练习基础语法
- 数据处理程序:练习数据结构和算法
- GUI 应用:练习 UI 开发
- 鸿蒙原生应用:练习分布式能力
社区资源(推荐):
- GitHub 开源项目:学习优秀代码
- 技术博客和文章:了解最新动态
- 开发者论坛:交流学习心得
我的学习建议:
- 官方文档要反复看,每次都有新收获
- 开源项目要多读,学习别人的代码风格
- 社区要多参与,交流能加深理解
- 实践最重要,理论要结合实践
十、快速参考卡片
说明:以下是仓颉语言的快速参考,方便查阅常用语法和 API。建议收藏本节,在实际开发中随时查看。
10.1、语法快速参考
基础语法:
- 变量:
let(不可变)、var(可变) - 函数:
func name(params): ReturnType { } - 类:
class Name { },接口:interface Name { } - 枚举:
enum Name { | Case1 | Case2 } - 可空类型:
Type?,安全解包:if (let v = value) { }
控制流:
- 条件:
if (condition) { } else { } - 模式匹配:
match (value) { case x => } - 循环:
for (i in 0..10) { }、while (condition) { }
数据结构:
- 数组:
[1, 2, 3] - 动态数组:
ArrayList<T>() - 哈希表:
HashMap<K, V>()
函数式编程:
- Lambda:
{ x: Int64 => x * x } - 高阶函数:
map、filter、reduce
10.2、常用 API 参考
字符串操作:
- 长度:
text.length - 大小写:
toUpperCase()、toLowerCase() - 查找:
contains("sub")、startsWith()、endsWith() - 分割:
split(",") - 替换:
replace("old", "new")
数组操作:
- 大小:
arr.size - 转换:
map({ x => x * 2 }) - 过滤:
filter({ x => x > 0 }) - 归约:
reduce(0, { acc, x => acc + x }) - 判断:
any({ x => x > 5 })、all({ x => x > 0 })
ArrayList 操作:
- 添加:
append(item)、insert(index, item) - 删除:
remove(index)、clear() - 访问:
[index]、size
HashMap 操作:
- 设置:
map[key] = value - 获取:
map.get(key) - 判断:
containsKey(key) - 删除:
remove(key)、clear()
提示:完整的 API 文档请参考官方文档,这里只列出最常用的方法。
十一、关于作者与学习资源
11.1、作者简介
郭靖,笔名“白鹿第一帅”,大数据与大模型开发工程师,中国开发者影响力年度榜单人物。在编程语言教学和技术写作方面有丰富经验,擅长将复杂的技术概念用简洁易懂的方式呈现,帮助初学者快速上手新技术,已帮助数百名开发者成功转型。作为技术内容创作者,自 2015 年至今累计发布技术博客 300 余篇,全网粉丝超 60000+,获得 CSDN“博客专家”等多个技术社区认证,并成为互联网顶级技术公会“极星会”成员。
同时作为资深社区组织者,运营多个西南地区技术社区,包括 CSDN 成都站(10000+ 成员)、AWS User Group Chengdu、字节跳动 Trae Friends@Chengdu等,累计组织线下技术活动超 50 场,致力于推动技术交流与开发者成长。
CSDN 博客地址:https://blog.csdn.net/qq_22695001
11.2、学习资源
- 仓颉语言官方教程
- DevEco Studio 下载
- 华为开发者学院
- Kotlin 语言参考(语法相似)
- Rust 程序设计语言(设计理念参考)
文章作者:白鹿第一帅,作者主页:https://blog.csdn.net/qq_22695001,未经授权,严禁转载,侵权必究!
总结
恭喜完成仓颉语言 30 天学习之旅!我们系统学习了变量声明、数据类型、控制流、函数、数据结构、面向对象、可空类型等核心语法,通过 30 多个代码示例和 3 个实用案例建立了对仓颉的整体认知。从我帮助 15 位朋友学习的经验看,掌握这些基础后就可以编写简单程序了,其中 80% 的人在学完后一周内就开发出了自己的第一个应用。学习建议:第一周巩固基础语法每天练习 2 小时,第二周学习类型系统和泛型深入理解编译期检查,第三周深入内存管理和并发编程掌握高性能开发技巧,第四周开始实战项目将所学知识融会贯通。学习编程最重要的是动手实践而不是死记硬背,建议完成文中所有示例代码并尝试扩展功能,遇到问题多查文档多思考。仓颉简洁而强大,特别适合鸿蒙原生应用开发,持续学习和实践你很快就能成为仓颉开发高手。
我是白鹿,一个不懈奋斗的程序猿。望本文能对你有所裨益,欢迎大家的一键三连!若有其他问题、建议或者补充可以留言在文章下方,感谢大家的支持!
