GO 语言入门指南:基础语法与通用特性解析|青训营

首先简单介绍下go语言,其主要具有以下基础特点:

  1. 简洁、清晰:Go语言的语法非常简洁,易于学习,也易于阅读和维护。

  2. 并发支持:Go语言内建对并发编程的支持,可通过Goroutines和Channels实现的。

  3. 垃圾回收:Go语言有内置的垃圾回收机制,这使得内存管理变得更加容易。

  4. 快速编译:Go语言的编译速度非常快,这使得开发过程更加高效。

  5. 标准库:Go语言有一个强大的标准库,提供了大量的内置函数和包。

  6. 跨平台:Go语言可以在多种操作系统和平台上运行。

一. 基础语法

1. 包的声明及导入

在Go语言中,package是代码组织的基本单位。每个Go程序都是由包构成的,包可以提供函数、类型等代码组件以此来通过最简的方式达到我们所需的目的。

1) 定义包

在go文件的第一行首先应定义包的名称。如创建一个名为main_go的包可以这样:package main_go

2) 导入包

使用import将包导入后可以使用该包所包含的各种函数及其他代码组件来实现各种功能(注意该方式仅能导入本地项目或go环境中已有的包)。

js 复制代码
package main
import "fmt"

func main() {
    fmt.Println("Hello, world!")
}

以上片段中导入了包fmt并使用了其函数fmt.Println打印了字段"Hello,world!"

js 复制代码
import ( 
"fmt" 
"math/rand"
"strings"
)

你也可以像这样一次导入多个包。

3) 自定义包

js 复制代码
import ( 
"fmt" 
rand "math/rand" 
)

像这样自定义包"math/rand"的名为rand,在使用时即可将rand作为前缀调用"math/rand"中的函数

4) 创建包

首先,你需要创建一个新的目录来存放你的包,包的名称通常与目录名相同。然后,在该目录中创建一个新的.go文件,在文件的第一行声明包的名称。例如,你可以创建一个名为mypackage的包,它有一个hello.go文件:

js 复制代码
// hello.go
package mypackage

import "fmt"

func Hello() {
    fmt.Println("Hello, world!")
}

然后,你可以在你的主程序中导入并使用这个包。你需要使用包的完整路径(相对于GOPATH/src或Go Modules的路径)。例如:

js 复制代码
// main.go
package main

import (
    "fmt"
    "your_project_name/mypackage"
)

func main() {
    fmt.Println("This is main function.")
    mypackage.Hello()
}

your_project_name应该替换为该项目的名称

在上述代码中,我们导入了mypackage包,并在main函数中调用了mypackage.Hello()函数。

注意:如果你的包在GOPATH之外,或者你正在使用Go Modules(Go 1.11及更高版本),你需要使用包的完整路径,例如github.com/yourusername/yourrepository/mypackage 如下

js 复制代码
import "github.com/yourusername/yourrepository/yourpackage"

并通过自定义简化

js 复制代码
import yp "github.com/yourusername/yourrepository/yourpackage"

2. 函数的定义

在Go语言中,func关键字用于声明函数。函数是一段可以被重复调用的代码块。

1) 函数声明

在Go语言中,你可以使用func关键字来声明一个函数。函数声明包括函数名、参数列表、返回值列表和函数体。例如:

js 复制代码
func add(x int, y int) int {
    return x + y
}

在这个例子中,add是函数名,xy是参数,int是返回值类型。

2) 多返回值

Go语言支持函数有多个返回值。例如:

js 复制代码
func swap(x, y string) (string, string) {
    return y, x
}

在这个例子中,swap函数返回两个string类型的值。

3) 命名返回值

在Go语言中,你可以给返回值命名,并在函数体中直接使用这些名称。例如:

js 复制代码
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

在这个例子中,xy是命名的返回值,return语句没有明确的返回值,它会返回xy的当前值。

3. 变量声明

在Go语言中,变量的声明可以通过var关键字进行。

1) 基本声明

你可以使用var关键字后跟变量名和变量类型来声明一个变量。

js 复制代码
var x int

在这个例子中,我们声明了一个名为x的变量,它的类型是int

2) 声明并初始化

你可以在声明变量的同时给它赋一个初始值。

js 复制代码
var x int = 10

在这个例子中,我们声明了一个名为x的变量,它的类型是int,并且我们给它赋了一个初始值10

3) 类型推断

如果你在声明变量的同时给它赋了一个初始值,Go语言可以自动推断出变量的类型。

js 复制代码
var x = 10

在这个例子中,Go语言会自动推断出x的类型是int

4) 简短声明

在函数内部,你可以使用:=来替代var进行变量的声明和初始化。

js 复制代码
x := 10

