没有”对象”,没有继承多态,没有泛型,没有try/catch
有接口,函数式编程,CSP并发模型(goroutine+channel)
基础语法 变量定义 Go语言没有全局变量的说法,定义在包内(函数外面)的变量叫包内变量
不赋初值,默认int是0,string是空串
不写type,Go也可以自动推断类型
简写,函数内可以把var用 := 代替,用来定义变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package mainimport "fmt" var aa = 3 var ss = "kkk" var bb = true var ( aaa = 33 sss = "kkkk" bbb = true ) func variableZeroValue () { var a int var s string fmt.Println(a, s) } func variableInitialValue () { var a, b int = 3 , 4 var s string = "abc" fmt.Println(a, b, s) } func variableTypeDeduction () { var a, b, c, s = 3 , 4 , true , "def" fmt.Println(a, b, c, s) } func variableShorter () { a, b, c, s := 3 , 4 , true , "def" b = 5 fmt.Println(a, b, c, s) } func main () { fmt.Println("Hello World" ) variableZeroValue() variableInitialValue() variableTypeDeduction() variableShorter() fmt.Println(aa, ss, bb) }
内建变量类型 布尔类型8位,string类型string是8位字节的集合
bool,string
加u就是无符号整数,不加就是有符号整数;一种是规定长度的,一种是不规定长度的,不规定长度的int,其长度根据操作系统来,操作系统是多少位,他的长度就是多少位
(u)int,(u)int8,(u)int16,(u)int32,(u)int64
uintptr是指针,指针的长度也是根据操作系统来的
uintptr
byte是8位的;rune是字符型,长度是32位的,也就是其他语言的char
byte,rune
浮点数类型,复数类型
float32,float64,complex64,complex128
验证欧拉公式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "fmt" "math" "math/cmplx" ) func euler () { fmt.Println(cmplx.Pow(math.E, 1i * math.Pi) + 1 ) fmt.Println(cmplx.Exp(1i * math.Pi) + 1 ) } func main () { c := 3 + 4i fmt.Println(cmplx.Abs(c)) euler() }
强制类型转换 go语言类型转换是强制的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport ( "fmt" "math" ) func triangle () { var a, b int = 3 , 4 var c int c = int (math.Sqrt(float64 (a * a + b * b))) fmt.Println(c) } func main () { triangle() }
常量 常量一定要赋值
用const定义常量,而且go语言常量命名不用大写const filename string
const数值可作为各种类型使用
const a, b = 3, 4,又可以是float又可以是int
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport ( "fmt" "math" ) func consts () { const filename string = "abc.txt" const a, b = 3 , 4 var c int c = int (math.Sqrt(a * a + b * b)) fmt.Println(filename, c) } func main () { consts() }
iota自增 一组const的定义,可以用iota表示自增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func enums () { const ( cpp = iota java python golang ) fmt.Println(cpp, java, python, golang) } func main () { enums() }
iota还可以作为自增值的种子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func enums () { const ( b = 1 << (10 * iota ) kb mb gb tb ) fmt.Println(b, kb, mb, gb, tb) } func main () { enums() }
枚举 go没有枚举类型,用常量表示枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func enums () { const ( cpp = 0 java = 1 python = 2 golang = 3 ) fmt.Println(cpp, java, python, golang) } func main () { enums() }
条件语句 if/else 第一种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport ( "fmt" "io/ioutil" ) func main () { const filename = "abc.txt" contents, err := ioutil.ReadFile(filename) if err != nil { fmt.Println(err) } else { fmt.Printf("%s\n" , contents) } }
第二种方式:
if的条件里可以赋值
if的条件里赋值的变量作用域就在这个if语句里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "fmt" "io/ioutil" ) func main () { const filename = "abc.txt" if contents, err := ioutil.ReadFile(filename); err != nil { fmt.Println(err) } else { fmt.Printf("%s\n" , contents) } }
switch switch会自动break,除非使用fallthrough
switch后可以没有表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport "fmt" func grade (score int ) string { g := "" switch { case score < 0 || score > 100 : panic (fmt.Sprintf( "Wrong score: %d" , score)) case score < 60 : g = "F" case score < 80 : g = "C" case score < 90 : g = "B" case score <= 100 : g = "A" } return g } func main () { fmt.Println( grade(0 ), grade(59 ), grade(60 ), grade(82 ), grade(99 ), grade(100 ), grade(101 ), ) }
for 省略起始条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" "strconv" ) func convertToBin (n int ) string { result := "" for ; n > 0 ; n /= 2 { lsb := n % 2 result = strconv.Itoa(lsb) + result } return result } func main () { fmt.Println( convertToBin(5 ), convertToBin(13 ), ) }
省略递增条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "bufio" "fmt" "os" ) func printFile (filename string ) { file, err := os.Open(filename) if err != nil { panic (err) } scanner := bufio.NewScanner(file) for scanner.Scan() { fmt.Println(scanner.Text()) } } func main () { printFile("abc.txt" ) }
省略终止条件
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func forever () { for { fmt.Println("abc" ) } } func main () { forever() }
函数 多个返回值 1 2 3 4 5 6 7 8 9 10 11 package mainimport "fmt" func div (a, b int ) (q, r int ) { return a / b, a % b } func main () { fmt.Println(div(13 , 3 )) }
多个返回值通常用于返回error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport "fmt" func eval (a, b int , op string ) (int , error ) { switch op { case "+" : return a + b, nil case "-" : return a - b, nil case "*" : return a * b, nil case "/" : q, _ := div(a, b) return q, nil default : return 0 , fmt.Errorf("unsupported operation: %s" , op) } } func div (a, b int ) (q, r int ) { return a / b, a % b } func main () { fmt.Println(eval(3 , 4 , "x" )) fmt.Println(div(13 , 3 )) }
函数的参数也可以是一个函数 匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" "math" "reflect" "runtime" ) func apply (op func (int , int ) int , a, b int ) int { p := reflect.ValueOf(op).Pointer() opName := runtime.FuncForPC(p).Name() fmt.Printf("Calling function %s with args (%d, %d)\n" , opName, a, b) return op(a, b) } func main () { fmt.Println(apply(func (a int , b int ) int { return int (math.Pow( float64 (a), float64 (b))) }, 3 , 4 )) }
可变参数列表 没有默认参数,可选参数,函数重载,操作符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func sum (numbers ...int ) int { s := 0 for i := range numbers { s += numbers[i] } return s } func main () { fmt.Println(sum(1 , 2 , 3 , 4 , 5 )) }
指针 指针不能运算
go语言只有值传递一种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func swap (a, b *int ) { *b, *a = *a, *b } func main () { a, b := 3 , 4 swap(&a, &b) fmt.Println(a, b) }
内建容器 数组 数组的遍历—range关键字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport "fmt" func main () { var arr1 [5 ]int arr2 := [3 ]int {1 , 3 , 5 } arr3 := [...]int {2 , 4 , 6 , 8 , 10 } var grid [4 ][5 ]int fmt.Println(arr1, arr2, arr3) fmt.Println(grid) for i := 0 ; i < len (arr3); i++ { fmt.Println(arr3[i]) } for i, v := range arr3 { fmt.Println(i, v) } }
数组传递是值类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" func printArray (arr [5]int ) { arr[0 ] = 100 for i, v := range arr { fmt.Println(i, v) } } func main () { var arr1 [5 ]int arr3 := [...]int {2 , 4 , 6 , 8 , 10 } printArray(arr1) printArray(arr3) fmt.Println(arr1, arr3) }
切片 切片本身没有数据,而是数组的一个视图(view)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func updateSlice (s []int ) { s[0 ] = 100 } func main () { arr := [...]int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := arr[2 :] fmt.Println(s1) s2 := arr[:] fmt.Println(s2) updateSlice(s1) fmt.Println(s1) fmt.Println(s2) }
slice的拓展
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { arr := [...]int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := arr[2 :6 ] fmt.Println(s1) s2 := s1[3 :5 ] fmt.Println(s2) }
Slice的实现 只要不超过capacity即可
slice可以向后扩展,不可以向前扩展
s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { arr := [...]int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := arr[2 :6 ] fmt.Println(s1) s2 := s1[3 :5 ] fmt.Println(s2) fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n" , s1, len (s1), cap (s1)) fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n" , s2, len (s2), cap (s2)) }
向Slice添加元素 s4和s5不在是对arr的view
添加元素时如果超越cap,系统会重新分配更大的底层数组
s = append(s, val)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { arr := [...]int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := arr[2 :6 ] fmt.Println(s1) s2 := s1[3 :5 ] fmt.Println(s2) s3 := append (s2, 10 ) s4 := append (s3, 11 ) s5 := append (s4, 12 ) fmt.Println("s3, s4, s5 =" , s3, s4, s5) fmt.Println(arr) }
Slice的定义 cap每次空间不够用的时候会翻倍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func printSlice (s []int ) { fmt.Printf("len=%d, cap=%d\n" , len (s), cap (s)) } func main () { s := []int {2 , 4 , 6 } for i := 0 ; i < 100 ; i++ { printSlice(s) s = append (s, 2 * i + 1 ) } fmt.Println(s) }
slice可以直接用make定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func printSlice (s []int ) { fmt.Printf("len=%d, cap=%d\n" , len (s), cap (s)) } func main () { s2 := make ([]int , 16 ) s3 := make ([]int , 10 , 32 ) printSlice(s2) printSlice(s3) }
Slice的复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func printSlice (s []int ) { fmt.Println(s) } func main () { s1 := []int {1 , 2 , 3 , 4 , 5 } s2 := []int {2 , 4 , 6 , 8 } copy (s1, s2) printSlice(s1) }
Slice删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func printSlice (s []int ) { fmt.Println(s) } func main () { s1 := []int {1 , 2 , 3 , 4 , 5 } s2 := []int {2 , 4 , 6 , 8 } copy (s1, s2) printSlice(s1) s1 = append (s1[:3 ], s1[4 :]...) printSlice(s1) }
Map map是无序的,类似hashmap
创建:make(map[string]int)
获取元素:m[key]
key不存在时,获得Value类型的初始值
用value,ok := m[key]来判断是否存在key用
delete删除一个key
使用range遍历key,或者遍历key,value对
不保证遍历顺序,如需顺序,需手动对key排序
使用len获得元素个数
map的key的类型:
map使用哈希表,必须可以比较相等
除了slice,map,function的内建类型都可以作为key
Struct类型不包含上述字段,也可作为key
定义 map[K]V
map[K1]map[K2]v
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { m := map [string ]string { "name" : "ccmouse" , "course" : "golang" , "site" : "imooc" , "quality" : "notbad" , } fmt.Println(m) m2 := make (map [string ]int ) var m3 map [string ]int fmt.Println(m2) fmt.Println(m3) }
遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" func main () { m := map [string ]string { "name" : "ccmouse" , "course" : "golang" , "site" : "imooc" , "quality" : "notbad" , } fmt.Println(m) for k, v := range m { fmt.Println(k, v) } for k := range m { fmt.Println(k) } for _, v := range m { fmt.Println(v) } }
支持[ ]操作 key不存在,也不会报错,而是输出zero value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func main () { m := map [string ]string { "name" : "ccmouse" , "course" : "golang" , "site" : "imooc" , "quality" : "notbad" , } fmt.Println(m["site" ]) fmt.Println(m["si" ]) }
key是否在map中,k1, ok := map[key],如果存在,k1就是值,ok为true;不存在k1为zero value,ok为false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { m := map [string ]string { "name" : "ccmouse" , "course" : "golang" , "site" : "imooc" , "quality" : "notbad" , } k1, ok := m["site" ] fmt.Println(k1, ok) k2, ok := m["si" ] fmt.Println(k2, ok) }
删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { m := map [string ]string { "name" : "ccmouse" , "course" : "golang" , "site" : "imooc" , "quality" : "notbad" , } _, ok := m["name" ] fmt.Println(ok) delete (m, "name" ) _, ok = m["name" ] fmt.Println(ok) }
字符串和字符串处理 rune相当于go的char
%x表示按16进制输出
使用range遍历pos,rune对
使用utf8.RuneCountInString获得字符数量
使用len获得字节长度
使用[]byte获得字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport ( "fmt" "unicode/utf8" ) func main () { s := "Yes咕咕咕" fmt.Println(len (s)) for _, b := range []byte (s) { fmt.Printf("%X " , b) } fmt.Println() for i, ch := range s { fmt.Printf("(%d %X) " , i, ch) } fmt.Println() fmt.Println("Rune count: " , utf8.RuneCountInString(s)) bytes := []byte (s) for len (bytes) > 0 { ch, size := utf8.DecodeRune(bytes) bytes = bytes[size:] fmt.Printf("%c " , ch) } fmt.Println() for i, ch := range []rune (s) { fmt.Printf("(%d %c) " , i, ch) } fmt.Println() }
面向对象 go语言仅支持封装,不支持继承和多态
go语言没有class,只有struct
结构的创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func main () { var root treeNode fmt.Println(root) root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) nodes := []treeNode { {value: 3 }, {}, {6 , nil , &root}, } fmt.Println(nodes) }
工厂函数 使用自定义工厂函数
go语言允许返回局部变量的地址,这在C++中是不允许的
C++中局部变量是分配在栈上的,函数一旦退出,局部变量就会被销毁;如果要传出函数体,就要分配在栈上
Java中,new出来的变量一般是放在堆上的,这样才能被GC回收
Go不需要知道变量是分配在堆上还是栈上,垃圾回收机制会自动判断之后是否还会用到这个变量,自动选择在堆上还是栈上分配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func createNode (value int ) *treeNode { return &treeNode{value: value} } func main () { var root treeNode fmt.Println(root) root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) root.left.right = createNode(2 ) }
给结构体定义方法 结构体的方法定义在结构体外部
值作为方法的接收者 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func (node treeNode) print () { fmt.Print(node.value) } func createNode (value int ) *treeNode { return &treeNode{value: value} } func main () { var root treeNode fmt.Println(root) root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) root.left.right = createNode(2 ) root.print () fmt.Println() }
指针作为方法的接收者 只有使用指针才可以改变结构内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func (node treeNode) print () { fmt.Print(node.value) } func (node *treeNode) setValue(value int ) { node.value = value } func createNode (value int ) *treeNode { return &treeNode{value: value} } func main () { var root treeNode root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) root.left.right = createNode(2 ) root.right.left.setValue(4 ) root.right.left.print () fmt.Println() }
nil指针也可以调用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func (node treeNode) print () { fmt.Print(node.value) } func (node *treeNode) setValue(value int ) { if node == nil { fmt.Println("Setting value to nil node. " + "Ignored" ) return } node.value = value } func createNode (value int ) *treeNode { return &treeNode{value: value} } func main () { var root treeNode root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) root.left.right = createNode(2 ) root.right.left.setValue(4 ) root.right.left.print () fmt.Println() var pRoot *treeNode pRoot.setValue(200 ) pRoot = &root pRoot.setValue(300 ) pRoot.print () }
树的遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package mainimport "fmt" type treeNode struct { value int left, right *treeNode } func (node treeNode) print () { fmt.Print(node.value, " " ) } func (node *treeNode) setValue(value int ) { if node == nil { fmt.Println("Setting value to nil node. " + "Ignored" ) return } node.value = value } func (node *treeNode) traverse() { if node == nil { return } node.left.traverse() node.print () node.right.traverse() } func createNode (value int ) *treeNode { return &treeNode{value: value} } func main () { var root treeNode root = treeNode{value: 3 } root.left = &treeNode{} root.right = &treeNode{5 , nil , nil } root.right.left = new (treeNode) root.left.right = createNode(2 ) root.right.left.setValue(4 ) root.traverse() }
值接收者VS指针接收者 要改变内容必须使用指针接收者
结构过大也考虑使用指针接收者
一致性:如有指针接收者,最好都是指针接收者
值接收者是go语言特有
值/指针接收者均可接收值/指针
包和封装 名字一般使用CamelCase
首字母大写:public
首字母小写:private
每个目录一个包
main包包含可执行入口
为结构定义的方法必须放在同一个包内,可以是不同文件
扩展已有类型 如何扩充系统类型或者别人的类型?
1.定义别名
2.使用组合
通过组合 通过组合实现后序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport ( "fmt" "golang/tree" ) type myTreeNode struct { node *tree.Node } func (myNode *myTreeNode) postOrder() { if myNode == nil || myNode.node == nil { return } left := myTreeNode{myNode.node.Left} left.postOrder() right := myTreeNode{myNode.node.Right} right.postOrder() myNode.node.Print() } func main () { var root tree.Node root = tree.Node{Value: 3 } root.Left = &tree.Node{} root.Right = &tree.Node{5 , nil , nil } root.Right.Left = new (tree.Node) root.Left.Right = tree.CreateNode(2 ) root.Right.Left.SetValue(4 ) root.Traverse() fmt.Println() myRoot := myTreeNode{&root} myRoot.postOrder() fmt.Println() }
定义别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package queuetype Queue []int func (q *Queue) Push(v int ) { *q = append (*q, v) } func (q *Queue) Pop() int { head := (*q)[0 ] *q = (*q)[1 :] return head } func (q *Queue) IsEmpty() bool { return len (*q) == 0 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "fmt" "golang/queue" ) func main () { q := queue.Queue{1 } q.Push(2 ) q.Push(3 ) fmt.Println(q.Pop()) fmt.Println(q.Pop()) fmt.Println(q.IsEmpty()) fmt.Println(q.Pop()) fmt.Println(q.IsEmpty()) }
GOPATH环境变量 默认在~/go(uniX,linux),%USERPROFILE%\go(windows)
官方推荐:所有项目和第三方库都放在同一个GOPATH下
也可以将每个项目放在不同的GOPATH
go get获取第三方库 go get命令演示
使用gopm来获取无法下载的包
go get -v github.com/gpmgo/gopm
面向接口 duck typing
像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子
描述事物的外部行为而非内部结构