青训营X豆包MarsCode技术训练营 | GO语言入门指南2

39 阅读11分钟

书接上回: 概述&环境搭建--->基础语法

一起来学golang吧~A Tour of Go

基本语法

Hello World语法解释

package main

import "fmt"
// This is discription
func main() {
    fmt.Println("Hello, World!")
}

1、Package声明

package main,表明这个Go文件属于 main 包。在Go中,每个可执行程序都必须有一个 main 包,并且在 main 包中必须有一个main()函数作为程序的入口点。

2、导入语句

import "fmt",导入了 fmt 包。fmt 包是Go标准库中的一个包,提供了输入输出和格式化文本的功能。本代码中,使用了 fmt.Println() 函数来输出文本。

3、注释

// This is description 是一行注释。在Go中,使用//表示单行注释,用于向代码中添加解释和说明。

4、main函数

在Go程序中,main() 函数是程序的入口点。当程序执行时,它会首先执行 main() 函数。在这段代码中,main() 函数简单地调用 fmt.Println() 函数,将字符串"Hello, World!"输出到控制台。

5、执行输出

调用 fmt.Println("Hello, World!") 将 “Hello, World!” 这个字符串输出到终端。fmt.Println() 函数用于在控制台输出文本,并在输出的最后自动添加一个换行符。

小贴士:在Go中,函数名首字母的大小写决定了该函数的可见性。

  • 如果函数名以大写字母开头,那么它是可导出的 ,其他包可以访问该函数。(类似面向对象语言中的 public)
  • 如果函数名以小写字母开头,它是私有的,其他包无法访问该函数。(类似面向对象语言中的 protected )

变量和常量:

变量

  1. 使用关键字var来声明一个变量,语法为:var variableName dataType
  2. 变量名必须以字母或下划线开头,可以包含字母、数字和下划线,但不能使用Go的关键字
  3. 可以一次性声明多个变量,例如:var x, y int
  4. 如果声明时未初始化变量,则变量会被赋予其数据类型的零值,比如:int类型的零值是0string类型的零值是空字符串""
  5. 可以使用短变量声明来创建并初始化变量,语法为:variableName := value
var age int             // 声明一个整数变量 age
var name string         // 声明一个字符串变量 name
var x, y int            // 一次性声明多个变量 x, y 
x = 10                  // 给 x 赋值 10
name = "Joke"           // 给 name 赋值 "Joke"

var count = 5           // 声明一个整数变量 count 并初始化为 5
email := "golang@go.com"  // 使用短变量声明声明一个字符串变量 email 并初始化为 "golang@go.com"

在Go中,变量声明后必须使用,否则会导致编译错误。

Tips: 如果想要声明一个变量但不使用它,可以使用下划线 _ 来代替变量名,表示该变量被丢弃,不会占用内存空间。例如:

_ = 100 // 声明一个未使用的变量并初始化为 100,但在后续代码中并不使用该变量

总结:Go中的变量声明可以使用var关键字或短变量声明。变量声明时可以指定数据类型和初始值。未使用的变量可以使用下划线 _ 来代替

常量

  1. 使用关键字const来声明常量,语法为:const constantName dataType = value
  2. 常量必须在声明时初始化,并且其值在程序运行期间不能改变。
  3. 常量可以是字符、字符串、布尔值或数值类型(整数、浮点数)。
  4. 常量名的命名规则与变量相同,以字母或下划线开头,可以包含字母、数字和下划线,但不能使用Go的关键字。
  5. 常量的值必须是一个编译时可以确定的表达式,例如:const PI = 3.14
const PI = 3.14             // 声明一个名为 PI 的常量并赋值为 3.14
const appName = "MyApp"     // 声明一个名为 appName 的常量并赋值为 "MyApp"
const isDebug = true        // 声明一个名为 isDebug 的常量并赋值为 true

// 可以在常量组中一次性声明多个常量
const (
    monday = "Monday"
    tuesday = "Tuesday"
    )

数据类型