在这个例子中,我们声明了一个名为x的变量,并给它赋了一个初始值10。Go语言会自动推断出x的类型是int

请注意,变量声明必须在函数内部进行,不能在包级别进行。如果在函数内部声明的变量没有被使用,会导致编译错误。

4. 控制流语句

在Go语言中,基础控制流语句主要包括条件语句(如if,else if,else),循环语句(如for),以及选择语句(如switch,select)。

1) if语句

if语句用于测试某个条件是否为真。

js 复制代码
if x > 0 {
    fmt.Println("x is positive")
}

2) if-else语句

if-else语句用于在条件为真时执行一段代码,条件为假时执行另一段代码。

js 复制代码
if x > 0 {
    fmt.Println("x is positive")
} else {
    fmt.Println("x is not positive")
}

3)if-else if-else语句

if-else if-else语句用于在多个条件中选择一个执行。

js 复制代码
if x > 0 {
    fmt.Println("x is positive")
} else if x < 0 {
    fmt.Println("x is negative")
} else {
    fmt.Println("x is zero")
}

4)for语句

for语句用于循环执行一段代码。

js 复制代码
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

若不添加for后面的循环条件则为死循环,可以在循环中添加continue来继续循环,也可以通过添加break来跳出循环

5)switch语句

switch语句用于在多个条件中选择一个执行。

js 复制代码
switch x {
case 0:
    fmt.Println("x is zero")
case 1:
    fmt.Println("x is one")
default:
    fmt.Println("x is neither zero nor one")
}

无需像c中的stiwch一样添加break来跳出,且go中的stiwch可以使用字符串,结构体等作为判断条件,以此取代部分的if-else语句

5. 数据类型

1)数组

数组是具有固定长度且元素类型相同的数据类型。例如,声明一个长度为5的整数数组:

js 复制代码
var arr [5]int

2)切片

切片是一个长度可变的数组,因其长度可变,应用也更广泛灵活。

js 复制代码
func main() {

	s := make([]string, 3)//可预先确定slice的初始长度
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
        fmt.Println(s) // [a b c]
	s = append(s, "d")//通过append命令添加新的元素
	s = append(s, "e", "f")
	fmt.Println(s) // [a b c d e f]

	c := make([]string, len(s))
	copy(c, s)//通过copy命令复制一个相同的slice
	fmt.Println(c) // [a b c d e f]

	fmt.Println(s[2:5]) // [c d e]//包含类似java的切片操作,截取部分元素
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good) // [g o o d]
}

3)映射(map)

映射是一种将唯一键映射到值的数据类型。

js 复制代码
func main() {
	m := make(map[string]int)//仍通过make命令创建map,且map需包含key值和value值
	m["one"] = 1//一一对应赋值
	m["two"] = 2
	fmt.Println(m)           // map[one:1 two:2]
	fmt.Println(len(m))      // 2
	fmt.Println(m["one"])    // 1
	fmt.Println(m["unknow"]) // 0

	r, ok := m["unknow"]//用于判断unknow是否在m中,若在则返回true
	fmt.Println(r, ok) // 0 false

	delete(m, "one")//删除kv对

	m2 := map[string]int{"one": 1, "two": 2}//定义kv对
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

go中的map是完全无序的

4)rang

它返回两个值:一个是元素的索引,另一个是该索引处的值

js 复制代码
nums := []int{2, 3, 4}
for i, num := range nums {
    fmt.Println("index:", i, "value:", num)
}//index: 0 num: 2;index: 1 num: 3;index: 2 num: 4

i为索引,num为元素,若无需索引可用_代替

若用于引导map

js 复制代码
m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) // a A ; b B
for k := range m {  
fmt.Println("key", k) // key a; key b  
}

5)指针

在Go语言中,指针是一种特殊的数据类型,它存储了另一个变量在内存中的地址,你可以通过指针访问、修改这个地址处的变量的值。

js 复制代码
func add2(n *int) {
	*n += 2
}
func main() {
	n := 5
	add2(&n)
	fmt.Println(n) // 7
}

在创建的add2函数中需在变量类型及变量前添加*,且在调用该函数时括号内的变量需要前置&以表明指向地址。

利用指针可在某些情况下减少拷贝的开销。

6)结构体

结构体(struct)是一种复合的数据类型,它可以包含零个或多个任意类型的值(称为字段)且每个字段都有一个名称和一个类型

js 复制代码
type Person struct {
    Name string
    Age  int
}

如上创建一个结构体,同时拥有string型变量Nameint型变量Age

js 复制代码
p := Person{Name: "Alice", Age: 30}
var d Person
	d.Name = "wang"
	d.Age = "24"

可通过以上方式添加实例并赋值。

js 复制代码
p.Age = 31
func checkname(u Person, Age string) bool {
	return u.Age == Age
}

