Go 语言入门指南:基础语法和常用特性解析 | 青训营X豆包MarsCode 技术训练营

64 阅读8分钟

加入青训营以后又可以学习一门语言啦!我们和以前一样,从Hello, World开始吧!

要一辈子golang哦

1.创建第一个 Go 程序

创建一个名为 hello.go 的文件,并编写以下代码:

package main
import "fmt"
func main() {
    fmt.Println("Hello, World!")
}

下面,我们将来介绍其中每一个语句的作用以及基础语法和常用特性

2. 运行 Go 程序

在终端中导航到 hello.go 文件所在的目录,然后运行以下命令:

go run hello.go

它会先编译代码,然后立即运行生成的可执行文件。这个命令适用于快速测试和运行 Go 程序,但不会生成一个独立的可执行文件。

如果你想要单独编译代码并生成可执行文件,可以使用以下命令:

go build hello.go

这会在当前目录下生成一个可执行的.exe文件

3. 基础语法

一般变量声明:

  1. 显式声明: 使用 var 关键字显式声明变量,可以指定变量类型,也可以省略类型让编译器自动推断。

    var a int = 1
    var b = 20 // 类型推断
    
  2. 简短声明: 使用 := 进行简短声明,只能在函数内部使用,编译器会自动推断变量类型。

    c := 30
    
  3. 多变量声明: 可以同时声明多个变量。

    var x, y, z int
    var i, j = 1, 2
    k, l := 3, 4
    
  4. 零值初始化: 未显式初始化的变量会被赋予其类型的零值。

    var s string   //空字符串 ""
    var n int     // 0
    var f float6` // 0.0
    var b bool    // false
    
  5. 匿名变量: 使用 _ 作为匿名变量,可以忽略不需要的返回值。

    _, err := someFunction()
    
  6. 常量声明: 使用 const 关键字声明常量,常量的值在编译时确定,不能被修改。

    const Pi = 3.14
    

函数声明与特点:

  1. 基本语法: 使用 func 关键字声明函数,函数名后跟参数列表和返回值类型。

    func functionName(parameterName type) returnType {// 函数体}
    
  2. 多返回值: Go 语言支持函数返回多个值,这在处理错误时非常有用。

func swap(x, y string) (string, string) {
    return y, x
}//交换两个字符串的内容

func main() {
    a, b := "hello", "world"
    fmt.Println("Before swap:", a, b)
    a, b = swap(a, b)
    fmt.Println("After swap:", a, b)
}

其中(string, string)代表返回值的类型,所以尤其要注意return的顺序,接收时可使用如案例所示方式

  1. 命名返回值: 可以为返回值命名,这样在函数体内可以直接使用这些变量,并且在 return 语句中可以省略返回值。
    func split(sum int) (x, y int) {
        x = sum * 4 / 9
        y = sum - x
        return    //返回的值为x,y
    }
  1. 可变参数: 使用 ... 语法表示可变参数,函数可以接受不定数量的参数。
    func printNumbers(numbers ...int) {
        for _, number := range numbers {
            fmt.Println(number)
        }
    }
  1. 匿名函数和闭包: 支持匿名函数和闭包,可以在函数内部定义和调用匿名函数。
    func main() {
        add := func(a, b int) int {
            return a + b
        }
        fmt.Println(add(3, 4))
    }
}
控制结构

Go中控制结构与c/c++,Java的主要区别是无需()

// if 语句
if x > 10 {
    fmt.Println("x is greater than 10")
} else {
    fmt.Println("x is less than or equal to 10"
}

// for 循环
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// switch 语句
switch day {
case "Monday":
    fmt.Println("Start of the week")
case "Friday":
    fmt.Println("End of the week")
default:
    fmt.Println("Midweek")
}

数组的声明特点

  1. 固定长度: 数组的长度在声明时就确定,且不能改变。

    var arr [5]int
    
  2. 默认值: 数组元素在声明时会被初始化为其类型的零值。

  3. 自动推断长度: 使用 ... 让编译器自动推断数组长度。

    arr := [...]int{123}
    

切片的特点

  1. 动态长度: 切片的长度是动态的,可以根据需要增长或缩减。

    var slice []int
    
  2. 引用类型: 切片是引用类型,指向底层数组的一部分。

    arr := [5]int{12345}
    slice := arr[1:4// [2, 3, 4]
    
  3. 内置函数: 使用 len 获取切片长度,使用 cap 获取切片容量。

    slice := []int{12345}
    fmt.Println(len(slice)) // 5
    fmt.Println(cap(slice)) // 5
    
  4. 切片操作: 可以对切片进行切片操作,生成新的切片。

    slice := []int{12345}
    newSlice := slice[1:3// [2, 3]
    
  5. 追加元素: 使用 append 函数向切片追加元素。

    slice := []int{123}
    slice = append(slice, 45// [1, 2, 3, 4, 5]
    

示例代码

package main
import "fmt"
func main() {
    // 数组示例
    var arr [5]int
    arr = [5]int{12345}
    fmt.Println("数组:", arr)
    // 切片示例
    slice := []int{123}
    fmt.Println("切片:", slice)
    slice = append(slice, 45)
    fmt.Println("追加元素后的切片:", slice)
    newSlice := slice[1:4]
    fmt.Println("切片操作后的新切片:", newSlice)
}

Map 的特点

  1. 键值对存储: Map 是一种无序的键值对集合,键和值的类型在声明时必须指定。

    var m map[string]int
    
  2. 动态大小: Map 的大小是动态的,可以根据需要自动扩展。

  3. 零值为 nil: 未初始化的 Map 的零值是 nil,不能直接存取值,需要使用 make 函数初始化。

    var m map[string]int
    fmt.Println(m == nil// true
    
  4. 初始化: 使用 make 函数初始化 Map。

    m := make(map[string]int)
    
  5. 字面量初始化: 可以使用字面量初始化 Map。

    m := map[string]int{"one"1"two"2}
    
  6. 添加和更新元素: 使用键访问 Map,可以添加或更新元素。

    m := make(map[string]int)
    m["one"] = 1
    m["two"] = 2
    
  7. 删除元素: 使用 delete 函数删除 Map 中的元素。

    delete(m, "one")
    
  8. 检查键是否存在: 通过双赋值检测键是否存在。

    value, ok := m["one"]
    if ok {
        fmt.Println("Key exists with value:", value)
    } else {
        fmt.Println("Key does not exist")
    }
  1. 遍历 Map: 使用 for range 循环遍历 Map。
    for key, value := range m {
        fmt.Println(key, value)
    }
  1. 并发访问: Map 不是线程安全的,在并发访问时需要使用同步机制如 sync.Mutex 或 sync.RWMutex

示例代码

package main
import "fmt"
func main() {
    // 初始化 Map
    m := make(map[string]int)
    // 添加元素
    m["one"] = 1
    m["two"] = 2
    // 更新元素
    m["two"] = 22
    // 检查键是否存在
    value, ok := m["one"]
    if ok {
        fmt.Println("Key 'one' exists with value:", value)
    } else {
        fmt.Println("Key 'one' does not exist")
    }
    // 删除元素
    delete(m, "one")
    // 遍历 Map
    for key, value := range m {
        fmt.Println(key, value)
    }
}
结构体
type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}

fmt.Println(p.Name)

并发编程

Go 语言内置对并发的支持,使用 goroutine 和 channel 实现并发编程。

// 启动一个 goroutine
go func() {
    fmt.Println("Hello from goroutine")
}()
// 使用 channel 进行通信
ch := make(chan int)
go func() {
    ch <- 42
}()
fmt.Println(<-ch)

包管理

使用 go get 命令来获取和管理外部包。例如:

go get github.com/gorilla/mux

单元测试

Go 语言内置了测试框架,使用 testing 包编写测试。

package main
import "testing"
func TestAdd(t *testing.T) {
    result := add(23)
    if result != 5 {
        t.Errorf("Expected 5, got %d", result)
    }
}

通过以上内容,你已经掌握了 Go 语言的基础语法和常用特性(不再只是hello,world了)。继续深入学习和实践,你将能够更好地使用 Go 语言进行开发。

最后,由于本人最近也在学习Java语言,所以列举了上文中与Java不同的点

变量声明的不同

Go

  • Go 语言支持显式声明和类型推断,使用 var 关键字或简短声明 :=
  • 未初始化的变量会被赋予零值。

Java

  • Java 语言需要显式声明变量类型,使用 类型 变量名 的形式。
  • 未初始化的变量不能使用,编译时会报错。

结构控制的不同

条件语句

Go

  • if 语句不需要括号,但代码块必须用大括号 {} 包围。
  • 支持在 if 语句中声明变量。

Java

  • if 语句需要括号 () 包围条件表达式,代码块用大括号 {} 包围。
  • 不支持在 if 语句中声明变量。

循环语句

Go

  • 只有 for 一种循环结构,可以用来实现传统的 for 循环、while 循环和无限循环。
  • for 语句的条件部分不需要括号。

Java

  • 支持 forwhile 和 do-while 三种循环结构。
  • for 和 while 语句的条件部分需要括号。

函数声明的不同

Go

  • 使用 func 关键字声明函数。
  • 参数类型和返回类型在参数名之后指定。
  • 支持多返回值。
  • 支持命名返回值。
  • 支持匿名函数和闭包。

Java

  • 使用 返回类型 函数名 的形式声明函数。
  • 参数类型在参数名之前指定。
  • 不支持多返回值,但可以使用对象或数组返回多个值。
  • 不支持命名返回值。
  • 支持匿名类和 lambda 表达式

由此可见Go与Java的区别还是挺大的,但我们万能的程序员会用不同的语言写出(同样的hello,world)不同的功能。语言就像跑鞋,都能走在路上,但雪地靴适合御寒,篮球鞋有更强的防侧翻,每个语言都有其优势区间,而我们要做的就是让语言在最适合自己的位置上发光发热。