作者:E4b9a6, 创建:2019-12-28, 字数:6341, 已阅:27, 最后更新:2019-12-28
Go(通常称为 Golang)是一种开源编程语言,由 Google 开发,设计目标是简单、高效和可并发,由 Robert Griesemer、Rob Pike 和 Ken Thompson 于 2007 年开始开发,并于 2009 年正式发布 Go常用于网络、系统、并发、分布式编程,采用Go的大型项目包括了 Docker、Kubernetes、Prometheus、Terraform 等,Go语言的主要特性是简洁、并发支持、静态类型、垃圾回收、工具链
GO的基础类型包括 Bool
、String
、int/int8/int16/int32/int64
、uint/uint8(byte)/uint16/uint32/uint64
、float32/float64
、complex64/complex128
等,变量的命名规则遵循骆驼命名法,如 userName
float32精确到小数点后7位,float64精确到小数点后64位,所以使用==和!=比较float类型时应注意
变量声明:
// 单变量声明
var userName string = "chancel"
// 多个变量声明
var (
userName string = "chancel"
userAge int = 10
userHistory = []float32 {0.01,0.2}
getName func() string
userFamily struct {
fatherName string
}
)
Go语言有匿名变量,通常采用关键字_
作为标识,_
变量不占用内存空间也不分配内存
package main
import (
"fmt"
)
func getData() (int, int) {
return 1, 2
}
func main() {
var a, _ = getData()
fmt.Println("a value is ", a)
}
指针运算符是Go语言被归入C语言家族的重要原因,一个指针是指向一个变量值的内存地址变量
一个内存地址用一个操作系统原生字(native word)来存储, 一个原生字在32位操作系统上占4个字节,在64位操作系统上占8个字节
在Go里,一个指针的形式为*T,类型T被成为指针类型的base type,指针通常缩写为Ptr
指针的声明如下,通常采用前面一种(无名指针类型)
*int
type Ptr *int
指针的用途,下面这个例子非常好
package main
import "fmt"
func double(x *int) {
*x += *x
x = nil
}
func main() {
var a = 3
double(&a)
fmt.Println(a) // 6
p := &a
double(p)
fmt.Println(a, p == nil) // 12 false
}
Go语言对指针做了很多限制,例子如下
package main
import "fmt"
func main() {
a := int64(5)
p := &a
// 下面这两行编译不通过 /*
p++
p = (&a) + 8
*/
*p++
fmt.Println(*p, a) // 6 6
fmt.Println(p == &a) // true
*&a++
*&*&a++
**&p++
*&*p++
fmt.Println(*p, a) // 10 10
}
在Go中,普通数组是固定长度,无法动态添加元素的
声明一个没有初始值数组 array_1
和一个拥有初始值的数组 array_2
代码如下
package main
import "fmt"
func main() {
var array_1 [5]int
fmt.Println(array_1)
var array_2 =[5]int{1,2,3,4,5}
for k,v := range array_2{
fmt.Println("Key:",k,"Value:",v)
}
}
// 输出如下
➜ go run main.go
[0 0 0 0 0]
Key: 0 Value: 1
Key: 1 Value: 2
Key: 2 Value: 3
Key: 3 Value: 4
Key: 4 Value: 5
固定数组的使用场景较少,我们经常需要更改数组的内容,即可变数组
可变数组的用途非常广泛,在Go中可以借助 Slice(切片) 来实现可变数组
切片的常见使用如下
package main
import (
"fmt"
"sort"
)
func main() {
var array_1 [5]int
fmt.Println(array_1)
var array_2 =[5]int{3,5,7,8,1}
for k,v := range array_2{
fmt.Println("Key:",k,"Value:",v)
}
// 将数组arrya_2转换为切片
var slice_1 = array_2[:]
// 注意切片取范围是半开区间,前闭后开,与Python类似
fmt.Println(slice_1[1:4])
// 追加元素,注意cap和len的长度变化导致的指针变化
fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])
slice_1 = append(slice_1,4,10,2)
fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])
slice_1 = append(slice_1,9,6)
fmt.Println("slice length:",len(slice_1),"cap:",cap(slice_1),"ptr:",&slice_1[0])
// 对元素进行排序
fmt.Println("slice content:",slice_1)
sort.Ints(slice_1)
fmt.Println("slice content:",slice_1)
}
// 输出如下
➜ go-algorithm-demo go run main.go
[0 0 0 0 0]
Key: 0 Value: 3
Key: 1 Value: 5
Key: 2 Value: 7
Key: 3 Value: 8
Key: 4 Value: 1
[5 7 8]
slice length: 5 cap: 5 ptr: 0xc0000a2090
slice length: 8 cap: 10 ptr: 0xc0000b6000
slice length: 10 cap: 10 ptr: 0xc0000b6000
slice content: [3 5 7 8 1 4 10 2 9 6]
slice content: [1 2 3 4 5 6 7 8 9 10]
可以从输出中看到,当cap变化的时候,切片的内存地址就会发生改变
切片的cap与length的区别在于cap是空间长度,如添加元素时cap长度不足则Go会采用以下公式来进行扩容
var newSclice = make(int[],len(oldSlice),2*len(oldSlice)+1)
Go语言的字典是哈希结构的,key的类型有限制,必须是可以直接比较的类型(Slice/Func/map等不行),Vlaue的类型则没有任何限制
借助value类型为slice的字典可实现Python语言的dict类型的效果
常见使用如下,与其他语言的字典区别并不太大
package main
import "fmt"
func main() {
var map_1 = map[int]string {1:"liming",2:"zhangsan"}
fmt.Println(map_1)
// map可以自动扩容
map_1[0] = "zijia"
map_1[3] = "momota"
map_1[4] = "lindan"
// map遍历
for k,v := range map_1{
fmt.Println("key",k,"value",v)
}
// map查找
var value_1,isExist_1 = map_1[4]
fmt.Println("index 4 vlaue exist is",isExist_1,",value is",value_1)
// 删除元素
delete(map_1,4)
var value_2,isExist_2 = map_1[4]
fmt.Println("index 4 vlaue exist is",isExist_2,",value is",value_2)
}
Go的接口是支持面向对象编程(OOP)的重要标志,与其他语言的接口不一样的是
接口的语法代码如下
//接口定义
type Shape interface {
Area() float64 //求面积
}
//圆形定义
type Circle struct {
radius float64
}
//圆形面积计算
func (this Circle) Area() float64 {
return math.Pi * this.radius * this.radius
}
//矩形定义
type Rectangle struct {
length float64
width float64
}
//矩形面积计算
func (this Rectangle) Area() float64 {
return this.length * this.width
}
func main() {
var circle Circle
circle.radius = 3
fmt.Println(circle.Area())
rec := Rectangle{5.0, 8}
fmt.Println(rec.Area())
}
方法与函数是类似的,区别在于方法是一类带有接受者(也就是实例)的特殊函数
所以在Go语言里面,方法可以看成是带有类型的函数
a Go method is a function that acts onvariable of a certain type, called the receiver. So a method is a specialkindof function ——《The Way to Go》
defer是延迟调用,执行方式是先进后出,常用于释放资源如关闭文件写入,关闭数据库连接等
与Python中的with语法较为相似
代码例子如下
package main
import (
"fmt"
)
func main() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
// 执行输出如下
➜ go run main.go
4
3
2
1
0
Go语言作为强类型语言,处理Json不如脚本语言方便
在处理Json上,Go采用了标签标识的方法来解决json字段与结构体字段对应的问题
如下面代码所示,其`json:"username"`
就是标签标识
除了标识对应关系外,还可以添加例如omitempty来标识序列化时忽略0值或Nil值
代码如下
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"username"`
Age int `json:"age,string"`
Gender int `json:"gender"`
Score int `json:"score,omitempty"`
}
func main() {
var data string = `{ "username":"chancel", "age":"18", "gender":0, "Score":666 }`
var s = &Student{}
var err = json.Unmarshal([]byte(data), s)
fmt.Println(err)
fmt.Println(s)
s.Score = 0
var jsonData, _ = json.Marshal(s)
fmt.Println(string(jsonData))
}
// 输出如下
➜ go run main.go
<nil>
&{chancel 18 0 666}
{"username":"chancel","age":"18","gender":0}
资料引用