【仓颉纪元】仓颉学习深度实践:30 天从零基础到独立开发

文章目录


前言

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 偏好? 命令行工具 优势 代码提示 可视化调试 项目管理 插件生态 优势 轻量快速 脚本自动化 远程开发

开发工具对比

特性 DevEco Studio 命令行工具 推荐人群
学习曲线 ⭐⭐⭐ ⭐⭐⭐⭐⭐ 初学者 vs 老手
代码提示 ✅ 完善 ❌ 无 需要提示的开发者
调试功能 ✅ 可视化 ⚠️ 命令行 需要调试的项目
启动速度 ⭐⭐⭐ ⭐⭐⭐⭐⭐ 快速验证代码
项目管理 ✅ 完善 ⚠️ 手动 大型项目
资源占用 2GB+ <100MB 配置较低的电脑
适合场景 完整项目开发 快速脚本验证 -

最初我纠结于两个选择:

  1. 使用 DevEco Studio(图形化 IDE,功能强大)
  2. 使用命令行工具(轻量级,适合老手)

作为一个习惯用 VSCode 的开发者,我最初想用命令行工具。但尝试后发现,DevEco Studio 的代码提示、调试功能、项目管理都很完善,特别适合初学者。最终我选择了 DevEco Studio,这个决定让我后续的学习效率提升了很多。

给初学者的建议:

  • 如果你是编程初学者,强烈推荐 DevEco Studio
  • 如果你有丰富的编程经验,可以尝试命令行工具
  • 两种方式可以并存,根据场景选择

1.1、开发工具选择与安装

方式一:使用 DevEco Studio(推荐初学者),DevEco Studio 是华为官方提供的集成开发环境,专为鸿蒙和仓颉开发设计。它提供了代码补全、语法高亮、调试工具、项目管理等功能,大大提升开发效率。

安装步骤:

  1. 访问华为开发者官网,下载 DevEco Studio
  2. 安装完成后,打开 IDE,在插件市场搜索"Cangjie"
  3. 安装仓颉插件,重启 IDE
  4. 创建新项目:File -> New -> Cangjie Project

我的安装经验:
我 下载阶段 下载阶段 我 访问官网 访问官网 我 下载安装包 下载安装包 我 等待下载完成 等待下载完成 安装阶段 安装阶段 我 运行安装程序 运行安装程序 我 选择安装路径 选择安装路径 我 等待安装 等待安装 配置阶段 配置阶段 我 首次启动 首次启动 我 下载SDK 下载SDK 我 安装插件 安装插件 我 配置完成 配置完成 DevEco Studio 安装历程

步骤 时间 注意事项
下载安装包 10-30 分钟 约 2GB,需要预留足够空间
安装程序 5-10 分钟 选择合适的安装路径
首次启动 2-3 分钟 会进行初始化配置
下载 SDK 10-15 分钟 建议配置国内镜像源
安装插件 3-5 分钟 搜索"Cangjie"插件
总计 30-60 分钟 一次性投入,后续高效
  • 下载大小约 2GB,需要预留足够空间
  • 首次启动会下载 SDK,需要等待 10-15 分钟
  • 建议配置国内镜像源,加快下载速度

方式二:使用命令行工具(适合有经验的开发者),命令行工具轻量级,启动快,适合快速验证代码。如果你习惯用 Vim、Emacs 或 VSCode,可以选择这种方式。

bash 复制代码
# 安装仓颉编译器(通过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 没意义,但我认为它很重要:

  1. 验证环境搭建是否成功
  2. 熟悉编译和运行流程
  3. 建立信心,迈出第一步

我的第一次尝试 ,创建 main.cj 文件:

cangjie 复制代码
// 这是注释
main() {
    println("你好,仓颉!")
    println("Hello, Cangjie!")
}

代码说明:

  • main():程序的入口函数,类似 Java 的 main 方法
  • println():打印函数,会自动换行
  • //:单行注释,编译器会忽略
  • 字符串用双引号包裹,支持中文

运行程序:

bash 复制代码
cjc run main.cj

输出:

复制代码
你好,仓颉!
Hello, Cangjie!