也可使用上述方式修改或通过函数调用结构体中的值

注:使用bool返回值return后跟随的时判断语句故用==

7)结构体方法

方法是一种特殊类型的函数,它与特定类型(如结构体)关联

首先需要定义方法,你可以使用func关键字来定义一个方法。方法的定义与函数类似,但在函数名前面需要添加一个参数,这个参数定义了这个方法是哪个类型的。例如,为Person结构体定义一个greet方法:

js 复制代码
type Person struct {
    Name string
}

func (p Person) greet() {
    fmt.Println("Hello, my name is", p.Name)
}

在这个例子中,greet方法与Person类型关联。

然后再调用方法:你可以使用.操作符来调用结构体的方法。例如:

js 复制代码
p := Person{Name: "Alice"}
p.greet() // 输出"Hello, my name is Alice"

在这个例子中,我们调用了pgreet方法。

补充一点:在定义方法时,接收者可以是值(如上面的例子)或指针。如果你想在方法中修改接收者的值,你需要使用指针接收者。例如:

js 复制代码
func (p *Person) setName(name string) {
    p.Name = name
}

p := Person{Name: "Alice"}
p.setName("Bob")
fmt.Println(p.Name) // 输出"Bob"

这个例子中,setName方法有一个指针接收者,所以它可以修改pName字段。

二. 特性解析

1.错误处理

在Go语言中,错误处理是一种重要的编程模式,Go语言使用error类型来处理错误。

错误类型:Go语言中的error是一个内建的接口类型,它有一个方法Error() string。如果一个类型实现了这个方法,那么它就满足了error接口。

js 复制代码
type error interface {
    Error() string
}

返回错误:在函数或方法中,你可以返回一个error类型的值来表示错误。如果没有错误发生,应该返回结果和nil。

js 复制代码
func doSomething() (result string, err error) {
    // 如果发生错误,返回错误
    if somethingWrong {
        return "", errors.New("something wrong happened")
    }
    // 如果没有错误,返回结果和nil
    return "success", nil
}

处理错误:当你调用一个可能返回错误的函数或方法时,你可以通过以下函数检查返回的错误值。

js 复制代码
result, err := doSomething()
if err != nil {
    // 处理错误,例如打印错误信息
    fmt.Println(err)
    return
}
// 如果没有错误,继续执行
fmt.Println(result)

需要注意的是,在Go语言中,习惯将错误作为函数的最后一个返回值,并使用error类型来表示错误。可以使用errors.New函数创建一个新的错误值,也可以使用自定义的错误类型来表示特定的错误情况。

2. 字符串格式化

我们可以通过各种方式来让程序输出我们想要的字符格式。

js 复制代码
func main() {
	s := "hello"
	n := 123
	p := point{1, 2}
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2}

	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

	f := 3.141592653
	fmt.Println(f)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}

3.Json操作

在Go语言中,你可以使用encoding/json包来进行JSON的编码(序列化)和解码(反序列化)。

你可以使用json.Marshal函数将Go值转换为JSON

js 复制代码
type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
bytes, err := json.Marshal(p)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(bytes)) // 输出'{"Name":"Alice","Age":30}'

上述代码将Person结构体序列化并输出,要注意的是,打印bytes时要添加string的数据类型否则会输出16进制的数组,同时JSON 操作默认会将输出的开头字母转化为大写,可通过在结构体变量后添加json:"小写"来转换为小写。

同时你也可以使用json.Unmarshal函数将JSON转换为Go值。

js 复制代码
var p Person
err := json.Unmarshal([]byte(`{"Name":"Alice","Age":30}`), &p)
if err != nil {
    log.Fatal(err)
}
fmt.Println(p) // 输出'{Alice 30}'

如果你不知道JSON的结构,你可以将其解码为map[string]interface{}

js 复制代码
var m map[string]interface{}
err := json.Unmarshal([]byte(`{"Name":"Alice","Age":30}`), &m)
if err != nil {
    log.Fatal(err)
}
fmt.Println(m) // 输出'map[Name:Alice Age:30]'

4. 时间处理

在Go语言中,你可以使用time包来处理时间,并做出相关操作。

  1. 获取当前时间:你可以使用time.Now函数获取当前时间

2)创建时间:你可以使用time.Date函数创建一个特定的时间:

js 复制代码
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
  1. 时间格式化:你可以使用Time.Format方法将时间格式化为字符串。Go语言使用一种特殊的日期2006-01-02 15:04:05来表示格式:
js 复制代码
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())

4)比较两个时间的差值:

js 复制代码
diff := t2.Sub(t1)
fmt.Println(diff)  //总差值                         
fmt.Println(diff.Minutes(), diff.Seconds()) //分和秒的差值

