Google Go学习笔记
命名规约:
- GO采用驼峰命名方式。
- GO没有public | private 等关键词,凡是小写变量(首字母)均为包内可见。
- GO没有public | private 等关键词,凡是大写变量(首字母)均为public类型。
一些有意思的地方:
- GO语言没有使用;(分号)作为一个语句的结束。
- GO语言语句的结束以“回车”代替。
- GO语言的{}没有类似Java的多变形式,GO语言只允许一种形式,即:
//正确并唯一的方式:
func test() {
//TO DO
}
//错误的方式1:
func test() { }
//错误的方式2:
func test()
{
//TO DO
}
- GO语言默认有两个方法,init() 和 main(),其中main()必须要跟package main放在一起使用,如:
package main
import "fmt"
func main() {
fm.Println("hello world!")
}
- 分组声明:
import (
"fmt"
"os"
)
var (
i int = 0
j string = “test”
)
const (
Pi = 3.1415926
)
- GO语言内置了三种预定义变量:true、false、iota
变量类型:(简单数据类型)
- 布尔类型:bool
- 整型:int8, byte, int16, int, uint, uintptr
- 浮点类型:float16, float32
- 复数类型:complex64, complex128
- 字符串类型:string
- 错误类型:error
- 字符类型:rune
变量类型:(复合数据类型)
- 指针(pointer)
- 数组(array)
- 切片(slice)
- 字典(map)
- 结构体(struct)
- 接口(interface)
- 通道(chan)
变量命名:
GO语言一般采用如下三种变量命名方式:
//方式1:
var i int
i = 100
//方式2:(无类型,有被赋的值来决定其变量类型)
var i = 100
//方式3:(省略var关键字方式)
i := 100
//简化方式1:
var i, j, k int
//简化方式2:
var i, j, k int = 1, 2, 3
//简化方式1:
i, j, k := 1, 2, 3
//简化方式3:
var (
i int
j float32
k string
}
//匿名变量
func test() (i,j,k int) {
i, j, k := 1, 2, 3
return
}
_, a, _ = test()
常量命名:
字符串:
枚举类型:
数组、切片、字典:
数组:
- 声明:
//方式1:
var arr [3]int
//方式2:
a := arr [3]int{1, 2, 3}
//方式3:(声明了长度为5的数组,但只赋值了前三个变量)
b := arr [5]int{1, 2, 3}
//方式4:(根据赋值的数量决定数组的长度)
c := arr [...]int{1, 2, 3}
- 数组的长度不可变。
- 数组是值类型变量。意味着以array作为参数的时候,每次操作都会产生一个array的副本。
- 数组长度的取得:len(arr)
- 使用for来遍历数组,如:
arr := [...]int{1,2,3,4,5}
for i := 0; i < len(arr); i++ {
fm.Print("Array Element is " + arr[i])
}
- 使用rang来遍历数组,如:
arr := [...]int{1,2,3,4,5}
for i, v := range arr {
fm.Println("Arrat Element [", i "]=", v)
}
- make不能用于数组的创建。
切片类型:
- 声明:
var slice []int //注意这里并没有在[]里面声明长度,这是与array在声明上的区别
- 切片可以直接创建,也可从已知的数组或者切片中创建,其获取方式:array[i:j] 或者 slice[i:j]
> * i:开始位置(从0开始)
> * j:结束位置(但不包含array[j],它的长度是j-i)
- 切片是一个引用类型(这是与array又一区别)
arr := [5]int{1,2,3,4,5}
var slice = []int
slice = arr[1:3]
//slice = 2,3,即包含arr[1] arr[2]
//几种特殊的[i:j]
slice := arr[:] //基于arr的全部元素生成切片
slice := arr[:3] //基于arr的前五个元素生成切片
slice := arr[3:] //从arr的第五个元素开始直到最后一个元素生成切片。
- 切片的取得:
> * len(slice) //取得当前slice存在的元素个数。
> * cap(slice) //取得当前slice全部的元素个数。
- 切片的组合,使用append关键字,如:
slice1 := []int{1,2,3,4,5}
i,j,k := 6, 7, 8
slice2 := make([]int,3)
append(slice1, i, j, k ) //slice1 = 1 2 3 4 5 6 7 8
append(slice1, slice2...) //slice1 = 1 2 3 4 5 0 0 0
- 当append的长度大于cap,会自动分配长度。
- 切片的复制,使用内置关键字:copy,可以用于长度不一致的两个切片的复制,如:
slice1 := []int{1,2,3,4,5}
slice2 := []int{6,7,8}
copy(slice1, slice2) //6 7 8 4 5
copy(slice2, slice1) //1 2 3
####字典(map):
- 声明:
var dict = map[int]string //声明一个map类型的变量dict,其中key是int类型,value是string类型
- map是一组无序的key = value的对应关系。
- 与slice一样可以使用make关键字创建。即:dict := make(map[int]string , 3)
- map的查找:
dict := make(map[int]string)
dict[0] = "world"
dict[1] = "hello"
fmt.Println(dict[0]) //world
value, ok := dict[0]
if ok {
fmt.Println("search ok, value = ", value )
}
value, ok = dict[2] //注意:由于之前的value与ok已经定义,所以这里不能用:=方式
if ok {
fmt.Println("search ok, value = ", value )
} else {
fmt.Println("search error")
}
- map的遍历:(与切片的方式一致)
dict := make(map[int]string)
dict[0] = "world"
dict[1] = "hello"
for k, v := range dict {
fmt.Println("dict[", k, "]=", v)
}
make用法:
条件语句:
if:
- 与其他语言相比,少了(),并且强制if else的格式,如:
if x {
//TO DO
} else {
//TO DO
}
- 可以在if中定义变量,此变量只能作用于该if条件块,如:
func add(a,b int) int {
return a + b
}
if x := add(1,2); x > 0 {
fmt.Println("x is ", x)
}
switch:
- 与if一样,省略了()
- 与其他语言相比,省略了break。
- case分支可以使用判断条件。
- 内置关键字:fallthrough(强制执行下一个case,类似有break的语言中没有写break的效果),如:
i := 1
switch i {
case 0:
fmt.Println("i is", i )
case 1:
fmt.Println("i is", i )
case 2,3,4:
fmt.Println("i is", i )
fallthrough
case i == 0:
fmt.Println("i is", i )
default:
fmt.Println("i is", i )
}
- switch后面的变量也可以省略,如:
//如这样使用的话,switch后面的变量必须要省略
i := 1
switch {
case i == 1:
fmt.Println("i is ", i)
default:
fmt.Println("i is ", i)
}
for:
函数:
- 使用func关键字。
- 函数也是一种(特殊)的变量。
- 允许有多个参数,如:
func test()
func test(a int, b string)
func test(a,b int)
- 允许有多个返回值,如:
func test(a,b int) int {
return a + b
}
func test(a,b int) (c,d int) {
c = a + b
d = a - b
return
}
func test(a,b int)(int, int) {
return a + b, a - b
}
- 可变参数,使用关键字...<类型>,如:
func test(a,b int, args ...int)
//调用
test(1, 2)
test(1, 2, 3)
test(1, 2, 3, 4, 5)
- 可变参数,可以传递任意类型的参数,使用关键字...interface{},如:
func test(a, b int, args ...interface{})
//调用
test(1, 2)
test(1, 2, 3)
test(1, 2, 3, 4, 5, "a", "b", true)
test(1, 2, "a", "b", "c", 3, 4, 5, 6, true, 1 < 2)
- 可变参数本质上是个切片(slice),所以具有slice的一切特性,如可以用range,append、copy等操作。
- 匿名函数,不加函数名,如:
f := func (a,b int) int {
return a + b
}
//使用:
c := f(1, 2)
- 函数作为值类型使用:
package main
import "fmt"
//
type testInt func(int) bool // 声明了一个函数类型
//
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
//
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// 声明的函数类型在这个地方当做了一个参数
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // 函数当做值来传递了
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven) // 函数当做值来传递了
fmt.Println("Even elements of slice are: ", even)
}
- 特殊的两个方法(保留函数):init()和main()
- 在定义时不能有参数、不能用返回值
- init()是可选的
- main()必须要跟package main在一起使用
- GO语言会自动调用init()和main(),无需手动调用。
- init()和main()的调用流程
> 
- 函数的导入,使用import方式,如:
//方式1:
import "fmt"
import "os"
//方式2:
import (
"fmt"
"os"
)
//方式3:(相对路径)
import "./xxx" //当前目录下xxx文件夹,不建议采用这方式
//方式4:(绝对路径)
import "xxx/yyy" //加载$GOPATH/src/xxxx/yyyy模块
//方式5:(.操作)
import (
. "fmt" //省略fmt前缀,如在调用时直接使用 Println()
)
//方式6:(别名操作)
import (
f "fmt" //直接使用f.Println
)
//方式7:(_操作)
import (
_ "github.com/ziutek/mymysql/godrv" //调用此包中的init函数
)
- defer(延迟),一般多用作出错处理的关闭操作,某些程度上类似与try...catch...finial,如:
file, err := os.Open()
if err != nil {
//TO DO
return
}
defer file.Close()
- 可以多次使用defer,defer采用后进先出模式,即后面定义的defer要先执行。
指针:
- GO语言的函数参数传递是传入参数的副本(copy),如:
func add1(a, b int) {
a = a + b
fmt.Println("func add1's a = ", a) //func add1's a = 3
}
a, b := 1, 2
add1(a, b)
fmt.Println("a = ", a) //a = 1
- 可以使用指针的方式来避免产生副本的方式。
- GO语言的指针定义方式:*<类型>,如a *int b *string
- GO语言的指针使用方式:*a + *b
- a的地址:&a
- *用于操作;&用于传址。
- 取地址(&)再传递指针(*)操作,如:
func add2(a, b *int) {
*a = *a + *b
fmt.Println("func add1's *a = ", *a) //func add1's *a = 3
}
a, b := 1, 2
add2(&a, &b) //&a = 3
- string、slice、map本质上就是指针类型,因而在操作的时候不需要转换为指针类型。
类型系统:
- 基本类型,int, float32, bool等。
- 复合类型,array, slice, map等。
- 可以指向任意对象的类型(Any类型),定义为:interface{}
- 值语义和引用语义。
- 面向对象。
- 接口。
type:
- 可以为任意类型添加方法(除指针类型外)
- 声明:
//声明
type A int
//定义
func (a A) add (b A) A {
return a + b
}
//使用
var i, j A = 1, 2
c := i.add(j)
fmt.Println("c = ", c)
struct(结构体):
- GO语言并没有传统的OOP等概念,要实现一个Class,在GO语言中使用struct方式。
- 声明:
type Rect struct {
width float32
height float32
}
- 使用:
//方式1:
var rect = new(Rect)
rect.width = 100
rect.height = 100
//方式2:(与方式1一样)
var rect = Rect{}
rect.width = 100
rect.height = 100
//方式3:(初期化时就赋值)
var rect = Rect{100, 100}
//方式4:(使用key : value的方式,所以不需要按照Rect定义的顺序赋值)
var rect = Rect{ height : 100, width : 100 }
- “类方法”:
//声明
func (r Rect ) Area() float32 {
return r.width * r.height
}
//使用:
r := Rect{100, 100}
area := r.Area()
fm.Println("area = ", area)
- 类继承:(匿名字段)
//声明
type Column struct {
Rect
length float32
}
//类方法
func (c Column) Volume() float32 {
//return c.Rect.Area() * c.length
return c.Rect.width * c.Rect.height * c.length
}
//使用
r := Rect{100, 100}
c := Column{r, 100}
vol := c.Volume()
fmt.Println("volume = ", vol)
- 方法的继承:
c.Area() //Column继承于Rect,因此也具有Rect的成员方法
fm.Println("area = ", c.Area())
- 方法的覆盖:
//定义
func (c Column) Area() float32 {
return c.Rect.width * 200
}
//使用
r := Rect{100,100}
c := Column{r,2}
r.Area() //调用了Rect的类方法Area()
fmt.Println("r.Area()", r.Area()) //10000
c.Area() //调用了Column的类方法Area()
fmt.Println("r.Area()", r.Area()) //20000
- 匿名字段不仅能适用于struct,它可以适用与任何类型,如:
//声明
type Column struct {
Rect
length float32
int
}
//使用
r := Rect{100, 100}
c := Column{Rect : r, length : 100}
//修改匿名字段(int)
c.int = 9
fmt.Println("c.int = ", c.int)
- 当继承(匿名函数)的字段出现重复时,GO语言采用最外层优先,如:
package main
import "fmt"
//
type Human struct {
name string
age int
phone string // Human类型拥有的字段
}
//
type Employee struct {
Human // 匿名字段Human
speciality string
phone string // 雇员的phone字段
}
//
func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
fmt.Println("Bob's work phone is:", Bob.phone)
// 如果我们要访问Human的phone字段
fmt.Println("Bob's personal phone is:", Bob.Human.phone)
}
interface:
- 声明:
type IObjc interface {
Area()
}
- 实现:
type Rect struct {
width float32
height float32
}
func (r Rect) Area() {
area := r.width * r.height
fmt.Println("Rect's Area = ", area)
}
type Column struct {
Rect
length float32
}
func (c Column) Area() {
area := c.Rect.width * c.Rect.height
fmt.Println("Column's Area = ", area)
}
//使用
r := Rect{100, 100}
c := Column{Rect{100, 200}, 2}
var objc IObjc
objc = r
objc.Area() //Rect's Area = 10000
objc = c
objc.Area() //Column's Area = 20000