第一次运行的感受,当我看到输出时,虽然只是两行文字,但心里很有成就感。这说明环境搭建成功了,我可以开始真正的学习了。
cjc run 编译失败 修复错误 编译成功 成功! 编写代码 编译 检查错误 运行 查看输出 常见错误:

  • 语法错误
  • 拼写错误
  • 编码问题

常见问题:

  1. 如果提示"cjc: command not found",说明环境变量没配置好
  2. 如果提示编译错误,检查代码是否有拼写错误
  3. 如果中文显示乱码,检查文件编码是否为 UTF-8

二、基础语法(10 分钟)

学习方法建议(Day 2-5,11 月 5-8 日),基础语法是学习的重点,也是最容易放弃的阶段。很多人觉得语法枯燥,看了就忘。我的经验是:不要死记硬背,要通过实践来学习。

我的学习方法:
否 是 看语法点 写代码验证 理解了吗? 写更多例子 记录笔记 第二天复习 进入下一个

  1. 看一个语法点,立即写代码验证
  2. 不理解的地方,写多个例子尝试
  3. 每天学习 2-3 个语法点,不贪多
  4. 第二天复习前一天的内容

学习效果对比

学习方式 记忆效果 理解深度 时间投入 推荐度
只看不练 ⭐⭐ ⭐⭐ 1 小时/天 ❌
看完就练 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 2 小时/天 ✅
大量练习 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 3 小时/天 ✅✅

这个方法让我在 4 天内掌握了基础语法,而且记得很牢。

2.1、变量声明与作用域

变量是什么?(Day 2 上午),变量就是存储数据的容器。学习变量时,我最初不理解 let 和 var 的区别,写了很多代码才明白:

  • let:声明不可变变量,一旦赋值就不能改变
  • var:声明可变变量,可以多次赋值

为什么要区分可变和不可变? 这是我学习仓颉时的第一个困惑。后来我理解了:

  1. 不可变变量更安全,不会被意外修改
  2. 不可变变量便于编译器优化
  3. 函数式编程推荐使用不可变变量

在实际开发中,我发现 80% 的变量都可以用 let,只有需要修改的才用 var。这让代码更安全、更易维护。

cangjie 复制代码
main() {
    // 使用 let 声明不可变变量(推荐)
    let name: String = "张三"
    let age: Int64 = 25
    
    // 类型推断(编译器自动推断类型)
    let city = "北京"  // 自动推断为 String
    let score = 95     // 自动推断为 Int64
    
    // 使用 var 声明可变变量
    var count = 0
    count = 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 可变 任意文本 文本数据 用户名
cangjie 复制代码
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.14
    let float64: Float64 = 2.718281828  // 默认浮点类型
    
    // 布尔类型
    let isStudent: Bool = true
    let isGraduated: Bool = false
    
    // 字符和字符串
    let char: Rune = 'A'  // 单个字符(Unicode)
    let text: String = "仓颉语言"
    
    // 类型转换
    let num = 42
    let numFloat = Float64(num)  // 显式转换
    let numStr = num.toString()  // 转为字符串
    
    println("整数: ${int64}")
    println("浮点: ${float64}")
    println("布尔: ${isStudent}")
    println("字符串: ${text}")
}

2.3、运算符与表达式