基本数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。Go 语言按类别有以下几种数据类型:

  1. 布尔型:
    • 布尔型的值只可以是常量 true 或者 false。eg:var b bool = true
  2. 数字类型:
    • 整型int和浮点型float32float64,Go语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
  3. 字符串类型:
    • 一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
  4. 派生类型: 包括:
    • 指针类型Pointer
    • 数组类型
    • 结构化类型struct
    • Channel类型
    • 函数类型
    • 切片类型
    • 接口类型interface
    • Map类型

复合数据类型

1、数组Array和切片Slice

数组是一个固定大小的数据结构,它包含一组相同类型的元素。在 Go 语言中,创建数组时,需要指定数组的大小,并且该大小在声明后无法改变。数组的大小是其类型的一部分,因此类型为 [size]dataType ,其中 size 表示数组大小, dataType 表示数组元素的数据类型。

  1. 数组是值类型,当将一个数组赋值给另一个数组时,会复制所有的元素。这意味着对于大型数组,复制操作可能会比较耗时和内存。
var numbers [5]int // 声明一个包含5个整数的数组

numbers = [5]int{1, 2, 3, 4, 5} // 初始化数组元素

numbers := [5]int{1, 2, 3, 4, 5} // 声明并初始化一个数组
  1. 切片是对数组的引用。在声明切片时,不需要指定大小,只需要指定元素的类型,可以动态增长和收缩。

Tips

  • 切片本身并不存储数据,它只是一个引用,指向底层数组的一部分。当切片的容量不足时,底层数组会被自动扩容。
  • 对切片的修改会影响到底层数组。当将一个切片赋值给另一个切片时,它们会引用相同的底层数组。
var mySlice []int // 声明一个切片

// 使用 make() 函数创建切片,第一个参数是切片类型,第二个参数是切片长度,第三个参数是切片容量(可选)
mySlice = make([]int, 5)    // 长度为5,容量也为5的切片

mySlice = []int{1, 2, 3, 4, 5} // 初始化一个切片

// 切片的动态增长
mySlice = append(mySlice, 6, 7, 8) // 添加元素到切片末尾

可以创建一个新的切片,它引用了现有切片或数组的一部分。这被称为切片的切片。切片的切片的语法是slice[start:end],其中start是第一个元素的索引(包括),end是最后一个元素后面的索引(不包括)。结果切片的长度是end - start,容量是从 start 索引到底层数组末尾的元素数量。

// 切片的切片,获取子切片
subSlice := mySlice[2:5] // 这将创建一个从索引 2 到索引 4(不包括 5)的子切片
2、映射(Map)

在Go中,映射(Map)是一种键值对的无序集合,类似于Python中的字典(dict)。它提供了一种方便的方式来存储和检索键值对,并且允许根据键快速查找对应的值。

  1. 创建映射(Map): 在Go中,使用map[keyType]valueType的语法来声明一个映射。其中keyType表示键的数据类型,valueType表示值的数据类型。
  2. 初始化映射: 使用make()函数来初始化一个映射。初始化映射后,才能对其进行赋值和操作。
  3. 添加和更新键值对: 可以使用赋值操作符(=)来添加或更新映射中的键值对。如果指定的键不存在,它将被添加到映射中;如果指定的键已经存在,它将更新对应的值。
  4. 访问和检查键值对: 可以通过指定键来访问映射中的值。如果指定的键不存在,将返回值类型的零值。为了区分键不存在和值为零值两种情况,可以使用多返回值的方式来检查键是否存在。
  5. 删除键值对: 可以使用delete()函数来删除映射中的键值对。如果指定的键不存在,delete()函数不会产生错误。
  6. 遍历映射: 使用for range循环可以遍历映射中的所有键值对。
// 1.声明一个映射,键为字符串类型,值为整数类型
var myMap map[string]int

// 2.初始化一个映射
myMap := make(map[string]int)

// 3.更新键 "apple" 对应的值
myMap["apple"] = 10
myMap["banana"] = 5
myMap["apple"] = 15 

// 4.访问和检查键值对
value := myMap["apple"] // 访问键 "apple" 对应的值
// 检查键是否存在
value, exists := myMap["orange"]
if exists {
    fmt.Println("The value for 'orange' is:", value)
} else {
    fmt.Println("Key 'orange' does not exist.")
}

