Go语言的基础组成-->包声明/引入包/函数/变量/语句&表达式/注释
package main -->定义了包名,必须在源文件中非注释的第一行指明这个文件属于哪个包。package main表示一个
可独立执行的程序,每个Go应用程序都包含一个名为main的包 import "fmt" -->告诉Go编译器这个程序需要使用fmt包(的函数,或其他元素),fmt包实现了格式化IO
(输入/输出)的函数
-->func main()是程序开始执行的函数。main函数是每一个可执行程序所必须包含的,一般都是在
启动后第一个执行的函数,如果有init()函数会先执行该函数
func main() { // --》 { 不能放在单独的行上 /*注释*/ fmt.Println("Hello, world!") }
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,那么使用这种形式的
标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包)-->导出,像面向对象
语言中的public
标识符如果以小写字母开头,则对包外是不可见的,但在整个包的内部是可见并且可用的-->protected
运行go代码-->go run hello.go
生成二进制文件-->go build hello.go --> .exe
Go语言基础语法
Go标记:Go程序可以由多个标记组成,可以是关键字、标识符、常量、字符串、符号
Go语言数据类型
布尔型-->var b bool = true
数字类型 --> int float32 float64
字符串类型 -->
派生类型-->指针类型Pointer、数组类型、结构化类型struct、Channel类型、函数类型、切片类型、接口类型interface、Map类型
声明变量使用var关键字,var identifier type
package main import "fmt" func main() { var a string = "Runoob" fmt.Println(a) var b, c int = 1, 2 fmt.Println(b, c) }
package main var x, y int var ( //这种因式分解关键字的写法一般用于声明全局变量 a int b bool ) var c, d int = 1, 2 var e, f = 123, "hello" func main() { g, h := 123, "hello" println(x, y, a, b, c, d, e, f, g, h) }
值类型和引用类型
int、float、bool、string这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
可以通过&i来获取变量i的内存地址。值类型的变量的值存储在栈中
一个引用类型的变量r1存储的是r1的值所在的内存地址,或内存地址中第一个字所在的位置
使用变量的首选形式 :=
全局变量允许声明但不使用,局部变量声明了就必须使用否则报错
交换两个变量的值,可以使用a,b = b,a
空白标识符_也被用于抛弃值,如值5在: _, b = 5, 7中被抛弃
_实际上是一个只写变量,不能得到它的值。这样做是因为Go语言中必须使用所有被声明的变量,但有时并不需要使用从一个函数得到的所有返回值
Go语言常量
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型
常量定义:const identifier [type] = value
可以省略类型说明符[type],因为编译器可以根据变量的值来推断其类型
package main import "fmt" func main() { const LENGTH int = 10 const WIDTH int = 5 var area int const a, b, c = 1, false, "str" area = LENGTH * WIDTH fmt.Printf("面积为:%d", area) println() println(a, b, c) }
//常量还可以用作枚举 const ( Unknown = 0 Female = 1 Male = 2 )
//常量可以用len(),cap(),unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过 package main import "unsafe" const ( a = "abc" b = len(a) //3 c = unsafe.Sizeof(a) //16 ) func main() { println(a, b, c) }
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量
iota在const关键字出现时将被重置为0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)
iota可以被用作枚举值
const ( a = iota b = iota c = iota )
Go语言运算符
Go语言条件语句
Go语言循环语句-->
for循环
goto语句,go语言的goto语句可以无条件地转移到过程中指定的行。goto语句通常与条件语句配合使用,可用来实现条件转移,构成循环,跳出循环体等功能
无限循环,for true
Go语言函数
函数是基本的代码块,用于执行一个任务
Go语言最少有个main函数
函数声明告诉了编译器函数的名称,返回类型,和参数
函数定义: func function_name([parameter list]) [return_types] {函数体}
func-->函数由func开始声明
function_name-->函数名称,函数名和参数列表一起构成了函数签名
parameter list-->参数列表
return_types-->返回类型
//函数返回两个数的最大值 func max(num1, num2 int) int { //声明局部变量 var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
闭包
package main import "fmt" func getSequence() func() int{ i := 0 return func() int { i += 1 return i } } func main() { nextNumber := getSequence() /*调用nextNumber函数,i变量自增1并返回*/ fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) /*创建新的函数nextNumber1,并查看结果*/ nextNumber1 := getSequence() fmt.Println(nextNumber1()) fmt.Println(nextNumber1()) }
Go语言函数方法
Go语言中同时有函数和方法。一个方法就是一个包含了接收者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针
package main import ( "fmt" ) /*定义结构体*/ type Circle struct { radius float64 } func main() { var c1 Circle c1.radius = 10.00 fmt.Println("圆的面积 = ", c1.getArea()) } // func (variable_name variable_data_type) function_name() [return_type] {/*函数体*/} func (c Circle) getArea() float64 { return 3.14 * c.radius * c.radius }
Go语言变量作用域
Go语言中变量可以在三个地方声明:
局部变量-->在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量
全局变量-->在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用
Go语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑
形式参数-->作为函数的局部变量来使用
package main import "fmt" /*声明全局变量*/ var a int = 20; func main() { var a int = 10 var b int = 20 var c int = 0 fmt.Printf("main()函数中 a = %d\n", a) c = sum(a, b) fmt.Printf("main()函数中c = %d\n", c) } func sum(a, b int) int { fmt.Printf("sum()函数中a = %d\n", a) fmt.Printf("sum()函数中b = %d\n", b) return a + b }
Go语言数组
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型
Go语言数组声明需要指定元素类型及元素个数-->var variable_name [SIZE] variable_type
Go语言指针
变量是一种使用方便的占位符,用于引用计算机内存地址
Go语言的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址
指针-->一个指针变量指向了一个值的内存地址
var ip *int
使用指针:定义指针变量-->为指针变量赋值-->访问指针变量中指向地址的值
package main import "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) }
Go空指针
当一个指针被定义后没有分配到任何变量时,它的值为nil
一个指针变量通常缩写为ptr
指向指针的指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量
Go语言指针作为函数参数
Go语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可
Go语言结构体
Go语言中数组可以存储同一类型的数据,但在结构体中可以为不同项定义不同的数据类型
定义结构体:使用type和struct语句
struct语句定义一个新的数据类型,结构体中有一个或多个成员
type语句设定了结构体的名称
type struct_variable_type struct {
member definition
member definition
......
member definition
}
package main import "fmt" type Books struct { title string author string subject string book_id int } func main() { //创建一个新的结构体 fmt.Println(Books{"Go 语言", "www.runoob.com", "Go", 12}) //可以使用key=>value格式 fmt.Println(Books{title: "Go 语言", author:"www.run.com", subject:"Go", book_id: 00}) }
访问结构体成员-->
结构体.成员名
结构体作为函数参数-->
可以向其他数据类型一样将结构体类型作为参数传递给函数
结构体指针-->
可以定义指向结构体的指针 var struct_pointer *Books
使用结构体指针访问结构体成员,使用“.”操作符
Go语言切片-->动态数组
Go语言切片是对数组的抽象,与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
定义切片-->可以声明一个未指定大小的数组来定义切片
var identifier []type
切片不需要说明长度,或使用make()函数来创建切片
var slice1 []type = make([]type, len)
或者
slice1 := make([]type, len)
len()和cap()函数
切片是可索引的,并且可以由len()方法获取长度
切片提供了计算容量的方法cap()可以测量切片最长可以达到多少
append()和copy()函数
如果想增加切片的容量,必须创建一个新的更大的切片并把原分片的内容都拷贝过来
Go语言范围Range
Go语言中range关键字用于for循环中迭代数组、切片、通道或集合的元素,在数组和切片中它赶回元素的索引和索引对应的值,在集合中返回key-value对
package main import "fmt" func main() { nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) for i, num := range nums { if num == 3 { fmt.Println("index: ", i) } } kvs := map[string] string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } for i, c := range "go" { fmt.Println(i, c) } }
Go语言Map
Map是一种无序的键值对的集合。Map最重要的是通过key来快速检索数据,key类似于索引,指向数据的值
Map是一种集合,可以像迭代数组和切片那样迭代它。Map是无序的,无法决定它的返回顺序,因为Map是使用hash表来实现的
定义Map
可以使用内建函数make也可以使用map关键字来定义Map
//声明变量,默认map是nil var map_variable map[key_data_type]value_data_type //使用make函数 map_variable := make(map[key_data_type]value_data_type)
package main import "fmt" func main() { var countryCapitalMap map[string]string 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"] if (ok) { fmt.Println("American的首都是", capital) } else { fmt.Println("American的首都不存在") } }
delete()函数:用于删除集合的元素,参数为map和其对应的key
package main import "fmt" func main() { countryCapitalMap := map[string]string{"France": "Pairs", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"} fmt.Println("原始地图") for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap[country]) } delete(countryCapitalMap, "France") fmt.Println("法国条目被删除") fmt.Println("删除元素后地图") for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap[country]) } }
Go语言递归函数
func recursion() {
recursion()
}
Go语言接口
Go语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口
package main
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
method_namen [return_type]
}
type struct_name struct {
}
func (struct_name_variable struct_name) method_name1[return_type]() {
}
package main import ( "fmt" ) type Phone interface { call() } type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() { fmt.Println("I an iPhone, I can call you!") } func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call() }
Go错误处理
Go语言通过内置的错误接口提供了非常简单的错误处理机制
//error类型是一个接口类型
type error interface {
Error() string
}
可以在编码中通过实现error接口类型来生成错误信息
函数通常在最后的返回值中返回错误信息
package main import ( "fmt" ) //定义一个DivideError结构 type DivideError struct { dividee int divider int } func (de *DivideError) Error() string { strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider:0` return fmt.Sprintf(strFormat, de.dividee) } func Divide(varDividee int, varDivider int) (result int, errorMsg string) { if varDivider == 0 { dData := DivideError{ dividee: varDividee, divider: varDivider, } errorMsg = dData.Error() return } else { return varDividee / varDivider, "" } } func main() { if result, errorMsg := Divide(100, 10) ; errorMsg == "" { fmt.Println("100/10 = ", result) } if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("errorMsg is:", errorMsg) } }
Go并发
Go语言支持并发,只需要通过go关键字来开启goroutine即可
goroutine是轻量级线程,goroutine的调度是由Golang运行时进行管理的
goroutine语法格式:
go 函数名(参数列表)
Go允许使用go语句开启一个新的运行期线程,即goroutine,以一个不同的、新创建的goroutine来执行一个函数。同一个程序中的所有goroutine共享同一个地址空间
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { //go f(x, y, z) --> f(x, y, z) go say("world") say("hello") }
通道channel
通道channel是用来传递数据的一个数据结构
通道可用于两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。操作符<-用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道
ch <- v //把v发送到通道ch v := <-ch //从ch接收数据并把值赋给v //声明一个通道,使用chan关键字 ch := make(chan int) //默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据
package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum //把sum发送到通道c } 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) }
package main //通道可以设置缓冲区,通过make的第二个参数指定缓冲区大小 //ch := make(chan int, 100) //带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态 //如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值 //如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞 import "fmt" func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<- ch) fmt.Println(<- ch) }
package main //Go遍历通道与关闭通道 //Go通过range关键字来实现遍历读取到的数据,类似于与数组或切片 格式 v, ok := <-ch //如果通道接收不到数据后ok就为false,这时通道就可以使用close()函数来关闭 import ( "fmt" ) 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) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } }
Go语言开发工具
原文:https://www.cnblogs.com/liushoudong/p/12935793.html