cangjie 复制代码
main() {
    // 算术运算符
    let a = 10
    let b = 3
    println("加法: ${a + b}")      // 13
    println("减法: ${a - b}")      // 7
    println("乘法: ${a * b}")      // 30
    println("除法: ${a / b}")      // 3
    println("取模: ${a % b}")      // 1
    
    // 比较运算符
    println("相等: ${a == b}")     // false
    println("不等: ${a != b}")     // true
    println("大于: ${a > b}")      // true
    println("小于: ${a < b}")      // false
    println("大于等于: ${a >= b}") // true
    println("小于等于: ${a <= b}") // false
    
    // 逻辑运算符
    let x = true
    let y = false
    println("与: ${x && y}")       // false
    println("或: ${x || y}")       // true
    println("非: ${!x}")           // false
    
    // 位运算符
    let m = 5   // 二进制: 0101
    let n = 3   // 二进制: 0011
    println("按位与: ${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 条件语句

cangjie 复制代码
main() {
    let score = 85
    
    // 基本 if-else
    if (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 模式匹配

cangjie 复制代码
main() {
    let day = 3
    
    // 基本 match
    match (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、循环语句

cangjie 复制代码
main() {
    // for 循环(范围)
    println("=== for 循环 ===")
    for (i in 0..5) {  // 0 到 4
        println("i = ${i}")
    }
    
    // for 循环(包含结束值)
    for (i in 0..=5) {  // 0 到 5
        println("i = ${i}")
    }
    
    // for 循环(步长)
    for (i in 0..10 step 2) {  // 0, 2, 4, 6, 8
        println("i = ${i}")
    }
    
    // 遍历数组
    let fruits = ["苹果", "香蕉", "橙子"]
    for (fruit in fruits) {
        println("水果: ${fruit}")
    }
    
    // while 循环
    println("=== while 循环 ===")
    var count = 0
    while (count < 3) {
        println("count = ${count}")
        count += 1
    }
    
    // do-while 循环
    println("=== do-while 循环 ===")
    var num = 0
    do {
        println("num = ${num}")
        num += 1
    } while (num < 3)
    
    // break 和 continue
    println("=== 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 函数
函数方式 重复代码方式 定义 printSum 函数 调用 printSum10,20 调用 printSum30,40 调用 printSum50,60 打印结果 计算 10+20 打印结果 计算 30+40 打印结果 计算 50+60

Day 6 上午,我写了一个计算器程序,代码重复严重:

cangjie 复制代码
// 重复的代码
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 下午,我学会了函数,代码简洁多了:

cangjie 复制代码
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、函数定义与参数

函数的组成部分,一个完整的函数包含:

  1. 函数名:描述函数的功能
  2. 参数列表:函数需要的输入
  3. 返回类型:函数的输出类型
  4. 函数体:具体的实现逻辑

命名建议:

规范 好的示例 不好的示例 说明
动词开头 getUserName() userName() 表明这是一个动作
有意义 calculateTotalScore() calc() 清晰表达功能
驼峰命名 printStudentInfo() print_student_info() 遵循语言规范
避免缩写 getMaximumValue() getMaxVal() 完整单词更清晰
  • 使用动词开头:get、set、calculate、print
  • 名称要有意义:getUserName 而不是 getName
  • 遵循驼峰命名:calculateTotalScore
cangjie 复制代码
// 基本函数
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 = 1
    for (_ in 0..exponent) {
        result *= base
    }
    return result
}

// 可变参数
func sum(numbers: Int64...): Int64 {
    var total: Int64 = 0
    for (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 表达式与闭包

cangjie 复制代码
main() {
    // Lambda 表达式
    let square = { x: Int64 => x * x }
    println("5的平方: ${square(5)}")
    
    // 多参数 Lambda
    let 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、数组基础操作

cangjie 复制代码
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] = 15
    println("修改后: ${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 动态数组

cangjie 复制代码
main() {
    // 创建 ArrayList
    var list = ArrayList<Int64>()
    
    // 添加元素
    list.append(10)
    list.append(20)
    list.append(30)
    
    println("列表: ${list}")
    println("大小: ${list.size}")
    
    // 插入元素
    list.insert(1, 15)  // 在索引1处插入15
    println("插入后: ${list}")
    
    // 删除元素
    list.remove(2)
    println("删除后: ${list}")
    
    // 遍历
    for (item in list) {
        println("元素: ${item}")
    }
}

4.3、HashMap 键值对存储

cangjie 复制代码
main() {
    // 创建 HashMap
    var scores = HashMap<String, Int64>()
    
    // 添加键值对
    scores["张三"] = 95
    scores["李四"] = 87
    scores["王五"] = 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 面向对象学习历程

Day 11 上午,我写了一个学生管理程序,用函数实现:

cangjie 复制代码
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 下午,我学会了类,代码清晰多了:

cangjie 复制代码
class Student {
    var name: String
    var age: Int64
    var score: Int64
    
    func print() {
        println("${name}, ${age}岁, ${score}分")
    }
}

let students = [
    Student("张三", 20, 95),
    Student("李四", 21, 87),
    Student("王五", 19, 92)
]

这个对比让我理解了面向对象的价值:把相关的数据和操作封装在一起,代码更清晰、更易维护。

什么时候用类?

  • 数据和操作相关时(如学生的信息和操作)
  • 需要创建多个相似对象时(如多个学生)
  • 需要继承和多态时(如动物类的不同子类)

5.1、类与对象基础

类和对象的关系,类是模板,对象是实例。就像:

  • 类是"学生"这个概念
  • 对象是具体的"张三"、"李四"

类的组成:

  1. 属性(数据):描述对象的特征
  2. 方法(操作):描述对象的行为
  3. 构造函数:创建对象时的初始化逻辑
cangjie 复制代码
// 定义类
class Person {
    // 属性
    var name: String
    var age: Int64
    
    // 构造函数
    init(name: String, age: Int64) {
        this.name = name
        this.age = age
    }
    
    // 方法
    func introduce(): Unit {
        println("我叫${name},今年${age}岁")
    }
    
    func haveBirthday(): Unit {
        age += 1
        println("${name}过生日了,现在${age}岁")
    }
}

main() {
    // 创建对象
    let person1 = Person("张三", 25)
    let person2 = Person("李四", 30)
    
    // 调用方法
    person1.introduce()
    person2.introduce()
    
    person1.haveBirthday()
}

5.2、继承与多态

cangjie 复制代码
// 基类
class Animal {
    var name: String
    
    init(name: String) {
        this.name = name
    }
    
    func makeSound(): Unit {
        println("${name}发出声音")
    }
}

// 派生类
class Dog <: Animal {
    var breed: String
    
    init(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、接口与抽象

cangjie 复制代码
// 定义接口
interface Drawable {
    func draw(): Unit
}

interface Movable {
    func move(x: Int64, y: Int64): Unit
}

// 实现接口
class Circle <: Drawable, Movable {
    var x: Int64
    var y: Int64
    var radius: Int64
    
    init(x: Int64, y: Int64, radius: Int64) {
        this.x = x
        this.y = y
        this.radius = radius
    }
    
    func draw(): Unit {
        println("在(${x}, ${y})绘制半径为${radius}的圆")
    }
    
    func move(newX: Int64, newY: Int64): Unit {
        x = newX
        y = newY
        println("圆移动到(${x}, ${y})")
    }
}

main() {
    let circle = Circle(10, 20, 5)
    circle.draw()
    circle.move(30, 40)
    circle.draw()
}

六、可空类型与错误处理(3 分钟)

6.1、可空类型与空安全

cangjie 复制代码
main() {
    // 可空类型声明
    let name: String? = getUserName()  // 可能为空
    
    // 安全调用
    if (let actualName = name) {
        println("用户名: ${actualName}")
    } else {
        println("用户名为空")
    }
    
    // 空值合并运算符
    let displayName = name ?? "访客"
    println("显示名称: ${displayName}")
    
    // 链式调用
    let length = name?.length ?? 0
    println("名称长度: ${length}")
}

func getUserName(): String? {
    // 模拟可能返回空值的函数
    let random = Math.random()
    if (random > 0.5) {
        return Some("张三")
    } else {
        return None
    }
}

6.2、Result 类型错误处理

cangjie 复制代码
// 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、异常捕获与处理

cangjie 复制代码
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 日) ,学完基础语法后,我开始做实战项目。这是学习过程中最重要的阶段,也是最有成就感的阶段。为什么要做实战项目?

  1. 巩固知识:理论和实践结合,记得更牢
  2. 发现问题:实战中会遇到各种问题,解决问题的过程就是学习
  3. 建立信心:完成一个项目,会有很大的成就感
  4. 积累经验:实战经验比理论知识更有价值

我的实战经历 :

Day 20-21:做了一个计算器程序,学会了函数和错误处理

Day 22-23:做了一个学生成绩管理系统,学会了类和数据结构

Day 24-25:做了一个待办事项列表,学会了枚举和状态管理

这三个项目让我对仓颉有了全面的理解,也让我有信心开发更复杂的应用。

项目选择建议:

  1. 从简单开始:计算器、猜数字游戏
  2. 逐步提升:学生管理、图书管理
  3. 挑战自己:天气应用、聊天程序

7.1、简易计算器实现

项目背景:计算器是一个经典的入门项目。它涉及函数、条件判断、错误处理等核心概念,非常适合练习。

功能需求:

  1. 支持加减乘除四则运算
  2. 处理除零错误
  3. 返回计算结果或错误信息

设计思路:

  1. 定义一个 calculator 函数,接受两个数和一个运算符
  2. 使用 match 模式匹配不同的运算符
  3. 使用可空类型处理错误情况

实现要点:

  • 使用 Float64 支持小数运算
  • 使用可空类型(Float64?)表示可能失败的结果
  • 除法要检查除数是否为零
cangjie 复制代码
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、学生成绩管理系统

cangjie 复制代码
class Student {
    var name: String
    var scores: HashMap<String, Int64>
    
    init(name: String) {
        this.name = name
        this.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 = 0
        for ((_, 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、待办事项管理应用

cangjie 复制代码
enum TaskStatus {
    | Pending
    | InProgress
    | Completed
    
    func toString(): String {
        match (this) {
            case Pending => "待办"
            case InProgress => "进行中"
            case Completed => "已完成"
        }
    }
}

class Task {
    var id: Int64
    var title: String
    var status: TaskStatus
    
    init(id: Int64, title: String) {
        this.id = id
        this.title = title
        this.status = TaskStatus.Pending
    }
    
    func start(): Unit {
        status = TaskStatus.InProgress
    }
    
    func complete(): Unit {
        status = TaskStatus.Completed
    }
}

class TodoList {
    var tasks: ArrayList<Task>
    var nextId: Int64
    
    init() {
        this.tasks = ArrayList()
        this.nextId = 1
    }
    
    func addTask(title: String): Unit {
        let task = Task(nextId, title)
        tasks.append(task)
        nextId += 1
        println("已添加任务: ${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、字符串操作技巧

cangjie 复制代码
main() {
    let text = "Hello, Cangjie!"
    
    // 字符串长度
    println("长度: ${text.length}")
    
    // 字符串拼接
    let greeting = "你好" + "," + "世界"
    println(greeting)
    
    // 字符串插值
    let name = "张三"
    let age = 25
    println("${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、数组高级操作

cangjie 复制代码
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、类型别名与泛型

cangjie 复制代码
// 定义类型别名
type UserId = Int64
type UserName = String
type Score = Int64

class User {
    var id: UserId
    var name: UserName
    var score: Score
    
    init(id: UserId, name: UserName, score: Score) {
        this.id = id
        this.name = name
        this.score = score
    }
}

main() {
    let user = User(1001, "张三", 95)
    println("用户 ${user.name} (ID: ${user.id}) 得分: ${user.score}")
}

九、学习路线建议

我的 30 天学习计划(Day 26-30,11 月 29-12 月 3 日)
2024-11-05 2024-11-07 2024-11-09 2024-11-11 2024-11-13 2024-11-15 2024-11-17 2024-11-19 2024-11-21 2024-11-23 2024-11-25 2024-11-27 2024-11-29 2024-12-01 2024-12-03 环境搭建 基础语法 函数与数据结构 面向对象 进阶特性 深入学习 标准库学习 并发编程 小型项目 中型项目 完整项目 基础阶段 进阶阶段 实战阶段 30天学习路线图

在帮助 15 位朋友学习仓颉的过程中,我总结出了一套高效的学习路线。这个路线经过实践验证,80% 的人都能在 30 天内从零基础到独立开发。学习的三个阶段:
基础阶段
第1-10天 进阶阶段
第11-20天 实战阶段
第21-30天 掌握语法 写简单程序 理解原理 解决问题 独立开发 完成项目

三个阶段对比

阶段 时间 目标 学习重点 学习方法 成果
基础阶段 第 1-10 天 掌握语法 变量、函数、类 多练习,不求甚解 能写简单程序
进阶阶段 第 11-20 天 理解原理 内存、并发、优化 多思考,理解原理 能解决问题
实战阶段 第 21-30 天 独立开发 项目实战 多实践,解决问题 能做完整项目

33% 33% 34% 30天学习时间分配 基础语法 进阶学习 实战项目

每个阶段的重点不同:

  • 基础阶段:多练习,不求甚解
  • 进阶阶段:多思考,理解原理
  • 实战阶段:多实践,解决问题

9.1、初学者学习路线

第 1-3 天:基础语法(Day 1-3),这三天是最关键的,也是最容易放弃的。很多人觉得语法枯燥,看了就忘。我的建议是:
否 是 否 是 学习语法点 立即写代码 理解了? 写更多例子 记录笔记 第二天复习 记住了? 下一个语法点

学习建议

建议 说明 效果
每天 2-3 个语法点 不贪多,求精通 ⭐⭐⭐⭐⭐
立即写代码验证 实践出真知 ⭐⭐⭐⭐⭐
多写例子 加深理解 ⭐⭐⭐⭐
第二天复习 巩固记忆 ⭐⭐⭐⭐⭐
  1. 每天学习 2-3 个语法点,不贪多
  2. 每个语法点都要写代码验证
  3. 不理解的地方,多写几个例子
  4. 第二天复习前一天的内容

学习内容:

  • 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
计算器 Day 13
学生管理 Day 14
待办列表 函数练习 错误处理 用户输入 类的使用 数据结构 CRUD操作 枚举类型 状态管理 列表操作

实战是检验学习成果的最好方式。通过做项目,可以发现自己的不足,也可以巩固知识。

项目建议:

天数 项目 难度 练习重点 预计时间
Day 11-12 计算器程序 ⭐⭐⭐ 函数、错误处理 4-6 小时
Day 13 学生成绩管理 ⭐⭐⭐⭐ 类、数据结构 3-4 小时
Day 14 待办事项列表 ⭐⭐⭐⭐ 枚举、状态管理 3-4 小时

实战开发流程
是 否 需求分析 设计方案 编写代码 测试验证 有问题? 调试修复 代码优化 完成项目

实战技巧:

  • 先设计后编码:想清楚要做什么,再开始写代码
  • 遇到问题先思考:不要一遇到问题就查答案
  • 完成后优化:先实现功能,再优化代码

9.2、进阶开发路线

第 15-20 天:深入学习(Day 15-20)
深入学习阶段 标准库源码 内存管理 并发编程 性能优化 ArrayList实现 HashMap实现 String实现 所有权机制 借用规则 生命周期 协程基础 async/await 并发安全 性能分析 内存优化 算法优化

这个阶段要深入理解仓颉的核心特性,不仅要知道怎么用,还要知道为什么这样设计。

学习内容与时间分配

天数 主题 学习内容 学习方法 时间投入
Day 15-16 标准库源码 ArrayList、HashMap 阅读源码、写笔记 4 小时/天
Day 17-18 内存管理 所有权、借用 实验验证、画图理解 3 小时/天
Day 19 并发编程 协程、async/await 写并发程序 4 小时
Day 20 性能优化 分析工具、优化技巧 性能测试 4 小时

学习方法:

  • 阅读官方文档,理解设计理念
  • 阅读标准库源码,学习最佳实践
  • 做性能测试,理解优化原理

第 21-30 天:实战项目(Day 21-30)
2024-11-24 2024-11-25 2024-11-26 2024-11-27 2024-11-28 2024-11-29 2024-11-30 2024-12-01 2024-12-02 2024-12-03 2024-12-04 2024-12-05 确定项目方向 需求分析 架构设计 核心功能开发 UI开发 测试优化 文档编写 项目选择 天气应用 总结 实战项目时间规划

这个阶段要做一个完整的项目,从需求分析到架构设计,从编码实现到测试部署。

项目建议:
项目选择 天气应用 笔记应用 聊天应用 网络请求 UI开发 数据缓存 数据持久化 分布式同步 富文本编辑 并发编程 实时通信 消息队列

项目 难度 技术栈 开发时间 适合人群
天气应用 ⭐⭐⭐ 网络、UI、缓存 7-9 天 初学者
笔记应用 ⭐⭐⭐⭐ 持久化、同步 8-10 天 有基础
聊天应用 ⭐⭐⭐⭐⭐ 并发、实时通信 9-10 天 进阶者

项目要求:

  • 功能完整:核心功能都要实现
  • 代码规范:遵循最佳实践
  • 有测试:至少有单元测试
  • 有文档:README 要写清楚

9.3、学习资源推荐

官方资源(必看):

资源 类型 重要性 更新频率 链接
仓颉语言官方文档 文档 ⭐⭐⭐⭐⭐ 每周 最权威的学习资料
HarmonyOS 开发者社区 社区 ⭐⭐⭐⭐⭐ 每天 有很多实战案例
DevEco Studio 教程 教程 ⭐⭐⭐⭐ 每月 学习 IDE 的使用

实践项目(必做):
实践项目 初级 中级 高级 命令行工具
基础语法 计算器
函数使用 数据处理
数据结构 GUI应用
UI开发 鸿蒙应用
分布式能力 完整项目
综合实战

  • 命令行工具:练习基础语法
  • 数据处理程序:练习数据结构和算法
  • GUI 应用:练习 UI 开发
  • 鸿蒙原生应用:练习分布式能力

社区资源(推荐):

  • GitHub 开源项目:学习优秀代码
  • 技术博客和文章:了解最新动态
  • 开发者论坛:交流学习心得

我的学习建议:
学习建议 文档 项目 社区 实践 反复阅读
每次新收获 多读代码
学习风格 积极参与
加深理解 理论结合
实践为主

  1. 官方文档要反复看,每次都有新收获
  2. 开源项目要多读,学习别人的代码风格
  3. 社区要多参与,交流能加深理解
  4. 实践最重要,理论要结合实践

十、快速参考卡片

说明:以下是仓颉语言的快速参考,方便查阅常用语法和 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、学习资源


文章作者 :白鹿第一帅,作者主页 :https://blog.csdn.net/qq_22695001,未经授权,严禁转载,侵权必究!


总结

恭喜完成仓颉语言 30 天学习之旅!我们系统学习了变量声明、数据类型、控制流、函数、数据结构、面向对象、可空类型等核心语法,通过 30 多个代码示例和 3 个实用案例建立了对仓颉的整体认知。从我帮助 15 位朋友学习的经验看,掌握这些基础后就可以编写简单程序了,其中 80% 的人在学完后一周内就开发出了自己的第一个应用。学习建议:第一周巩固基础语法每天练习 2 小时,第二周学习类型系统和泛型深入理解编译期检查,第三周深入内存管理和并发编程掌握高性能开发技巧,第四周开始实战项目将所学知识融会贯通。学习编程最重要的是动手实践而不是死记硬背,建议完成文中所有示例代码并尝试扩展功能,遇到问题多查文档多思考。仓颉简洁而强大,特别适合鸿蒙原生应用开发,持续学习和实践你很快就能成为仓颉开发高手。


我是白鹿,一个不懈奋斗的程序猿。望本文能对你有所裨益,欢迎大家的一键三连!若有其他问题、建议或者补充可以留言在文章下方,感谢大家的支持!

相关推荐
金銀銅鐵4 天前
[Java] 通过行为参数化传递代码
后端·函数式编程
ProcessOn官方账号9 天前
java函数式编程--入门基础
java·编程·函数式编程
2601_9623004718 天前
python是跨平台的吗
python·开源·跨平台·面向对象·库
2601_9623824322 天前
Python 代码不可不知的函数式编程技术
python·函数式编程·高阶函数·嵌套函数·头等函数
TinyMemory22 天前
Java 面向对象核心入门(九):方法重写 Override|彻底分清重载与重写
java·面向对象·override·方法重写·重载与重写
TinyMemory24 天前
Java 面向对象核心入门(七):方法重载 Overload,同一个方法名能干不一样的活
java·面向对象·overload·方法重载
一晌小贪欢25 天前
python-第29天:Python面向对象之多态与抽象类
开发语言·python·数据可视化·面向对象·python办公·python多态
一晌小贪欢1 个月前
python-第27天:Python面向对象详解
开发语言·python·数据可视化·面向对象·python办公
TinyMemory1 个月前
Java 面向对象核心入门(六):this 关键字完全解析
java·笔记·面向对象·java新手·this关键字
杨充1 个月前
02.面向对象特性思考
面向对象