本人比较看重go的简洁性、跨平台不依赖运行时环境以及高并发(都是相对于Java),同时,我也比较侧重于后端开发,毕竟大部分理工男(懂的都懂)
我也算是开始自己的go语言学习了,但愿不要想java那样…
基础语法#
变量和常量数据类型#
//常量定义
const (
StatusOK = iota + 1 //iota是go语言的一个预定义标识符,表示常量生成器,在const关键字出现时被重置为0,并在每个const声明中自动递增。
StatusBadRequest //2
StatusUnauthorized //3
MB = 1024 * 1024 //1048576
)
//变量定义
func main() {
var str1 string = "Hello Go 1" //标准定义
var str2 = "Hello Go 2" //类型推断
str3 := "Hello Go 3" //短定义(通常用于函数中)
fmt.Println(str1, str2, str3) //output:Hello Go 1 Hello Go 2 Hello Go 3
str1, str2 = str2, str1 //交换str1与str2的值
fmt.Println(str1, str2, str3) // output:Hello Go 2 Hello Go 1 Hello Go 3
}
//空值
func testNonInitValue() {
var a int
var b string
var c float64
fmt.Println(a, b, c) //output: 0 “” 0
//上一行b输出的结果代表b的值是一个空字符串,而不是null或者undefined。
}
//至于类型转换,认真学过Java都知道
...略
//类型自定义
type ll int64 // 定义一个新的类型ll,它的底层类型是int64
//为ll类型实现String方法,使其满足fmt.Stringer接口
func (l ll) String() string {
return fmt.Sprintf("%d", l)
}
//结构体
type UserValue struct {
name string
age int
email string
}
//同理,结构体也可以实现String方法
func (u UserValue) String() string {
return fmt.Sprintf("name:%s, age:%d, email:%s)", u.name, u.age, u.email)
}
// slice
var arr [3]int //只定义,为使用 output: 0,0,0
b := [3]int{1, 2, 3} //临时定义,并且附上初值 output:1,2,3
var c []int //不定长
c1 = append(c, 1,2,3,4,5)
c2 := []int{1,2,3,4,5}
sc1 := make([]int, 0, len(c1)) // make函数创建一个长度为0,容量为len(c1)的切片
sc2 := new ([]int) // new函数创建一个指向长度为0的切片的指针
//map 键值对
m1 := map[string]int{"a": 1, "b": 2} //定义并初始化一个map output: map[a:1 b:2]
m2 := make(map[string]int) //使用make函数创建一个空的map
m2["c"] = 3 //向map中添加一个键值对
for k, v := range m1 { // 使用range关键字遍历map,k代表键,v代表值
fmt.Printf("key: %s, value: %d\n", k, v) //output: key: a, value: 1 \n key: b, value: 2
}go流程控制#
//if语句
if x > 0 {
fmt.Println("x is positive")
} else if x < 0 {
fmt.Println("x is negative")
} else {
fmt.Println("x is zero")
}
//switch语句
switch day := time.Now().Weekday(); day {
case time.Monday:
fmt.Println("It's Monday")
case time.Tuesday:
fmt.Println("It's Tuesday")
default:
fmt.Println("It's another day")
}
//for循环
for i := 0; i < 5; i++ {
fmt.Println(i) //output: 0 1 2 3 4
}
//goto语句
func example() {
fmt.Println("Start")
goto Skip
fmt.Println("This will be skipped")
Skip:
fmt.Println("End")
}
//output: Start \n End
//select语句
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "Hello from channel 1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "Hello from channel 2"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1) //output: Hello from channel 1
case msg2 := <-ch2:
fmt.Println(msg2) //output: Hello from channel 2
case <-time.After(3 * time.Second):
fmt.Println("Timeout")
}
go函数和方法#
//函数定义
func add(a int, b int) int {
return a + b
}
//函数调用
result := add(3, 5) //output: 8
//方法定义
type Rectangle struct {
width float64
height float64
}
func (r Rectangle) Area() float64 {
return r.width * r.height
}
//方法调用
rect := Rectangle{width: 4, height: 5}
area := rect.Area() //output: 20
// 匿名函数
func() {
fmt.Println("This is an anonymous function")
}() //output: This is an anonymous function
// 闭包
func makeAdder(x int) func(int) int {
return func(y int) int {
return x + y
}
}
add5 := makeAdder(5)
result := add5(3) //output: 8
//可变参数
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
result := sum(1, 2, 3, 4) //output: 10go指针#
//指针定义
var p *int //定义一个指向int类型的指针
//指针赋值
x := 10
p = &x //将变量x的地址赋值给指针p
//指针解引用
value := *p //通过指针p获取x的值 output: 10
//指针修改值
*p = 20 //通过指针p修改x的值
fmt.Println(x) //output: 20
//指针与函数
func increment(n *int) {
*n++ //通过指针n修改传入的值
}
num := 5
increment(&num) //传入num的地址
fmt.Println(num) //output: 6
//指针与结构体
type Point struct {
x int
y int
}
p1 := Point{x: 1, y: 2}
p2 := &p1 //创建一个指向p1的指针
p2.x = 3 //通过指针p2修改p1的x值
fmt.Println(p1) //output: {3 2}
//指针与切片
func modifySlice(s []int) {
s[0] = 100 //修改切片的第一个元素
}
slice := []int{1, 2, 3}
modifySlice(slice) //传入切片
fmt.Println(slice) //output: [100 2 3]
//二级指针
x := 10
p := &x
pp := &p
fmt.Println("x:", x, "p:", p, "pp:", pp, "**pp:", **pp) //output: x: 10 p:随机地址 pp: 随机地址 **pp: 10go错误处理#
//error类型
type error interface {
Error() string
}
//defer语句
func example() {
defer fmt.Println("This will be printed last")
fmt.Println("This will be printed first")
}//output: This will be printed first \n This will be printed last
//panic和recover
func example() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("Something went wrong") //触发panic
fmt.Println("This will not be printed")
}//output: Recovered from panic: Something went wronggoError样例:
package main
import (
"fmt"
"os"
"time"
)
const (
TimeFmt = "2006-01-02 15:04:05"
)
func main() {
username, err := queryDatabase(100)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(username)
}
err = readFile("test.txt")
if err != nil {
fmt.Println(err)
}
a := []int{2, 4, 1, 24, 4, 32, 1, 243, 65, 43, 765, 23, 234}
res, err := safeAccess(a, 100)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
defres := deferReturn()
fmt.Println("deferResult:", defres)
}
type BusinessError struct {
Code int
Message string
Time time.Time
}
func (e *BusinessError) Error() string {
return fmt.Sprintf("错误代码: %d \n 消息: %s \n 时间:%s \n",
e.Code, e.Message, e.Time.Format(TimeFmt))
}
func queryDatabase(userID int) (string, error) {
if userID <= 0 {
return "", &BusinessError{
Code: 400,
Message: "用户不存在",
Time: time.Now(),
}
}
if userID == 999 { //数据库连接超时
return "", &BusinessError{
Code: 401,
Message: "数据库连接超时",
Time: time.Now(),
}
}
return "李四", nil
}
func readFile(fileName string) error {
file, err := os.Open(fileName)
if err != nil {
return fmt.Errorf("打开文件失败: %w", err)
}
defer file.Close()
buf := make([]byte, 1024)
_, err = file.Read(buf)
if err != nil {
return fmt.Errorf("读取文件失败: %w", err)
}
return nil
}
func safeAccess(arr []int, index int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
return arr[index], nil
}
func deferReturn() (result int) {
defer func() {
result++
}()
return 10
}go方法#
方法定义:
绑定一个已定义类型的函数称为方法。方法与函数的区别在于,方法有一个特殊的接收者参数,它表示调用该方法的实例。接收者可以是值类型或指针类型。
已定义类型须为我们自己定义的类型,不能是内置类型(如int、string等),也不能是其他包中定义的类型。
func (接收者 接收者类型) 方法名(参数列表) 返回值列表 {
// 方法体
}go接收者类型:值接收者(不改变参数值),指针接收者(改变参数值)
样例:
package main
import "fmt"
type BankAccount struct {
AccountNumber string
AccountHolder string
Balance float64
IsActive bool
}
func (acc BankAccount) GetAccountInfo() string {
status := "active"
if !acc.IsActive {
status = "unactive"
}
return fmt.Sprintf("账号:%s, 持有人:%s , 余额:%.3f, 状态: %s",
acc.AccountNumber, acc.AccountHolder, acc.Balance, status)
}
func (acc *BankAccount) Deposit(amount float64) error {
if !acc.IsActive {
return fmt.Errorf("账号已冻结,无法存款")
}
if amount <= 0 {
return fmt.Errorf("存款金额必须大于0")
}
acc.Balance += amount
return nil
}
func (acc *BankAccount) Withdraw(amount float64) error {
if !acc.IsActive {
return fmt.Errorf("账号已冻结,无法取款")
}
if amount <= 0 {
return fmt.Errorf("取款金额必须大于0")
}
if amount > acc.Balance {
return fmt.Errorf("取款金额不能大于余额")
}
acc.Balance -= amount
return nil
}
func (acc *BankAccount) Freeze() {
acc.IsActive = false
}
func (acc *BankAccount) Unfreeze() {
acc.IsActive = true
}
func main() {
acc := BankAccount{
AccountNumber: "654819854",
AccountHolder: "李四",
IsActive: true,
Balance: 1000.0,
}
fmt.Println(acc.GetAccountInfo())
if err := acc.Deposit(1000.0); err != nil {
fmt.Println("存款失败", err)
}
fmt.Println("存款成功,余额:", acc.Balance)
if err := acc.Withdraw(acc.Balance); err != nil {
fmt.Println("取款失败", err)
}
fmt.Println("取款成功,余额:", acc.Balance)
acc.Freeze()
if err := acc.Deposit(500.0); err != nil {
fmt.Println("存款失败", err)
}
acc.Unfreeze()
if err := acc.Deposit(500.0); err != nil {
fmt.Println("存款失败", err)
}
}
go接口#
接口定义:
接口为Go语言中实现多态的重要特性,它定义一组方法的签名,但不进行具体实现
若一个类型实现了接口中的所有方法,则该类型实现了该接口,不需要显示声明
type 接口名 interface {
方法1(参数列表) 返回值列表
方法2(参数列表) 返回值列表
...
}
//空接口
var x interface{}
//空接口可以存储任意类型的值, 类似于Object(Java) any(Typescript)
x := "Hello, World!"go样例:
package main
import (
"fmt"
"time"
)
type Payment interface {
Pay(amount float64) (string, error)
Refund(trcID string, amount float64) (string, error)
Query(trcID string) (string, error)
}
type AliPay struct {
AppID string
AppSecret string
MerchantID string
}
func (acc AliPay) Pay(amount float64) (string, error) {
return fmt.Sprintf("ALIPAY-%d", time.Now().Unix()), nil
}
func (acc AliPay) Refund(trcID string, amount float64) (string, error) {
return fmt.Sprintf("ALIPAY_REFUND-%s", trcID), nil
}
func (acc AliPay) Query(trcID string) (string, error) {
return "交易成功", nil
}
type WechatPay struct {
AppID string
AppSecret string
MerchantID string
}
func (acc WechatPay) Pay(amount float64) (string, error) {
return fmt.Sprintf("WECHAT-%d", time.Now().Unix()), nil
}
func (acc WechatPay) Refund(trcID string, amount float64) (string, error) {
return fmt.Sprintf("WECHAT_REFUND-%s", trcID), nil
}
func (acc WechatPay) Query(trcID string) (string, error) {
return "交易成功", nil
}
func ProcessPayment(pay Payment, amount float64) (string, error) {
fmt.Println("开始处理支付请求...")
trsID, err := pay.Pay(amount)
if err != nil {
return "", err
}
fmt.Printf("支付成功, 交易号: %s\n", trsID)
return trsID, nil
}
func main() {
ali := AliPay{
AppID: "alipay",
AppSecret: "alisecret",
MerchantID: "ali_merchant",
}
wechat := WechatPay{
AppID: "wechat",
AppSecret: "wechatsecret",
MerchantID: "wedchat_merchant",
}
trs := []struct {
Payment Payment
Amount float64
Name string
}{
{ali, 100, "支付宝"},
{wechat, 100, "微信"},
}
for _, t := range trs {
trID, err := ProcessPayment(t.Payment, t.Amount)
if err != nil {
fmt.Println("支付失败, 错误信息:", err)
}
fmt.Println("p: 支付成功, 交易号:", trID)
}
//空接口
var x interface{}
x = 123
EmptyInterface(x)
x = "hello"
EmptyInterface(x)
}
func EmptyInterface(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
default:
fmt.Println("unknown")
}
}goGo面向对象#
Go语言虽然不是传统意义上的面向对象语言,但它支持一些面向对象的特性,如结构体、方法和接口等。
组合替代继承:Go语言不支持类的继承,但可以通过组合来实现类似的功能。通过将一个结构体嵌入到另一个结构体中,可以实现代码复用和行为扩展。
样例1
package main
import (
"fmt"
"time"
)
type BaseUser struct {
ID int64
UserName string
Email string
CreatedAt time.Time
}
func (u BaseUser) GetCreateDate() string {
return u.CreatedAt.Format("2006-01-02")
}
func (u *BaseUser) DisplayBasicInfo() {
fmt.Printf("ID: %d Name: %s Email: %s CreateAt: %s", u.ID, u.UserName, u.Email, u.GetCreateDate())
}
type Address struct {
Province string
City string
Street string
Detail string
}
func (a Address) GetFullAddress() string {
return fmt.Sprintf("%s%s%s%s", a.Province, a.City, a.Street, a.Detail)
}
type NormalUser struct {
BaseUser
Address []Address
}
func (n *NormalUser) DisplayBasicInfo() {
fmt.Printf("ID: %d Name: %s Email: %s", n.ID, n.UserName, n.Email)
}
func (n *NormalUser) AddAddress(address Address) {
n.Address = append(n.Address, address)
}
type VIPUser struct {
BaseUser
Address []Address
VIPLevel int
Discount float64
ExpireTime time.Time
}
func (vu *VIPUser) AddAddress(address Address) {
vu.Address = append(vu.Address, address)
}
func (vu *VIPUser) IsVipValid() bool {
return vu.ExpireTime.After(time.Now())
}
func (vu *VIPUser) GetDiscount() float64 {
if vu.IsVipValid() {
return vu.Discount
}
return 1.0
}
type UserService struct {
}
const (
UserTypeNormal = "normal"
UserTypeVip = "vip"
)
func getUserId() int64 {
return time.Now().Unix()
}
func (s *UserService) CreateUser(username, email string, userType string) (interface{}, error) {
baseUser := BaseUser{
ID: getUserId(),
UserName: username,
Email: email,
CreatedAt: time.Now(),
}
switch userType {
case UserTypeNormal:
normalUser := NormalUser{
BaseUser: baseUser,
Address: []Address{},
}
return normalUser, nil
case UserTypeVip:
vipUser := VIPUser{
BaseUser: baseUser,
VIPLevel: 1,
Discount: 0.9,
ExpireTime: time.Now().AddDate(0, 0, 30),
}
return vipUser, nil
default:
return 0, fmt.Errorf("unknown user type: %s", userType)
}
}
func main() {
ser := &UserService{}
normalUser, err := ser.CreateUser("张三", "zhangsan@example.com", UserTypeNormal)
if err != nil {
fmt.Println("normalUsr: ", err)
return
}
vipUser, err := ser.CreateUser("李四", "lisi@example.com", UserTypeVip)
if err != nil {
fmt.Println("vipUsr: ", err)
return
}
nUser := normalUser.(NormalUser)
nUser.AddAddress(Address{
Province: "四川省",
City: "成都市",
Street: "物流大道",
Detail: "物流大道666号",
})
nUser.DisplayBasicInfo()
users := []interface{}{nUser, vipUser}
for _, user := range users {
switch u := user.(type) {
case NormalUser:
fmt.Println("普通用户")
fmt.Println("用户ID:", u.ID)
fmt.Println("用户名:", u.UserName)
fmt.Println("邮箱:", u.Email)
fmt.Println("创建时间:", u.GetCreateDate())
fmt.Println("地址:")
for _, addr := range u.Address {
fmt.Println(" -", addr.GetFullAddress())
}
case VIPUser:
fmt.Println("普通用户")
fmt.Println("用户ID:", u.ID)
fmt.Println("用户名:", u.UserName)
fmt.Println("邮箱:", u.Email)
fmt.Println("创建时间:", u.GetCreateDate())
fmt.Println("VIP等级:", u.VIPLevel)
fmt.Println("折扣:", u.Discount)
fmt.Println("VIP状态:", u.IsVipValid())
fmt.Println("地址:")
for _, addr := range u.Address {
addr.GetFullAddress()
}
}
}
}go样例2:
package main
import (
"fmt"
)
type Notifier interface {
Notify(message string) error
}
type EmailNotifier struct {
SMTPHost string
Port string
}
func (e *EmailNotifier) Notify(message string) error {
fmt.Printf("发送邮件成功: %s\n", message)
return nil
}
type SmsNotifier struct {
APIKey string
TmplCode string
}
func (s *SmsNotifier) Notify(message string) error {
fmt.Printf("发送短信成功:: %s\n", message)
return nil
}
type BroadCastNotifier struct {
notifierList []Notifier
}
func (b *BroadCastNotifier) Notify(message string) error {
for _, n := range b.notifierList {
if err := n.Notify(message); nil != err {
return err
}
}
return nil
}
type OrderService struct {
notifier Notifier
}
func (o *OrderService) SetNotify(n Notifier) {
o.notifier = n
}
func (o *OrderService) CreateOrder(product string, quantity int) {
fmt.Printf("创建订单:%s x %d \n", product, quantity)
if err := o.notifier.Notify("订单已创建"); err != nil {
fmt.Println(err)
}
}
func main() {
orderService := &OrderService{}
emailNotifier := &EmailNotifier{
SMTPHost: "smtp.163.com",
Port: "465",
}
smsNotifier := &SmsNotifier{
APIKey: "asgdhjghkjfxbanuie_jlkdfsla",
TmplCode: "hjklncxsaoiuiw",
}
broadCastNotifier := &BroadCastNotifier{
notifierList: []Notifier{emailNotifier, smsNotifier},
}
orderService.SetNotify(emailNotifier)
orderService.CreateOrder("邮件", 1)
orderService.SetNotify(smsNotifier)
orderService.CreateOrder("手机", 2)
orderService.SetNotify(broadCastNotifier)
orderService.CreateOrder("手机-邮件", 3)
}
go包与包的导入#
每个go源文件都必须属于一个包,声明在第一行
package main //main包是一个特殊的包,它定义了一个独立可执行的程序,而不是一个库。main包必须包含一个名为main的函数,作为程序的入口点。
package mypackage //自定义包,通常用于组织代码和实现模块化。包名通常与目录名相同,便于管理和导入。go包导入机制:
import (
"标准包名"
"第三方包"
"项目内部包"
"自定义包"
)
//也可以使用单个导入语句导入一个包
import "fmt"
import "github.com/gin-gonic/gin"go既然提到包的导入,就不得不提包的可见性了:
- 首字母大写的标识符是导出的,可以在包外部访问。
- 首字母小写的标识符是未导出的,只能在包内部访问。
go常用标准库:
- fmt:格式化输入输出
- os:操作系统功能
- io:输入输出操作
- net/http:HTTP客户端和服务器
- encoding/json:JSON编码和解码
- time:时间和日期处理
- math/rand:随机数生成
- strings:字符串操作
- strconv:字符串与基本数据类型转换
若需要其他标准库可查询官方文档
样例:
反射#
定义:程序在运行时检查其自身的结构和行为的能力。
核心:interface{} + reflect包
var x int = 42
// 类型信息
t := reflect.TypeOf(x)
// 值信息
v := reflect.ValueOf(x)goGo工具链#
Go工具链是Go语言提供的一组命令行工具,用于构建、测试、安装和管理Go代码。常用的Go工具链包括:
- go mod: Go模块依赖管理
- go build: 编译程序生成可执行文件
- go run: 编译直接运行Go程序
- go test: 运行测试,支持单元测试、示例测试和基准测试。
- go fmt: 格式化代码
- go get: 添加依赖包到模块
- go install: 编译并安装包或命令
- go vet: 静态分析工具,检查代码中的潜在问题
- go doc: 查看文档
调试、分析工具(pprof,日志库)#
Go提供了强大的调试和性能分析工具:
- Delve调试器: Go语言专用的调试器,支持断点、单步执行、变量查看等功能
- pprof性能分析: Go内置的性能分析工具,用于分析CPU和内存使用情况,帮助开发者优化程序性能
- 日志库: Go标准库提供了基本的日志功能(log包),同时也有第三方日志库(如logrus、zap等)提供更丰富的日志功能和性能优化
- 内置工具: racedetector, benchmark
