赞
踩
目录
环境安装好了之后我们先写一个HelloWorld。
- package main
-
- import "fmt"
-
- func main() {
- /* 这是我的第一个简单的程序 */
- fmt.Println("Hello, World!")
- }
让我们来看下以上程序的各个部分:
第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
需要注意的是 { 不能单独放在一行,所以以下代码在运行时会产生错误:
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字
- var identifier type
- var identifier1, identifier2 type
- package main
- import "fmt"
- func main() {
- var a string = "Runoob"
- fmt.Println(a)
-
- var b, c int = 1, 2
- fmt.Println(b, c)
- }
数组长度不确定的时候可以使用 [...]或者[],编译器会根据元素个数自行推断数组的长度:
- var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- var balance2 = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- package main
-
- import "fmt"
-
- func main() {
- //数组
- var arr1 [3]int
- var arr2 [3]int = [3]int{1, 2, 3}
- fmt.Println(arr1,arr2)
-
- //切片,可以理解为动态数组
- var slice []int = []int{0, 1}
- fmt.Println(slice)
- slice = append(slice, 2, 3, 4, 5, 6)
- fmt.Println(slice)
-
- //数组的=是拷贝
- var arr3 [3]int = arr2
- arr3[0] = 666
- fmt.Println(arr1,arr3)
-
- //切片的=是引用
- var slice2 []int = slice
- fmt.Println(slice, slice2)
- slice2[0] = 666
- fmt.Println(slice, slice2)
- }
除了make可以创建切片之外,未设置长度的数组也可以创建切片。
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound],实例如下:
- package main
-
- import "fmt"
-
- func main() {
- /* 创建切片 */
- numbers := []int{0,1,2,3,4,5,6,7,8}
- printSlice(numbers)
-
- /* 打印原始切片 */
- fmt.Println("numbers ==", numbers)
-
- /* 打印子切片从索引1(包含) 到索引4(不包含)*/
- fmt.Println("numbers[1:4] ==", numbers[1:4])
-
- /* 默认下限为 0*/
- fmt.Println("numbers[:3] ==", numbers[:3])
-
- /* 默认上限为 len(s)*/
- fmt.Println("numbers[4:] ==", numbers[4:])
-
- numbers1 := make([]int,0,5)
- printSlice(numbers1)
-
- /* 打印子切片从索引 0(包含) 到索引 2(不包含) */
- number2 := numbers[:2]
- printSlice(number2)
-
- /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
- number3 := numbers[2:5]
- printSlice(number3)
-
- }
-
- func printSlice(x []int){
- fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
- }
Go语言提供的内置函数make()可以用于灵活地创建数组切片。
创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
创建了一个键类型为string、值类型为PersonInfo的map
myMap = make(map[string] PersonInfo)
也可以选择是否在创建时指定该map的初始存储能力,创建了一个初始存储能力为100的map
myMap = make(map[string] PersonInfo, 100)
创建并初始化map的代码
- myMap = map[string] PersonInfo{
- "1234": PersonInfo{"1", "Jack", "Room 101,..."},
- }
- package main
-
- func main(){
- // 先声明map
- var m1 map[string]string
- // 再使用make函数创建一个非nil的map,nil map不能赋值
- m1 = make(map[string]string)
- // 最后给已声明的map赋值
- m1["a"] = "aa"
- m1["b"] = "bb"
-
- // 直接创建
- m2 := make(map[string]int)
- // 然后赋值
- m2["1"] = 1
- m2["2"] = 2
- }
注意:省略 var, 注意 := 左侧如果没有声明新的变量,就产生编译错误。
- package main
-
- import "fmt"
-
- func main() {
- mySlice := make([]int, 5, 10)
-
- fmt.Println("len(mySlice):", len(mySlice))
- fmt.Println("cap(mySlice):", cap(mySlice))
- }
cap()函数返回的是数组切片分配的空间大小
- package main
-
- import "unsafe"
- const (
- a = "abc"
- b = len(a)
- c = unsafe.Sizeof(a)
- )
-
- func main(){
- println(a, b, c)
- }
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值:
- const (
- a = iota
- b = iota
- c = iota
- )
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
- const (
- a = iota
- b
- c
- )
看一个简单的实例
- package main
-
- import "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)
- }
Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。
和 C 语言的 for 一样:
for init; condition; post { }
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
比如遍历map
- for key, value := range oldMap {
- newMap[key] = value
- }
for循环说完了,我们来看一下switch中的一个关键字fallthrough,Go默认switch的每个case都有一个break,如果需要去掉某个case的break,则需要添加fallthrough。
- func function_name( [parameter list] ) [return_types] {
- 函数体
- }
注意,Go 函数可以返回多个值
- package main
-
- import "fmt"
-
- func swap(x, y string) (string, string) {
- return y, x
- }
-
- func main() {
- a, b := swap("Google", "baidu")
- fmt.Println(a, b)
- }
defer指定了在函数退出时必须执行的语句
- //1、关闭文件句柄
- func read(){
- file := open(filename);
- defer file.close();
-
- //文件操作
- }
-
- //2、锁资源释放
- func read(){
- mc.lock();
- defer mc.unlock();
-
- //其他操作
- }
-
- //3、数据库连接释放
- func read(){
- conn := openDatabase();
- defer conn.close();
-
- //其他操作
- }
如果同一个函数中有多个defer,执行顺序为先进后出,类似于栈。
- package main
-
- import (
- "fmt"
- )
-
- func main(){
- defer fmt.Println("1");
- defer fmt.Println("2");
- defer fmt.Println("3");
- defer fmt.Println("4");
- defer fmt.Println("5");
- }
- var ip *int /* 指向整型*/
- var fp *float32 /* 指向浮点型 */
golang也有野指针问题,要注意。
结构体中的小写的函数和变量在包之外无法调用,golang的大写类似c++ public,小写类似c++ private
- 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() {
- var Book1 Books /* 声明 Book1 为 Books 类型 */
- var Book2 Books /* 声明 Book2 为 Books 类型 */
-
- /* book 1 描述 */
- Book1.title = "Go 语言"
- Book1.author = "www.runoob.com"
- Book1.subject = "Go 语言教程"
- Book1.book_id = 6495407
-
- /* book 2 描述 */
- Book2.title = "Python 教程"
- Book2.author = "www.runoob.com"
- Book2.subject = "Python 语言教程"
- Book2.book_id = 6495700
-
- /* 打印 Book1 信息 */
- fmt.Printf( "Book 1 title : %s\n", Book1.title)
- fmt.Printf( "Book 1 author : %s\n", Book1.author)
- fmt.Printf( "Book 1 subject : %s\n", Book1.subject)
- fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id)
-
- /* 打印 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)
- }
- /* 定义接口 */
- type interface_name interface {
- method_name1 [return_type]
- method_name2 [return_type]
- method_name3 [return_type]
- ...
- method_namen [return_type]
- }
-
- /* 定义结构体 */
- type struct_name struct {
- /* variables */
- }
-
- /* 实现接口方法 */
- func (struct_name_variable struct_name) method_name1() [return_type] {
- /* 方法实现 */
- }
- ...
- func (struct_name_variable struct_name) method_namen() [return_type] {
- /* 方法实现*/
- }
- package main
-
- import (
- "fmt"
- )
-
- // Shaper 接口类型
- type Shaper interface {
- Area() float64
- }
-
- // Circle struct类型
- type Circle struct {
- radius float64
- }
-
- // Circle类型实现Shaper中的方法Area()
- func (c *Circle) Area() float64 {
- return 3.14 * c.radius * c.radius
- }
-
- // Square struct类型
- type Square struct {
- length float64
- }
-
- // Square类型实现Shaper中的方法Area()
- func (s *Square) Area() float64 {
- return s.length * s.length
- }
-
- func main() {
- // Circle类型的指针类型实例
- c := new(Circle)
- c.radius = 2.5
-
- // Square类型的值类型实例
- s := Square{3.2}
-
- // Sharpe接口实例ins1,它自身是指针类型的
- var ins1 Shaper
- // 将Circle实例c赋值给接口实例ins1
- // 那么ins1中就保存了实例c
- ins1 = c
- fmt.Println(ins1)
- fmt.Println(ins1.Area())
-
- // 使用类型推断将Square实例s赋值给接口实例
- ins2 := s
- fmt.Println(ins2)
- fmt.Println(ins2.Area())
- }
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
go 函数名( 参数列表 )
例如:
go f(x, y, z)
开启一个新的 goroutine:
f(x, y, z)
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 say("world")
- say("hello")
- }
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <-
用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
- ch <- v // 把 v 发送到通道 ch
- v := <-ch // 从 ch 接收数据,并把值赋给 v
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
ch := make(chan int)
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:
- 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)
- }
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
- package main
-
- import "fmt"
-
- func main() {
- // 这里我们定义了一个可以存储整数类型的带缓冲通道
- // 缓冲区大小为2
- ch := make(chan int, 2)
-
- // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
- // 而不用立刻需要去同步读取数据
- ch <- 1
- ch <- 2
-
- // 获取这两个数据
- fmt.Println(<-ch)
- fmt.Println(<-ch)
- }
Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:
v, ok := <-ch
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。
- package main
-
- 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)
- // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
- // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
- // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
- // 会结束,从而在接收第 11 个数据的时候就阻塞了。
- for i := range c {
- fmt.Println(i)
- }
- }
Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:
- package main
-
- import "fmt"
-
- func getSequence() func() int {
- i:=0
- return func() int {
- i+=1
- return i
- }
- }
-
- func main(){
- /* nextNumber 为一个函数,函数 i 为 0 */
- nextNumber := getSequence()
-
- /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
- fmt.Println(nextNumber())
- fmt.Println(nextNumber())
- fmt.Println(nextNumber())
-
- /* 创建新的函数 nextNumber1,并查看结果 */
- nextNumber1 := getSequence()
- fmt.Println(nextNumber1())
- fmt.Println(nextNumber1())
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。