首先简单介绍下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环境中已有的包)。
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
以上片段中导入了包fmt
并使用了其函数fmt.Println
打印了字段"Hello,world!
"
import (
"fmt"
"math/rand"
"strings"
)
你也可以像这样一次导入多个包。
3) 自定义包
import (
"fmt"
rand "math/rand"
)
像这样自定义包"math/rand
"的名为rand
,在使用时即可将rand作为前缀调用"math/rand
"中的函数
4) 创建包
首先,你需要创建一个新的目录来存放你的包,包的名称通常与目录名相同。然后,在该目录中创建一个新的.go文件,在文件的第一行声明包的名称。例如,你可以创建一个名为mypackage
的包,它有一个hello.go文件:
// hello.go
package mypackage
import "fmt"
func Hello() {
fmt.Println("Hello, world!")
}
然后,你可以在你的主程序中导入并使用这个包。你需要使用包的完整路径(相对于GOPATH/src或Go Modules的路径)。例如:
// 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
如下
import "github.com/yourusername/yourrepository/yourpackage"
并通过自定义简化
import yp "github.com/yourusername/yourrepository/yourpackage"
2. 函数的定义
在Go语言中,func关键字用于声明函数。函数是一段可以被重复调用的代码块。
1) 函数声明
在Go语言中,你可以使用func关键字来声明一个函数。函数声明包括函数名、参数列表、返回值列表和函数体。例如:
func add(x int, y int) int {
return x + y
}
在这个例子中,add
是函数名,x
和y
是参数,int
是返回值类型。
2) 多返回值
Go语言支持函数有多个返回值。例如:
func swap(x, y string) (string, string) {
return y, x
}
在这个例子中,swap
函数返回两个string
类型的值。
3) 命名返回值
在Go语言中,你可以给返回值命名,并在函数体中直接使用这些名称。例如:
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
在这个例子中,x
和y
是命名的返回值,return
语句没有明确的返回值,它会返回x
和y
的当前值。
3. 变量声明
在Go语言中,变量的声明可以通过var关键字进行。
1) 基本声明
你可以使用var关键字后跟变量名和变量类型来声明一个变量。
var x int
在这个例子中,我们声明了一个名为x
的变量,它的类型是int
。
2) 声明并初始化
你可以在声明变量的同时给它赋一个初始值。
var x int = 10
在这个例子中,我们声明了一个名为x
的变量,它的类型是int
,并且我们给它赋了一个初始值10
。
3) 类型推断
如果你在声明变量的同时给它赋了一个初始值,Go语言可以自动推断出变量的类型。
var x = 10
在这个例子中,Go语言会自动推断出x
的类型是int
。
4) 简短声明
在函数内部,你可以使用:=来替代var进行变量的声明和初始化。
x := 10
在这个例子中,我们声明了一个名为x
的变量,并给它赋了一个初始值10
。Go语言会自动推断出x
的类型是int
。
请注意,变量声明必须在函数内部进行,不能在包级别进行。如果在函数内部声明的变量没有被使用,会导致编译错误。
4. 控制流语句
在Go语言中,基础控制流语句主要包括条件语句(如if,else if,else),循环语句(如for),以及选择语句(如switch,select)。
1) if语句
if语句用于测试某个条件是否为真。
if x > 0 {
fmt.Println("x is positive")
}
2) if-else语句
if-else语句用于在条件为真时执行一段代码,条件为假时执行另一段代码。
if x > 0 {
fmt.Println("x is positive")
} else {
fmt.Println("x is not positive")
}
3)if-else if-else语句
if-else if-else语句用于在多个条件中选择一个执行。
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语句用于循环执行一段代码。
for i := 0; i < 10; i++ {
fmt.Println(i)
}
若不添加for
后面的循环条件则为死循环,可以在循环中添加continue
来继续循环,也可以通过添加break
来跳出循环
5)switch语句
switch语句用于在多个条件中选择一个执行。
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的整数数组:
var arr [5]int
2)切片
切片是一个长度可变的数组,因其长度可变,应用也更广泛灵活。
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)
映射是一种将唯一键映射到值的数据类型。
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
它返回两个值:一个是元素的索引,另一个是该索引处的值
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
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语言中,指针是一种特殊的数据类型,它存储了另一个变量在内存中的地址,你可以通过指针访问、修改这个地址处的变量的值。
func add2(n *int) {
*n += 2
}
func main() {
n := 5
add2(&n)
fmt.Println(n) // 7
}
在创建的add2函数中需在变量类型及变量前添加*,且在调用该函数时括号内的变量需要前置&以表明指向地址。
利用指针可在某些情况下减少拷贝的开销。
6)结构体
结构体(struct)是一种复合的数据类型,它可以包含零个或多个任意类型的值(称为字段)且每个字段都有一个名称和一个类型
type Person struct {
Name string
Age int
}
如上创建一个结构体,同时拥有string
型变量Name
和int
型变量Age
p := Person{Name: "Alice", Age: 30}
var d Person
d.Name = "wang"
d.Age = "24"
可通过以上方式添加实例并赋值。
p.Age = 31
func checkname(u Person, Age string) bool {
return u.Age == Age
}
也可使用上述方式修改或通过函数调用结构体中的值
注:使用bool
返回值return
后跟随的时判断语句故用==
7)结构体方法
方法是一种特殊类型的函数,它与特定类型(如结构体)关联
首先需要定义方法,你可以使用func
关键字来定义一个方法。方法的定义与函数类似,但在函数名前面需要添加一个参数,这个参数定义了这个方法是哪个类型的。例如,为Person
结构体定义一个greet
方法:
type Person struct {
Name string
}
func (p Person) greet() {
fmt.Println("Hello, my name is", p.Name)
}
在这个例子中,greet
方法与Person
类型关联。
然后再调用方法:你可以使用.操作符来调用结构体的方法。例如:
p := Person{Name: "Alice"}
p.greet() // 输出"Hello, my name is Alice"
在这个例子中,我们调用了p
的greet
方法。
补充一点:在定义方法时,接收者可以是值(如上面的例子)或指针。如果你想在方法中修改接收者的值,你需要使用指针接收者。例如:
func (p *Person) setName(name string) {
p.Name = name
}
p := Person{Name: "Alice"}
p.setName("Bob")
fmt.Println(p.Name) // 输出"Bob"
这个例子中,setName
方法有一个指针接收者,所以它可以修改p
的Name
字段。
二. 特性解析
1.错误处理
在Go语言中,错误处理是一种重要的编程模式,Go语言使用error
类型来处理错误。
错误类型:Go语言中的error
是一个内建的接口类型,它有一个方法Error() string
。如果一个类型实现了这个方法,那么它就满足了error
接口。
type error interface {
Error() string
}
返回错误:在函数或方法中,你可以返回一个error类型的值来表示错误。如果没有错误发生,应该返回结果和nil。
func doSomething() (result string, err error) {
// 如果发生错误,返回错误
if somethingWrong {
return "", errors.New("something wrong happened")
}
// 如果没有错误,返回结果和nil
return "success", nil
}
处理错误:当你调用一个可能返回错误的函数或方法时,你可以通过以下函数检查返回的错误值。
result, err := doSomething()
if err != nil {
// 处理错误,例如打印错误信息
fmt.Println(err)
return
}
// 如果没有错误,继续执行
fmt.Println(result)
需要注意的是,在Go语言中,习惯将错误作为函数的最后一个返回值,并使用error
类型来表示错误。可以使用errors.New
函数创建一个新的错误值,也可以使用自定义的错误类型来表示特定的错误情况。
2. 字符串格式化
我们可以通过各种方式来让程序输出我们想要的字符格式。
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。
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值。
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{}
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函数创建一个特定的时间:
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
3) 时间格式化:你可以使用Time.Format方法将时间格式化为字符串。Go语言使用一种特殊的日期2006-01-02 15:04:05来表示格式:
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())
4)比较两个时间的差值:
diff := t2.Sub(t1)
fmt.Println(diff) //总差值
fmt.Println(diff.Minutes(), diff.Seconds()) //分和秒的差值
5)获取时间戳:
fmt.Println(now.Unix())
多用于系统交互。
5.并发编程
在Go语言中,你可以使用Goroutines和Channels来进行并发编程。
1. Goroutines:你可以使用go关键字来启动一个新的Goroutine(轻量级线程)。例如:
func sayHello() {
fmt.Println("Hello, world!")
}
go sayHello()
在这个例子中,sayHello函数在一个新的Goroutine中运行。
2. Channels:Channels是用来在Goroutines之间传递数据的。你可以使用make(chan val-type)
来创建一个新的channel。例如:
messages := make(chan string)
go func() { messages