这段笔记讲解了Go语言中的几个核心概念,包括指针传递point和值传递、结构体struct及其方法struct-method、错误处理error、字符串操作string等,展示了如何使用Go语言的特性来简化程序的设计与开发。重点涵盖了结构体与指针传递的区别、方法的定义与使用、错误处理的实践、以及字符串操作函数的使用场景,帮助理解如何在不同的场合使用这些功能来提高代码的效率与可读性。请根据目录快速检索和入门。
走进 Go 语言基础语法
01-point
package main
import "fmt"
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
核心知识点:
这段代码展示了Go语言中的值传递和指针传递。Go语言中,函数参数的传递有两种方式:值传递和指针传递。值传递会对参数进行复制,不影响原始值,而指针传递则直接操作原始数据。
基本语法:
func add2(n int) { n += 2 }: 函数add2使用值传递方式,传递的是参数的副本,对n的修改不会影响原始值。func add2ptr(n *int) { *n += 2 }: 函数add2ptr使用指针传递,*n表示对指针指向的内存地址进行修改,可以影响原始值。add2(n): 调用add2传递的是变量n的副本,因此原始值不会改变。add2ptr(&n): 调用add2ptr时,使用&n获取变量n的地址并传递给函数,从而使函数可以修改原始值。
应用场景:
- 值传递:适用于只需要传递参数值,不需要修改原始数据的情况。这样可以保护原始数据的完整性。
- 指针传递:适用于需要在函数内部修改原始数据的情况,例如在函数中更新变量状态或操作较大的数据结构时,传递指针可以提高效率。
- 指针传递也常用于函数需要返回多个结果,或者通过减少数据复制来提高性能的场景。
02-struct
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
核心知识点:
这段代码展示了Go语言中结构体(struct)的定义与使用,以及如何通过值和指针来操作结构体实例。结构体是将多个字段组合在一起的聚合数据类型,适用于描述具有多个属性的对象。
基本语法:
type user struct { name string; password string }: 定义一个名为user的结构体类型,包含两个字段:name和password。a := user{name: "wang", password: "1024"}: 使用字段名称进行结构体初始化,确保代码可读性和明确性。b := user{"wang", "1024"}: 简短的初始化方式,但没有字段名称,适用于顺序已知且简单的情况。c := user{name: "wang"}和c.password = "1024": 可以部分初始化结构体字段,剩余字段手动赋值。var d user: 声明结构体变量d,然后逐个赋值字段。checkPassword(u user, password string) bool: 通过值传递检查用户密码,传递的是user的副本。checkPassword2(u *user, password string) bool: 通过指针传递检查用户密码,直接操作原始结构体,适合需要修改或处理大数据时提高效率。
应用场景:
- 结构体:用于描述复杂的数据结构,是面向对象编程的一种方式,适用于描述具有多个属性的实体对象,例如用户信息、订单、车辆等。
- 值传递:用于对结构体的只读操作,传递副本不会改变原始数据,确保原始值安全。
- 指针传递:适用于需要修改结构体内容的情况,或者需要避免对结构体进行复制以节省内存,适合大对象或频繁操作的场景。
03-struct-method
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
核心知识点:
这段代码展示了Go语言中方法(method)的定义与使用,尤其是结构体类型方法的接收者类型(值接收者和指针接收者)。方法是绑定在特定类型上的函数,常用于实现与类型关联的操作。
基本语法:
func (u user) checkPassword(password string) bool: 值接收者方法,u user表示checkPassword是绑定在user结构体上的方法。使用值接收者时,方法对结构体做的任何更改不会影响原始实例。func (u *user) resetPassword(password string): 指针接收者方法,u *user表示resetPassword可以修改原始结构体的字段。使用指针接收者时,可以直接对结构体内容进行更改。a.resetPassword("2048"): 调用指针接收者方法resetPassword,修改了结构体a的password字段。a.checkPassword("2048"): 调用值接收者方法checkPassword,检查密码是否匹配。
应用场景:
- 值接收者方法:适用于只读操作,即不需要改变结构体内部状态的方法。例如检查或读取数据。
- 指针接收者方法:适用于需要修改结构体内部状态的方法,通过指针传递可以直接对原始对象进行更改,常用于更新数据的操作。
- 面向对象特性:方法是面向对象编程中的重要特性,通过方法可以封装类型的操作,提高代码可读性和复用性,适合处理复杂业务逻辑的场景。
04-error
package main
import (
"errors"
"fmt"
)
// user 定义了一个用户结构体,包含用户名和密码字段
type user struct {
name string
password string
}
// findUser 在给定的用户切片中查找指定用户名的用户,如果找到则返回该用户的指针和 nil 错误,否则返回 nil 和一个自定义错误
func findUser(users []user, name string) (v *user, err error) {
// 遍历 users 切片,查找用户名匹配的用户
for _, u := range users {
// 如果找到匹配的用户名,返回该用户的指针和 nil 作为错误
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
// 查找名为 "wang" 的用户
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err!= nil {
// 如果发生错误,打印错误信息并返回
fmt.Println(err)
return
}
// 打印找到的用户的名字
fmt.Println(u.name) // wang
// 查找名为 "li" 的用户,如果未找到则打印错误信息
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err!= nil {
fmt.Println(err) // not found
return
} else {
// 如果找到,打印找到的用户的名字
fmt.Println(u.name)
}
}
核心知识点:
这段代码展示了Go语言中的错误处理方法以及结构体指针的使用。Go语言中的错误处理是通过显式返回 error 类型值来实现的,这种方式可以让程序员清楚地了解是否发生错误及其原因。
基本语法:
errors.New("not found"): 使用标准库errors创建一个新的错误,描述特定的错误情况。func findUser(users []user, name string) (v *user, err error): 定义了一个函数findUser,返回用户指针和error,通过多重返回值来表示查找结果和错误状态。for _, u := range users { ... }: 使用range遍历用户切片,查找符合条件的用户。return &u, nil: 如果找到用户,返回其指针和nil作为无错误的标识。return nil, errors.New("not found"): 未找到用户时,返回nil和自定义错误。if u, err := findUser(...); err != nil { ... }: 使用条件语句检查错误,通过err != nil来判断是否发生错误。
应用场景:
- 指针返回:当需要返回修改后的结构体或需要避免数据复制时,返回结构体的指针,可以提高效率和代码灵活性。
- 错误处理:Go语言采用显式错误处理,通过返回
error类型值让调用者处理错误,适用于需要详细控制错误处理逻辑的情况,例如数据查找失败、文件读写异常等。 - 结构体切片查找:函数
findUser提供了在结构体切片中查找特定元素的模式,适用于实现类似数据库查找的功能。
05-string
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
}
核心知识点:
这段代码展示了Go语言标准库 strings 包中常用字符串操作函数的用法。strings 包提供了多种字符串处理函数,如查找、替换、分割和大小写转换等,这些函数可以简化对字符串的常见操作。
基本语法:
strings.Contains(a, "ll"): 检查子串是否存在于字符串中,返回布尔值。strings.Count(a, "l"): 统计子串在字符串中出现的次数。strings.HasPrefix(a, "he"): 检查字符串是否以指定前缀开头。strings.HasSuffix(a, "llo"): 检查字符串是否以指定后缀结尾。strings.Index(a, "ll"): 返回子串首次出现的索引位置,找不到返回-1。strings.Join([]string{"he", "llo"}, "-"): 将字符串切片中的元素用指定分隔符连接。strings.Repeat(a, 2): 重复字符串指定次数。strings.Replace(a, "e", "E", -1): 替换子串,-1表示替换所有匹配的子串。strings.Split("a-b-c", "-"): 使用指定分隔符将字符串分割为切片。strings.ToLower(a),strings.ToUpper(a): 将字符串转换为小写或大写。len(a): 获取字符串字节长度。注意,Go 中的字符串是 UTF-8 编码,中文字符占用多个字节,因此len(b)会返回字节长度而非字符数。
应用场景:
- 字符串搜索和匹配:例如
strings.Contains、strings.HasPrefix等适用于验证字符串内容的场景,常用于数据过滤和格式检查。 - 字符串处理:函数如
strings.Replace、strings.Split、strings.Join等用于修改和操作字符串,适合处理输入和生成输出字符串的场景。 - 大小写转换:用于标准化字符串内容,例如用户输入处理、标签或关键词统一化。
- 字符计数:
len获取字节长度,适合需要处理字符编码的情况,尤其在处理多字节字符(如中文)时需要注意字符与字节的区别。