第一个golang程序
package mainimport "fmt"func main() { fmt.Println("hello golang")}
根本数据类型
- 布尔型( true 或者 false)
- 数字类型( 整型 int 和 浮点型 float32、float64 )
- 字符串类型( 字符串就是一串固定长度的字符连接起来的字符序列 )
- 派生类型:
- 指针类型(Pointer)
- 数组类型
- 结构化类型(struct)
- Channel 类型
- 函数类型
- 切片类型
- 接口类型(interface)
- Map 类型
数字类型
- 整形
- uint8
无符号 8 位整型 (0 到 255)
- uint16
无符号 16 位整型 (0 到 65535)
- uint32
无符号 32 位整型 (0 到 4294967295)
- uint64
无符号 64 位整型 (0 到 18446744073709551615)
- int8
有符号 8 位整型 (-128 到 127)
- int16
有符号 16 位整型 (-32768 到 32767)
- int32
有符号 32 位整型 (-2147483648 到 2147483647)
- int64
有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
- 浮点型
- float32
32位浮点型数
- float64
64位浮点型数
- complex64
32 位实数和虚数
- complex128
64 位实数和虚数
- 其余数字类型
- byte
相似 uint8
- rune
相似 int32
- uint
32 或 64 位
- int
与 uint 一样大小
- uintptr
无符号整型,用于寄存一个指针
定义变量
// 申明一个变量var identifier type// 能够一次申明多个变量var identifier1, identifier2 type// 依据值自行断定变量类型var v_name = value// 简短模式 省略 var, 留神 := 左侧如果没有申明新的变量v_name := value
定义常量
// 申明一个常量const identifier [type] = value// 显式类型定义const b string = "abc"// 隐式类型定义const b = "abc"// 多个雷同类型的申明(隐式类型定义)const c_name1, c_name2 = value1, value2
iota
iota,非凡常量,能够认为是一个能够被编译器批改的常量
iota 在 const关键字呈现时将被重置为 0(const 外部的第一行之前),const 中每新增一行常量申明将使 iota 计数一次(iota 可了解为 const 语句块中的行索引)。
iota 能够被用作枚举值
package mainimport "fmt"func main() { const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,复原计数 i //8 ) fmt.Println(a,b,c,d,e,f,g,h,i)}
运行后果
0 1 2 ha ha 100 100 7 8
第一个 iota 等于 0,每当 iota 在新的一行被应用时,它的值都会主动加 1
条件管制语句
if & if else
package mainimport "fmt"func main() { var a = 12 if a > 10 { fmt.Println("a>10") } else { fmt.Println("a<=10") }}
运行后果
a>10
switch
package mainimport "fmt"func main() { var a = 12 switch a { case 1: fmt.Println(1) case 2: fmt.Println(2) case 12: fmt.Println(12) default: fmt.Println(a) }}
运行后果
12
应用 fallthrough 会强制执行前面的 case 语句,fallthrough 不会判断下一条 case 的表达式后果是否为 true
package mainimport "fmt"func main() { var a = 1 switch a { case 1: fmt.Println(1) fallthrough case 2: fmt.Println(2) case 12: fmt.Println(12) default: fmt.Println(a) }}
运行后果
12
select
select 是 Go 中的一个控制结构,相似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接管。
select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
package mainimport "fmt"func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = <-c1: fmt.Printf("received ", i1, " from c1\n") case c2 <- i2: fmt.Printf("sent ", i2, " to c2\n") case i3, ok := <-c3: if ok { fmt.Printf("received ", i3, " from c3\n") } else { fmt.Printf("c3 is closed\n") } default: fmt.Printf("no communication\n") }}
运行后果
no communication
循环管制语句
for
package mainimport "fmt"func main() { for i := 1; i < 10; i++ { fmt.Println(i) }}
package mainimport "fmt"func main() { var i = 1 for i < 10 { fmt.Println(i) i++ }}
运行后果
123456789
死循环
for {}
函数
package mainimport "fmt"func main() { test(1)}func test(i int) int { for i < 10 { fmt.Println(i) i++ } return i}
运行后果
123456789
package mainimport "fmt"func main() { i := test(1, 9) fmt.Println("最大值为:", i)}func test(i, j int) int { if i > j { return i } else { return j }}
运行后果
最大值为: 9
函数返回多个值
package mainimport "fmt"func main() { s, s2 := test("hello", "go") fmt.Println(s, s2)}func test(i, j string) (string, string) { return i, j}
运行后果
hello go
值传递 和 援用传递
package mainimport "fmt"func main() { var a = 3 var b = 4 fmt.Println("值传递运行前a=", a, "b=", b) test1(a, b) fmt.Println("值传递运行后a=", a, "b=", b) fmt.Println("===============================================") var i = 1 var j = 2 fmt.Println("援用传递运行前i=", i, "j=", j) test2(&i, &j) fmt.Println("援用传递运行后i=", i, "j=", j)}// 值传递func test1(i, j int) (int, int) { var temp int temp = i i = j j = temp return i, j}// 援用传递func test2(i, j *int) (int, int) { var temp int temp = *i *i = *j *j = temp return *i, *j}
运行后果
值传递运行前a= 3 b= 4值传递运行后a= 3 b= 4===============================================援用传递运行前i= 1 j= 2援用传递运行后i= 2 j= 1
函数作为实参
package mainimport "fmt"func main() { funcA := func(a int) int { return a } fmt.Println(funcA(12))}
运行后果
12
闭包
Go 语言反对匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于能够间接应用函数内的变量,不用申明。
package mainimport "fmt"func main() { next := getSequence() fmt.Println(next()) fmt.Println(next()) fmt.Println(next())}func getSequence() func() int { a := 1 return func() int { a++ return a }}
运行后果
234
办法
Go 语言中同时有函数和办法。一个办法就是一个蕴含了接受者的函数,接受者能够是命名类型或者构造体类型的一个值或者是一个指针。所有给定类型的办法属于该类型的办法集
package mainimport "fmt"type Circle struct { radius float64}func (circle Circle) getPerimeter() float64 { return 3.14 * circle.radius * 2}func main() { var circle Circle circle.radius = 10 fmt.Println(circle.getPerimeter())}
运行后果
62.800000000000004
变量作用域
Go 语言中变量能够在三个中央申明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
package mainimport "fmt"// 全局变量var a = 1func main() { // 局部变量 var b = 2 test(a) test(b)}// 形式参数func test(a int) { fmt.Println(a)}
数组
数组是具备雷同惟一类型的一组已编号且长度固定的数据项序列,这种类型能够是任意的原始类型例如整形、字符串或者自定义类型
申明数组
// 模式var variable_name [SIZE] variable_type// 举例var balance [10] float32
初始化数组
// 初始化一个长度为5的float32数组var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}// 如果疏忽 [] 中的数字不设置数组大小var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}balance[6] = 60.0
拜访数组元素
var a float32 = balance[5]
指针
一个指针变量指向了一个值的内存地址
申明指针
// 模式var var_name *var-type// 举例var ip *int /* 指向整型*/var fp *float32 /* 指向浮点型 */
package mainimport "fmt"func main() { var a int = 20 /* 申明理论变量 */ var ip *int /* 申明指针变量 */ ip = &a /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %x\n", &a) /* 指针变量的存储地址 */ fmt.Printf("ip 变量贮存的指针地址: %x\n", ip) /* 应用指针拜访值 */ fmt.Printf("*ip 变量的值: %d\n", *ip)}
运行后果
a 变量的地址是: c00000a0b0ip 变量贮存的指针地址: c00000a0b0*ip 变量的值: 20
空指针
当一个指针被定义后没有调配到任何变量时,它的值为 nil。
nil 指针也称为空指针。一个指针变量通常缩写为 ptr。
package mainimport "fmt"func main() { var ip *int /* 申明指针变量 */ /* 指针变量的存储地址 */ fmt.Printf("ip 的值为: %x\n", ip)}
运行后果
ip 的值为: 0
空指针判断
if(ptr != nil) /* ptr 不是空指针 */if(ptr == nil) /* ptr 是空指针 */
指针数组
package mainimport "fmt"func main() { a := []int{10, 100, 200} // 遍历数组 for i := 0; i < len(a); i++ { fmt.Printf("a[%d] = %d\n", i, a[i]) } fmt.Println("==================================") // 有一种状况,咱们可能须要保留数组,这样咱们就须要应用到指针。 // 以下申明了整型指针数组: var ptr [3]*int for i := 0; i < len(a); i++ { /* 整数地址赋值给指针数组 */ ptr[i] = &a[i] } for i := 0; i < len(ptr); i++ { fmt.Printf("a[%d] = %d\n", i, *ptr[i]) }}
运行后果
a[0] = 10a[1] = 100a[2] = 200==================================a[0] = 10a[1] = 100a[2] = 200
指向指针的指针
如果一个指针变量寄存的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
指向指针的指针变量申明格局
var ptr **int;
package mainimport "fmt"func main() { var a int var ptr *int var pptr **int a = 3000 /* 指针 ptr 地址 */ ptr = &a /* 指向指针 ptr 地址 */ pptr = &ptr /* 获取 pptr 的值 */ fmt.Printf("变量 a = %d\n", a) fmt.Printf("指针变量 *ptr = %d\n", *ptr) fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)}
运行后果
变量 a = 3000指针变量 *ptr = 3000指向指针的指针变量 **pptr = 3000
指针作为函数参数
package mainimport "fmt"func main() { var a = 3 var b = 4 fmt.Println("值传递运行前a=", a, "b=", b) test1(a, b) fmt.Println("值传递运行后a=", a, "b=", b) fmt.Println("===============================================") var i = 1 var j = 2 fmt.Println("援用传递运行前i=", i, "j=", j) test2(&i, &j) fmt.Println("援用传递运行后i=", i, "j=", j)}// 值传递func test1(i, j int) (int, int) { var temp int temp = i i = j j = temp return i, j}// 援用传递func test2(i, j *int) (int, int) { var temp int temp = *i *i = *j *j = temp return *i, *j}
运行后果
值传递运行前a= 3 b= 4值传递运行后a= 3 b= 4===============================================援用传递运行前i= 1 j= 2援用传递运行后i= 2 j= 1
构造体
构造体是由一系列具备雷同类型或不同类型的数据形成的数据汇合。
构造体示意一项记录,比方保留图书馆的书籍记录,每本书有以下属性:
Title :题目
Author : 作者
Subject:学科
ID:书籍ID
定义
type struct_variable_type struct { member definition member definition ... member definition}
一旦定义了构造体类型,它就能用于变量的申明,语法格局如下:
variable_name := structure_variable_type {value1, value2...valuen}或variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
package mainimport "fmt"// 一、定义构造体type Books struct { title string author string subject string book_id int}func main() { // 创立一个新的构造体 fmt.Println(Books{"Go 语言", "Google", "Go 语言教程", 6495407}) // 也能够应用 key => value 格局 fmt.Println(Books{title: "Go 语言", author: "Google", subject: "Go 语言教程", book_id: 6495407}) // 疏忽的字段为 0 或 空 fmt.Println(Books{title: "Go 语言", author: "Google"}) fmt.Println("=========================") // 二、拜访构造体成员 /* book 2 形容 */ var Book2 Books Book2.title = "Python 教程" Book2.author = "Python" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book2 信息 */ fmt.Printf("Book 2 title : %s\n", Book2.title) fmt.Printf("Book 2 author : %s\n", Book2.author) fmt.Printf("Book 2 subject : %s\n", Book2.subject) fmt.Printf("Book 2 book_id : %d\n", Book2.book_id) fmt.Println("=========================") // 三、构造体作为函数参数 printBook(Book2) fmt.Println("=========================") // 四、构造体指针 printBook2(&Book2)}func printBook(book Books) { fmt.Printf("Book title : %s\n", book.title) fmt.Printf("Book author : %s\n", book.author) fmt.Printf("Book subject : %s\n", book.subject) fmt.Printf("Book book_id : %d\n", book.book_id)}func printBook2(book *Books) { fmt.Printf("Book title : %s\n", book.title) fmt.Printf("Book author : %s\n", book.author) fmt.Printf("Book subject : %s\n", book.subject) fmt.Printf("Book book_id : %d\n", book.book_id)}
运行后果
{Go 语言 Google Go 语言教程 6495407}{Go 语言 Google Go 语言教程 6495407}{Go 语言 Google 0}=========================Book 2 title : Python 教程Book 2 author : PythonBook 2 subject : Python 语言教程Book 2 book_id : 6495700=========================Book title : Python 教程Book author : PythonBook subject : Python 语言教程Book book_id : 6495700=========================Book title : Python 教程Book author : PythonBook subject : Python 语言教程Book book_id : 6495700
切片(Slice)
Go 数组的长度不可扭转,与数组相比切片的长度是不固定的,能够追加元素,在追加时可能使切片的容量增大。
定义切片
- var identifier []type
- var slice1 []type = make([]type, len) 或者简写 slice1 := make([]type, len) 指定长度
- make([]T, length, capacity) 指定容量
package mainimport "fmt"func main() { // 切片初始化 var slice = []int{1, 2, 3} // 从下标startIndex到endIndex-1 下的元素 切片截取 fmt.Println(slice[0:2]) fmt.Println(slice[:2]) fmt.Println(slice[0:]) fmt.Println("=======================") // len() 和 cap() 函数 fmt.Printf("len=%d cap=%d slice=%v\n", len(slice), cap(slice), slice) fmt.Println("=======================") // 空切片 var numbers []int fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers), cap(numbers), numbers) if numbers == nil { fmt.Printf("切片是空的\n") } fmt.Println("=======================") // append() 和 copy() 函数 var numbers1 []int // append() 追加 numbers1 = append(numbers1, 1) numbers1 = append(numbers1, 2, 3, 4) fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers1), cap(numbers1), numbers1) fmt.Println("=======================") // copy() 复制 /* 创立切片 numbers2 是之前切片的两倍容量*/ numbers2 := make([]int, len(numbers1), (cap(numbers1))*2) /* 拷贝 numbers1 的内容到 numbers2 */ copy(numbers2, numbers1) fmt.Printf("len=%d cap=%d slice=%v\n", len(numbers2), cap(numbers2), numbers2)}
运行后果
[1 2][1 2][1 2 3]=======================len=3 cap=3 slice=[1 2 3]=======================len=0 cap=0 slice=[]切片是空的=======================len=4 cap=4 slice=[1 2 3 4]=======================len=4 cap=8 slice=[1 2 3 4]
范畴(Range)
package mainimport "fmt"func main() { //这是咱们应用range去求一个slice的和。应用数组跟这个很相似 nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) //在数组上应用range将传入index和值两个变量。下面那个例子咱们不须要应用该元素的序号,所以咱们应用空白符"_"省略了。有时侯咱们的确须要晓得它的索引。 for i, num := range nums { if num == 3 { fmt.Println("index:", i) } } //range也能够用在map的键值对上。 kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } //range也能够用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)自身。 for i, c := range "go" { fmt.Println(i, c) }}
运行后果
sum: 9index: 1a -> appleb -> banana0 1031 111
Map(汇合)
Map 是一种无序的键值对的汇合。Map 最重要的一点是通过 key 来疾速检索数据,key 相似于索引,指向数据的值。
定义汇合
/* 申明变量,默认 map 是 nil */var map_variable map[key_data_type]value_data_type/* 应用 make 函数 */map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就会创立一个 nil map。nil map 不能用来寄存键值对
package mainimport "fmt"func main() { var countryCapitalMap = make(map[string]string) /* map插入key - value对,各个国家对应的首都 */ countryCapitalMap["France"] = "巴黎" countryCapitalMap["Italy"] = "罗马" countryCapitalMap["Japan"] = "东京" countryCapitalMap["India "] = "新德里" /*应用键输入地图值 */ for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap[country]) } /*查看元素在汇合中是否存在 */ capital, ok := countryCapitalMap["American"] /*如果确定是实在的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if ok { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") } fmt.Println("========================") // delete() 函数 for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap[country]) } // 删除元素 delete(countryCapitalMap, "France") fmt.Println("法国条目被删除") for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap[country]) }}
运行后果
France 首都是 巴黎Italy 首都是 罗马Japan 首都是 东京India 首都是 新德里American 的首都不存在========================India 首都是 新德里France 首都是 巴黎Italy 首都是 罗马Japan 首都是 东京法国条目被删除Japan 首都是 东京India 首都是 新德里Italy 首都是 罗马
递归函数
递归,就是在运行的过程中调用本人
阶乘
package mainimport "fmt"func main() { var i int = 15 fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))}func Factorial(n uint64) (result uint64) { if n > 0 { result = n * Factorial(n-1) return result } return 1}
运行后果
15 的阶乘是 1307674368000
斐波那契数列
package mainimport "fmt"func main() { var i int for i = 0; i < 10; i++ { fmt.Printf("%d\t", fibonacci(i)) }}func fibonacci(n int) int { if n < 2 { return n } return fibonacci(n-2) + fibonacci(n-1)}
运行后果
0 1 1 2 3 5 8 13 21 34
类型转换
类型转换用于将一种数据类型的变量转换为另外一种类型的变量。
type_name(expression)
package mainimport "fmt"func main() { var sum int = 17 var count int = 5 var mean float32 mean = float32(sum) / float32(count) fmt.Printf("mean 的值为: %f\n", mean)}
运行后果
mean 的值为: 3.400000
接口
package mainimport "fmt"type Phone interface { call()}type NokiaPhone struct {}type IPhone struct {}func main() { n := new(NokiaPhone) n.call() i := new(IPhone) i.call()}func (NokiaPhone) call() { fmt.Println("nokiaPhone")}func (IPhone) call() { fmt.Println("IPhone")}
运行后果
nokiaPhoneIPhone
错误处理
Go 语言通过内置的谬误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义
type error interface { Error() string}
咱们能够在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最初的返回值中返回错误信息。应用errors.New 可返回一个错误信息
package mainimport ( "errors" "fmt")func main() { _, err := Sqrt(-1) if err != nil { fmt.Println(err) }}func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 实现 return f, nil}
运行后果
math: square root of negative number
并发
Go 语言反对并发,咱们只须要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行治理的。
goroutine 语法格局:
go 函数名( 参数列表 )
Go 容许应用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。
package mainimport ( "fmt" "time")func main() { go say("world") say("hello")}func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }}
运行后果
helloworldworldhellohelloworldworldhellohello
通道(channel)
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通信。操作符 <- 用于指定通道的方向,发送或接管。如果未指定方向,则为双向通道。
申明一个通道,通道在应用前必须先创立:
ch := make(chan int)
留神:默认状况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
package mainimport ( "fmt")func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从通道 c 中接管 fmt.Println(x, y, x+y)}func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 把 sum 发送到通道 c}
运行后果
-5 17 12
通道缓冲区
通道能够设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
带缓冲区的通道容许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据能够放在缓冲区外面,能够期待接收端去获取数据,而不是立即须要接收端去获取数据。
不过因为缓冲区的大小是无限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无奈再发送数据了。
留神:如果通道不带缓冲,发送方会阻塞直到接管方从通道中接管了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着须要期待直到某个接管方获取到一个值。接管方在有值能够接管之前会始终阻塞。
package mainimport ( "fmt")func main() { // 这里咱们定义了一个能够存储整数类型的带缓冲通道 // 缓冲区大小为2 ch := make(chan int, 2) // 因为 ch 是带缓冲的通道,咱们能够同时发送两个数据 // 而不必立即须要去同步读取数据 ch <- 1 ch <- 2 // 获取这两个数据 fmt.Println(<-ch) fmt.Println(<-ch)}
运行后果
12
遍历通道与敞开通道
Go 通过 range 关键字来实现遍历读取到的数据,相似于与数组或切片。格局如下:
v, ok := <-ch
package mainimport ( "fmt")func main() { c := make(chan int, 10) go fibonacci(cap(c), c) // range 函数遍历每个从通道接管到的数据,因为 c 在发送完 10 个 // 数据之后就敞开了通道,所以这里咱们 range 函数在接管到 10 个数据 // 之后就完结了。如果下面的 c 通道不敞开,那么 range 函数就不 // 会完结,从而在接管第 11 个数据的时候就阻塞了。 for i := range c { fmt.Println(i) }}func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } // 敞开通道 close(c)}
运行后果
0112358132134