后端Go语言入门语法笔记 | 豆包MarsCode AI 刷题

2 阅读4分钟

入门

语言特性

  • 高性能
  • 自动垃圾回收
  • 并发编程
  • 网络库
  • 编译速度快、交叉编译

基础语法

Hello World
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World\n")
    return 0
}

package main:表示该代码文件属于main包的一部分

import ("fmt"):导入格式化库fmt

func main():程序执行入口

fmt.Println("Hello World\n"):在标准化输出打印Hello World\n

定义变量

定义变量的方式有两种

var v Type [= value]
v := value

其中定义全局变量必须使用var声明。

分支
if  condition {}
switch [condition]:
case value | condition:
    // ...
default:
    // ...
循环

go语言中没有while关键字,使用for condition {}实现

for i := 0; i < n; i++ {}
for i < n {}
for k, v := range array {}

数据结构

数组(array)

数组为一段定长序列,其定义方式为:

var arr [num]Type
arr := [num]Type{...}

数组在作为参数传递时,采用值传递(传递时会拷贝整个数组)而非引用传递,函数内无法直接对原数组修改。

切片(slice)

切片的是一段变长序列,可以动态的调整序列长度,其定义方式为:

s := make([]Type, length)

切片是一种引用类型的数据结构,其结构体为

// src/reflect/value.go
type SliceHeader struct {
	Data uintptr // 指向序列首个元素的指针
	Len  int // 切片长度
	Cap  int // 切片容量
}

切片在作为参数传递时,只会拷贝该结构体,而非结构体内指针指向的序列。对结构体参数的修改不会影响原结构体,但可以修改所指向的元素,当函数内切片发生扩容时,函数内外的两个切片指向的底层序列将会不同。

Data指向一段底层的连续序列,在序列满时,go会将申请一片新的连续的内存空间,将旧序列内容拷贝到新序列。在切列容量小于256时,增长策略时新序列容量为旧序列的翻倍,容量超过256后,新切片容量计算公式为:

Cnew=Cold+Cold+32564C_\text{new}=C_\text{old}+\frac{C_\text{old}+3*256}{4}

且新切片的Data指针指向的位置与旧切片的不相同。

切片循环访问方式:

for i := 0; i < len(arr); i++ {/* arr[i] */}
for idx, val := range(arr) {/* 不使用idx则改其为 _ */}
哈希表(map)

存储键值对的无序数据结构,其定义方式为:

mp := make(map[kType]vType)
mp := map[kType]vType{k1: v1, k2: v2, /*...*/ }

访问与删除方式为:

val, ok := mp[key]
if !ok {/* ... */}
delete(mp, key)
指针(pointer)

使用方式与C的指针类似,不同点:

  • 不能允许指针运算
  • 不允许指针间类型转换

unsafe.Pointer为通用指针(unsafe)

函数(function)
func name(args Type) returns {}

当函数的参数为指针类型时,会拷贝指针作为形参,以便修改指向对象,但使得逃逸分析将对象分配到堆空间上而非栈空间,而降低访问速度以及带来堆内对象垃圾回收的成本,因此对于只读小对象时,直接拷贝对象传参可能性能会更好。

错误(error)
type error interface {
    Error() string
}

从error的接口可知,创建一个error的关键是报错的信息。创建方式如下:

errors.New("error")

捕获与处理方式:

_, err := f()
if err != nil { /* ... */ }
结构体(struct)
type Item struct {
    Name Type // public
    id Type // private
}
func (r Name) function() {} // 值接收,不能直接改变结构体内成员
func (r *Name) function() {} // 指针接收,能直接改变结构体内成员
面向对象

面向对象三要素:封装、继承、多态

封装

结构体内包装属性和方法,使用属性名和方法名的开头大小写控制可见性,大写为public,小写为private。

继承

不同于Java的关键字extends,go使用匿名字段实现类似的功能。

type Card struct {
    Item // 匿名字段
}

//访问方式
card.Name // 而非card.Item.Name

多态

go提供接口interface来提供多继承和多态功能,例如:

// in sort.Interface
type Interface interface {
        // Len is the number of elements in the collection.
        Len() int
        // Less reports whether the element with
        // index i should sort before the element with index j.
        Less(i, j int) bool
        // Swap swaps the elements with indexes i and j.
        Swap(i, j int)
}
// implementation
type Cards []card
func (a Cards) Len() int           { return len(a) }
func (a Cards) Less(i, j int) bool { return a[i].id < a[j].id }
func (a Cards) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

通过自定义LenLessSwap方法,实现sort的接口,就可以使用sort.Sort函数排序自定义类型,每个类型的基于自己的sort.Sort实现不同排序规则,即为多态。