5)获取时间戳:

js 复制代码
fmt.Println(now.Unix())

多用于系统交互。

5.并发编程

在Go语言中,你可以使用GoroutinesChannels来进行并发编程。

  1. Goroutines:你可以使用go关键字来启动一个新的Goroutine(轻量级线程)。例如:
js 复制代码
func sayHello() {
    fmt.Println("Hello, world!")
}

go sayHello()

在这个例子中,sayHello函数在一个新的Goroutine中运行。

  1. Channels:Channels是用来在Goroutines之间传递数据的。你可以使用make(chan val-type)来创建一个新的channel。例如:
js 复制代码
messages := make(chan string)

go func() { messages <- "ping" }()

msg := <-messages
fmt.Println(msg)

在这个例子中,我们创建了一个新的channel,然后在一个新的Goroutine中向这个channel发送了一个值,然后在主Goroutine中接收了这个值。

  1. Buffered Channels:默认的channels是无缓冲的,这意味着只有在接收(<-chan)方准备好接收新的数据时,才允许发送(chan<-)方发送数据,你可以在make函数的第二个参数中指定channel的缓冲大小,来创建一个有缓冲的channel。
js 复制代码
bufferedChan := make(chan int, 2)

bufferedChan <- 1
bufferedChan <- 2

fmt.Println(<-bufferedChan)
fmt.Println(<-bufferedChan)

6. 内存管理

在Go语言中,内存管理主要由垃圾回收器(Garbage Collector,GC)自动处理,你通常不需要手动进行内存分配和释放。

  1. 内存分配:当你声明变量或创建数据(如数组、切片、映射、结构体、指针等)时,Go语言会自动为你分配内存。

  2. 内存释放:当一个变量或数据不再被使用时,Go语言的垃圾回收器会自动释放它占用的内存。一个变量或数据不再被使用的条件是它不再被任何其他变量或数据引用。

  3. 垃圾回收:Go语言的垃圾回收器会定期运行,查找并释放不再被使用的内存。你无需手动触发垃圾回收,但如果需要,可以调用runtime.GC()函数来手动触发垃圾回收。

7. 包管理

在Go语言中,包管理主要通过Go Modules进行。Go Modules是Go 1.11及更高版本中的官方包管理解决方案。 对包的操作通常有以下几种:

  1. 初始化模块:你可以使用go mod init命令在你的项目目录中初始化一个新的模块:
js 复制代码
go mod init github.com/yourusername/yourproject
  1. 添加依赖:当你在代码中导入一个新的包并运行go buildgo test或其他一些命令时,Go会自动查找并添加你的依赖到go.mod文件,并下载依赖到本地的pkg/mod目录。

  2. 升级和降级依赖:你可以使用go get命令来升级或降级你的依赖。例如,升级到最新版本:

js 复制代码
go get github.com/some/dependency@latest

或者降级到特定版本:

js 复制代码
go get github.com/some/dependency@v1.0.0
  1. 清理依赖:你可以使用go mod tidy命令来移除不再需要的依赖。

三. 总结

本文章主要简单介绍了go语言的基础语法及通用特性,旨在帮助该语言的初学者了解和掌握一些基本内容,并能够编译简单的go程序,以此来打开通向高阶语法及相关知识的大门。 对于Go语言的初学者,以下是一些推荐的学习资源:

  1. Go官方文档:Go语言的官方文档是学习Go语言的最权威的资源。你可以在这里找到关于语言规范、标准库和Go工具的详细信息。(golang.org/doc/

  2. Go by Example:这是一个很好的在线教程,通过实例来教授Go语言的各个方面。(gobyexample.com/

  3. The Go Programming Language:这本书由Go语言的设计者之一Alan Donovan和Brian Kernighan共同撰写,是学习Go语言的经典书籍。

  4. Go Web编程:如果你对使用Go进行Web开发感兴趣,这本书是一个很好的资源。

  5. Go语言圣经:这是一本在线的免费书籍,详细介绍了Go语言的各个方面。(gopl.io/

  6. 在线课程:网站如Coursera、Udemy、edX等提供了一些Go语言的在线课程。

  7. Go语言社区:Go语言有一个活跃的社区,你可以在GitHub、Stack Overflow、Reddit等地方找到许多有用的资源和帮助。

相关推荐
hlsd#39 分钟前
go mod 依赖管理
开发语言·后端·golang
陈大爷(有低保)43 分钟前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、44 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头1 小时前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
阿华的代码王国1 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
小码编匠1 小时前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计
德育处主任Pro2 小时前
『Django』APIView基于类的用法
后端·python·django
哎呦没4 小时前
SpringBoot框架下的资产管理自动化
java·spring boot·后端