// 5.删除键 "banana" 对应的键值对
delete(myMap, "banana") 

// 遍历映射
for key, value := range myMap {
    fmt.Println(key, value)
}
3、结构体(Struct)

在Go语言中,结构体(Struct)是一种自定义的复合数据类型,它允许我们将不同类型的数据组合在一起,形成一个新的数据结构。结构体是由一系列字段(fields)组成,每个字段可以是不同的数据类型。结构体中的字段称为成员(members),它们表示结构体的特征和属性。

  1. 声明结构体: 使用type关键字来声明一个新的结构体类型。结构体的定义以关键字type开头,后面紧跟结构体的名称,然后是一个由字段组成的花括号代码块。
  2. 创建结构体实例: 通过声明一个结构体变量并为其成员赋值,我们可以创建结构体的实例。
  3. 访问结构体成员: 可以使用.运算符来访问结构体的成员。
  4. 结构体的匿名字段: 结构体允许字段没有名称,这样的字段称为匿名字段。匿名字段的数据类型必须是命名的类型或具有命名的类型。
  5. 嵌套结构体: 结构体可以包含其他结构体作为其成员,这被称为嵌套结构体。通过嵌套结构体,我们可以建立更复杂的数据结构
  6. 结构体的方法: 结构体可以关联方法,通过这些方法可以为结构体类型添加行为。方法是特殊类型的函数,它们与结构体关联,可以在结构体实例上调用。
// 1.声明结构体
type Person struct {
    Name   string
    Age    int
    Height float64
}

// 2.创建一个Person结构体的实例
person1 := Person{
    Name:   "Alice",
    Age:    30,
    Height: 1.75,
}

// 3.访问结构体成员
fmt.Println(person1.Name)   // 输出: "Alice"
fmt.Println(person1.Age)    // 输出: 30
fmt.Println(person1.Height) // 输出: 1.75

// 4.结构体的匿名字段
type Circle struct {
    float64 // 匿名字段,代表圆的半径
}

// 5.嵌套结构体
type Address struct {
    City  string
    State string
}
type Person struct {
    Name    string
    Age     int
    Address Address // 嵌套结构体作为成员
}

// 6.结构体的方法
type Rectangle struct {
    Width  float64
    Height float64
}
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area() // 调用结构体的方法
4、接口Interface

在Go语言中,接口Interface是一种抽象类型,它定义了一组方法的集合,但并不提供这些方法的具体实现。接口描述了对象的行为,而不关心对象的具体类型。任何类型只要实现了接口中定义的所有方法,就被认为是实现了该接口。

  1. 定义接口: 使用type关键字和interface关键字来定义一个接口。接口的定义由一组方法签名组成,这些方法签名描述了接口中的方法。方法签名包括方法的名称、参数列表和返回值列表。
  2. 实现接口: 要实现一个接口,只需在类型上提供接口中定义的所有方法。实现接口不需要显式地声明实现了哪个接口,只要提供了接口中的方法,编译器会自动判断该类型实现了哪些接口。
  3. 多态性: 接口的一个重要特性是多态性。多态性使得一个接口类型的变量可以在不同实现类型上调用相同的方法,实现了面向对象编程中的多态概念。
// 1.定义接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

// 2.实现接口
type Rectangle struct {
    Width  float64
    Height float64
}
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
    return 2*r.Width + 2*r.Height
}

// 3.多态性
func printShapeInfo(s Shape) {
    fmt.Println("Area:", s.Area())
    fmt.Println("Perimeter:", s.Perimeter())
}
rect := Rectangle{Width: 5, Height: 3}
printShapeInfo(rect)

在上面的示例中,定义了一个Shape接口,它有两个方法Area()Perimeter()。然后创建了一个Rectangle类型并为它实现了Shape接口的方法。在printShapeInfo函数中,接收一个Shape接口类型的参数,并在不同类型的实现上调用了Area()Perimeter()方法。这样,printShapeInfo函数可以处理任何实现了Shape接口的类型,实现了多态性。