go语言学习笔记

Go语言是什么
2009年11月10日,Go语言正式成为开源编程语言家庭的一员。

Go语言(或称Golang)是云计算时代的C语言。Go语言的诞生是为了让程序员有更高的生产效率,Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。开发人员在为项目选择语言时,不得不在快速开发和性能之间做出选择。C和C++这类语言提供了很快的执行速度,而 Ruby 和 Python 这类语言则擅长快速开发。Go语言在这两者间架起了桥梁,不仅提供了高性能的语言,同时也让开发更快速。

Go语言优势
可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。

静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。

语言层面支持并发,这个就是Go最大的特色,天生的支持并发。Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。

内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。

简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。

丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大。

内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。

跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。

内嵌C支持,Go里面也可以直接包含C代码,利用现有的丰富的C库。

Go适合用来做什么
服务器编程,以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。

分布式系统,数据库代理器等。

网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用。

内存数据库,如google开发的groupcache,couchbase的部分组建。

命令行来运行程序
每个Go源代码文件的开头都是一个package声明,表示该Go代码所属的包。包是Go语言里最基本的分发单位,也是工程管理中依赖关系的体现。

要生成Go可执行程序,必须建立一个名字为main的包,并且在该包中包含一个叫main()的函数(该函数是Go可执行程序的执行起点),一个文件夹里只能有一个main 函数,即只能有一个函数的入口。如果一定要将多个有main函数的代码放到一个文件中,则只需要在cmd中先编译,再运行(1. go build XXX.go    2.    XXX.go )或者直接单独运行(go run)。 

go build 命令:编译GO代码,生成一个可执行文件。go run 命令:不生成可执行文件,直接运行。

Go语言的main()函数不能带参数,也不能定义返回值。命令行传入的参数在os.Args变量 中保存。如果需要支持命令行开关,可使用flag包。后面我们将解释如何使用flag包来做命令行参数规范的定义,以及获取和解析命令行参数。

在包声明之后,是一系列的import语句,用于导入该程序所依赖的包。由于本示例程序用到了Println()函数,所以需要导入该函数所属的fmt包。

所有Go函数以关键字func开头。一个常规的函数定义包含以下部分:

func 函数名(参数列表)(返回值列表) {//左花括号的位置是固定在此位置,如果放到下一行,则会发生错误。
    // 函数体
}

Go程序并不要求开发者在每个语句后面加上分号表示语句结束。

变量命名 
 Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:heapSort和Heapsort是两个不同的名字。

变量声明和初始化 
1.声明的变量必须使用,声明的包也必须使用。2.只是声明没初始化的变量默认为0  3.同一个大括号里声明的变量名是唯一的。

4.变量初始化,var b int=10(声明时再赋值)。 
或者先声明,再赋值。

var b int
b=20
 最常用的是自动推到类型:

c:=10
根据初始化的值自动推到其类型。c:对于一个变量只能使用一次(初始化只需要初始化一次)

Printf和Println的区别
a:=10
fmt.Println("a=",a)//一段一段处理,直接处理一段,自动加换行
fmt.Printf("a=%d\n",a)//格式化处理,a的值放到%d的位置进行输出
使用起来Printf更加的方便控制格式,如:

//输出a,b,c三个数的值
a,b,c:=10,20,30
fmt.Println("a=",a,",b=",b,",c=",c)
fmt.Printf("a=%d,b=%d,c=%d\n",a,b,c)
多重赋值和匿名变量
//a,b,c三个数赋值
a,b,c:=10,20,30
//交换a,b值,和python 用法一样
a,b:=10,20
a,b=b,a
//可以这么理解,相当于左边的值是上边初始化的值,还没有发生变化,而后边改变后的值不影响左边值。
 匿名变量

temp,_=a,b//匿名变量丢弃数据不做处理,所以只给temp赋值,配合函数返回值使用才有优势
常量的使用
变量声明需要var,常量声明需要const

const a int=10
//常量也可以自动推到类型,但是不需要使用:=
const a=10//自动推到类型
fmt.Println("a,%T\n",a)//%T代表显示a的类型
多个变量定义
// 1 project main.go
package main
 
import (
    "fmt"
)
 
func main() {
    //多个变量定义方式1
    //var a int
    //var b float64
    //多个变量定义方式2
    var (
        a int
        b float64
    )
    a, b = 2, 3.2
    fmt.Println("a=", a)
    //fmt.Println("H")
    fmt.Println("b=", b)
    //多个常量定义方式1
    // const c int = 2
    // const d float64 = 3.2
    //多个常量定义方式2
    const (
        c int     = 2
        d float64 = 3.2
    )
    fmt.Println("c=", c)
    fmt.Println("d=", d)
}
iota枚举
package main
 
import "fmt"
 
func main() {
    //1.iota常量自动生成器,每隔一行代码,自动累加1
    //2.iota遇到const自动重置为0
    const (
        a     = iota //0
        b     = iota //1
        d int = 1
 
        c = iota //2
    )
    //遇到const重置为0
    const e = iota
    fmt.Printf("a=%d,b=%d,c=%d,d=%d,e=%d\n", a, b, c, d, e)
    //4.可以只写一个iota
    const (
        a1 = iota //0
        b1        //1
        c1        //2
    )
    fmt.Printf("a1=%d,b1=%d,c1=%d\n", a1, b1, c1)
    //5.如果在同一行,值都一样
    const (
        i          = iota
        j1, j2, j3 = iota, iota, iota
        k          = iota
    )
    fmt.Printf("i=%d,j1=%d,j2=%d,j3=%d,k=%d\n", i, j1, j2, j3, k)
}
基础数据类型


上表注释:byte只能放英文的字符,rune可以存放中文的字符,零值是指声明变量的初始值。 

bool类型 
package main
 
import "fmt"
 
func main() {
    //1、声明变量
    var a bool
    a = true
    fmt.Println("a=", a)
 
    //2.自动推到类型
    const b = 10
    fmt.Println("b=", b)
 
    c := false
    fmt.Println("c=", c)
 
    //3、没有初始化,零值为false
    var d bool
    fmt.Println("d=", d)
}
float类型 
package main
 
import "fmt"
 
func main() {
    //1、声明变量
    var a bool
    a = true
    fmt.Println("a=", a)
 
    //2.自动推到类型
    const b = 10
    fmt.Println("b=", b)
 
    c := false
    fmt.Println("c=", c)
 
    //3、没有初始化,零值为false
    var d bool
    fmt.Println("d=", d)
}
字符类型
package main
 
import "fmt"
 
func main() {
    //1.声明字符类型
    var ch byte
    ch = 97
    //fmt.Println("ch=", ch) //这种打印得不到字符类型的结果
    //格式化打印,%c以字符打印,%d以整形方式打印
    fmt.Printf("%c,%d\n", ch, ch)
 
    //2.字符是用单引号
    ch = 'a'
    fmt.Printf("%c,%d\n", ch, ch)
    //大小写转化,之间相差32
    fmt.Printf("小写转大写:%c\n", 'a'-32)
    fmt.Printf("大写转小写:%c\n", 'A'+32)
 
}
字符串类型
package main
 
import "fmt"
 
func main() {
    //1.声明变量
    var str1 string
    str1 = "abcd"
    fmt.Println("str1=", str1)
    //自动推到类型
    str2 := "min"
    fmt.Printf("str2 类型是%T", str2)
    fmt.Printf("字符串长度是:%d", len(str2))
}
字符和字符串的区别
1)字符单引号,字符串双引号,2)字符元素一般只有一个,转义字符除外。字符串一般由一个或多个组成,字符串隐藏了一个转义字符‘\0’,

复数类型
package main
 
import "fmt"
 
func main() {
    //初始化
    var t complex128
    t = 2.1 + 3.14i
    fmt.Println("t=", t)
 
    //自动推导类型
    t2 := 3.3 + 4i
    fmt.Printf("t2 type is %T\n", t2)
    //通过内置函数,取实部和虚部
    fmt.Println("real(t2)=", real(t2), ",imag(t2)=", imag(t2))
}
格式化输入输出 
打印格式    含义
%%    一个%字面量
%b    一个二进制整数值(基数为2),或者是一个(高级的)用科学计数法表示的指数为2的浮点数
%c    字符型。可以把输入的数字按照ASCII码相应转换为对应的字符
%d    一个十进制数值(基数为10)
%e    以科学记数法e表示的浮点数或者复数值
%E    以科学记数法E表示的浮点数或者复数值
%f    以标准记数法表示的浮点数或者复数值
%g    以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出
%G    以%E或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出
%o    一个以八进制表示的数字(基数为8)
%p    以十六进制(基数为16)表示的一个值的地址,前缀为0x,字母使用小写的a-f表示
%q    使用Go语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字
%s    字符串。输出字符串中的字符直至字符串中的空字符(字符串以’\0‘结尾,这个’\0’即空字符)
%t    以true或者false输出的布尔值
%T    使用Go语法输出的值的类型
%U    一个用Unicode表示法表示的整型码点,默认值为4个数字字符
%v    使用默认格式输出的内置或者自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话
%x    以十六进制表示的整型值(基数为十六),数字a-f使用小写表示
%X    以十六进制表示的整型值(基数为十六),数字A-F使用小写表示
 

package main
 
import "fmt"
 
func main() {
    a := 10
    b := "abc"
    c := 'a'
    d := 2.1
    //%T操作变量所属类型
    fmt.Printf("%T,%T,%T,%T\n", a, b, c, d)
    //输出数据
    fmt.Printf("a=%d,b=%s,c=%c,d=%f\n", a, b, c, d)
    //%v自动匹配格式
    fmt.Printf("a=%v,b=%v,c=%v,d=%v\n", a, b, c, d)
}
变量的输入
package main
 
import "fmt"
 
func main() {
    var a int
    fmt.Printf("请输入变量a: ")
 
    //阻塞等待用户的输入
    fmt.Scanf("%d", &a) //不能忘了&
 
    //另一种方法
    fmt.Scan(&a)
    fmt.Printf("a=", a)
}
类型转化 
package main
 
import "fmt"
 
func main() {
    var a bool
    a = true
    //fmt.Printf("a=%d\n", a)
    fmt.Printf("a=%t\n", a) //输出bool类型
    //bool类型不能转化为整形
    //fmt.Printf("a=%d\n", int(a)) //强制输出%d类型
    //整形不能转化为bool类型
    //不能转化的类型叫做不兼容类型
    var ch byte
    ch = 'a'
    var t int
    t = int(ch)//类型转换,把ch值转化为int
    fmt.Printf("t=%d ", t)
 
}
类型别名
给一个名字起一个别名 

package main
 
import "fmt"
 
func main() {
    type bigint int64 //给64起一个别名叫bigint
 
    var a bigint
    fmt.Printf("a type is %T\n", a)
 
    type (
        long int64
        char byte
    )
    var b long = 11
    var ch char = 'a'
    fmt.Printf("b=%d,ch=%c", b, ch)
 
}
运算符 
 

package main
 
import "fmt"
 
func main() {
    fmt.Println("4>3 的结果", 4 > 3)
    fmt.Println("4!=3的结果", 4 != 3)
 
    //取反
    fmt.Println("4>3 的结果", !(4 > 3))
    fmt.Println("4!=3的结果", !(4 != 3))
 
    fmt.Println("true && true", true && true)
 
    //先&& 后||
    fmt.Println(true && true || false)
}
 if的使用
package main
 
import "fmt"
 
func main() {
    a := "王红"
    if a == "王红" { //左括号必须与if在同一行
        fmt.Println("true")
    }
    //if支持1个初始化语句(a:=10),初始化语句和判断条件(a==10)以分号分割
 
    if a := 10; a == 10 { //条件为真则输出true
        fmt.Println("true")
    }
    //if elseif  else的使用
    b := 10
    if b == 10 {
        fmt.Println("true")
    } else {
        fmt.Println("false")
    }
    if a := 10; a == 10 {
        fmt.Println("true")
    } else {
        fmt.Println("false")
    }
 
    //多个if  else
    c := 9
 
    if c == 10 {
        fmt.Println("1")
    } else if c < 10 {
        fmt.Println("2")
    } else {
        fmt.Println("3")
    }
 
    if c := 9; c == 10 {
        fmt.Println("1")
    } else if c < 10 {
        fmt.Println("2")
    } else {
        fmt.Println("3")
    }
    
}

switch 的使用  
package main
 
import "fmt"
 
func main() {
    //break默认包含
    num := 1
    switch num { //switch后面写的是变量本身
    case 1:
        fmt.Println("按下的是1楼")
        //break不写也行,默认有
        fallthrough//不跳出switch语句,后面的无条件执行
    case 2:
        fmt.Println("按下的是2楼")
        //break
    case 3:
        fmt.Println("按下的是3楼")
        //break
    default:
        fmt.Println("按下的是3楼")
    }
}
for循环
package main
 
import "fmt"
 
func main() {
    //for 初始化条件;判断条件;条件变化{
    //    }
    //1+2+3....+100
    sum := 0
    for i := 1; i <= 100; i++ {
        sum += i
    }
    fmt.Println("sum= ", sum)
}
 range的使用
package main
 
import "fmt"
 
func main() {
    //配合数组或者切片使用
    str := "abc"
    //for 打印
    for i := 0; i < len(str); i++ {
        fmt.Printf("str[%d]=%c\n", i, str[i])
    }
 
    //range打印,默认返回两个值
    for i, ite := range str {
        fmt.Printf("str[%d]=%c\n", i, ite)
    }
 
    //r第二个返回值默认丢弃
    for i := range str {
        fmt.Printf("str[%d]=%c\n", i, str[i])
    }
    for i, _ := range str {
        fmt.Printf("str[%d]=%c\n", i, str[i])
    }
}
break和continue 
 
package main
 
import "fmt"
import "time"
 
func main() {
    i := 0
    for { //for 后面不写任何东西,那么这个循环条件永远为真,即死循环
        i++
        time.Sleep(time.Second) //延时1秒
        if i == 5 {
            //break
            continue
        }
        fmt.Println("i= ", i)
 
    }
}
goto的使用 
package main
 
import "fmt"
 
//import "time"//导入的包必须使用,要不然会发生错误
 
func main() {
    //break //break 只能使用 in a loop, switch, or select
    //goto 任何地方都可以使用,但是不能跨函数使用
    fmt.Println("11111")
    goto END //goto 是关键字 END是用户起的名字,叫做标签
 
    fmt.Println("222222")
END:
    fmt.Println("333333")
 
}
无参无返回值函数的使用
package main //必须有一个main包
 
import "fmt"
 
//无参无返回函数的定义
func MyFunc() {
    a := 333
    fmt.Println("a= ", a)
}
 
func main() { //必须有一个入口
    MyFunc() //调用函数
}
 

有参无返回值函数的使用 
package main //必须有一个main包
 
import "fmt"
 
//有参无返回函数的定义,普通参数列表
//定义参数时,括号中的参数是形参
func MyFunc(a int) { //圆括号中定义参数,括号中定义参数少了关键字var
 
    fmt.Println("a= ", a)
 
}
 
//多个参数传递
 
func MyFunc01(a int, b int) { // (a,b int)
    fmt.Printf("a= %d, b= %d\n", a, b)
}
 
//传递不同类型的参数
func MyFunc01(a int, b string, c float64) {
    fmt.Printf("a=%d,b=%d\n", a, b)
}
 
func MyFunc01(a, b int, c string, d, e float64) {
    fmt.Printf("a=%d,b=%d\n", a, b)
}
 
func main() { //必须有一个入口
    //有参无返回值调用格式:函数名(所需参数)
    //调用函数传递的参数叫实参
    //实参只能传递给形参
    MyFunc(666) //调用函数
    MyFunc01(111, 222)
}
 不定参数类型
package main //必须有一个main包
 
import "fmt"
 
func MyFunc01(args ...int) { //传递的实参可以是0个,也可以是多个
    fmt.Println("len(args)=", len(args)) //获取用户传递参数的个数
    for i := 0; i < len(args); i++ {
        fmt.Printf("args[%d]=%d\n", i, args[i])
    }
 
    for i, data := range args {
        fmt.Printf("args[%d]=%d\n", i, data)
    }
}
 
//一个是不定参数,一个是确定的参数
//固定参数一定要传参,不定参数可以根据需求传参
func MyFunc01(a int, args ...int) {
 
}
 
//不定参数只能放到参数列表的最后一个
//错误示范
/*
func MyFunc01(args ...int,a int) {
}
*/
func main() { //必须有一个入口
    MyFunc01()
    fmt.Println("++++++++++++++++++++++++++")
    MyFunc01(1)
    fmt.Println("++++++++++++++++++++++++++")
    MyFunc01(1, 2, 3)
}
package main
 
import "fmt"
 
func MyFunc(tmp ...int) {
    for _, data := range tmp {
        fmt.Println("tmp= ", data)
    }
}
 
func MyFunc1(tmp ...int) {
    for _, data := range tmp {
        fmt.Println("tmp= ", data)
    }
 
}
func test(args ...int) {
    //全部元素传递给MyFunc
    MyFunc(args...)
 
    //只想把后两个参数传递给MyFunc1
    MyFunc1(args[2:]...) //从args[2]开始(包含本身),把后面的所有元素传递过去
}
 
func main() {
    test(1, 2, 3, 4)
}
函数无参数,有一个返回值
 

package main
 
import "fmt"
 
//无参有返回值:只有一个返回值
//有返回值的函数需要通过return返回
func myfunc01() int {
    return 666
 
}
 
//给返回值变量起一个名字,最常用的写法
func myfunc02() (result int) {
    result = 666
    return
 
}
 
func main() {
    //无参有返回值函数调用
    var a int
    a = myfunc01()
    fmt.Println("a=", a)
 
    b := myfunc01()
    fmt.Println("b=", b)
 
    c := myfunc02()
    fmt.Println("c=", c)
}
函数无参数,有多个返回值
 

package main
 
import "fmt"
 
func myfunc01() (int, int, int) {
    return 1, 2, 3
}
 
// go推荐写法
func myfunc02() (a int, b int, c int) { //(a,b,c int)
    a, b, c = 111, 222, 333
    return
}
 
func main() {
    a, b, c := myfunc02()
    fmt.Printf("a=%d,b=%d,c=%d", a, b, c)
 
}
有参数有返回值
package main
 
import "fmt"
 
func myfunc01(a, b int) (max, min int) {
    if a > b {
        max = a
        min = b
    } else {
        max = b
        min = a
    }
 
    return
}
 
func main() {
    max, min := myfunc01(10, 20)
    fmt.Printf("max=%d,min=%d", max, min)
 
}
 递归函数
/*
 
package main
 
import "fmt"
 
func myfunc02() (b int) {
    b = 222
    fmt.Println("mufunc02 b", b)
    return
}
 
func myfunc01() (a int) {
    a = 111
    //调用另外一个函数
    b := myfunc02()
    fmt.Println("myfunc01 b=", b)
 
    fmt.Println("myfunc02 a=", a)
    return
}
 
func main() {
    a := myfunc01()
    fmt.Printf("a=%d", a)
 
}
 
 
*/
package main
 
import "fmt"
 
func funcc(c int) {
    fmt.Println("c= ", c)
}
 
func funcb(b int) {
    funcc(b - 1)
    fmt.Println("b= ", b)
 
}
 
func funca(a int) {
    funcb(a - 1)
    fmt.Println("a= ", a)
}
 
func main() {
    funca(3)
    fmt.Println("main")
 
}
 
package main
 
import "fmt"
 
func test(a int) {
    if a == 1 {
        return
    }
    fmt.Println("a= ", a)
    test(a - 1)
 
}
 
//实现1+2+3+....+100
func add(a int) (sum int) {
    if a == 1 {
        return 1
    } else {
        return a + add(a-1)
    }
}
 
func main() {
    test(3)
    fmt.Println("main")
    b := add(100)
    fmt.Println("b=", b)
 
}
函数类型 
package main
 
import "fmt"
 
func Add(a, b int) int {
    return a + b
}
 
func minus(a, b int) int {
    return a - b
}
 
//函数也是一种数据类型,通过type给一个函数类型起名
//Functype是一个函数类型,Functype是func(int,int)int函数类型的名字
type Functype func(int, int) int //没有函数名字,没有大括号
 
func main() {
    var result int
    result = Add(1, 2)
    fmt.Println("result= ", result) //传统调用方法
 
    //声明一个函数类型的变量,变量名叫fTest
    var fTest Functype
    fTest = Add            //给这个函数类型的变量赋值
    result = fTest(10, 20) //等价于Add(10,20)
    fmt.Println("result2= ", result)
 
    fTest = minus
    result = fTest(10, 5) //等价于minus(10,5)
    fmt.Println("result3= ", result)
 
}
回调函数
package main
 
import "fmt"
 
//回调函数:函数有一个参数是函数类型,这个函数就是回调函数
type Functype func(int, int) int //定义一个函数类型,名字是Functype
//多态:多种形态,调用同一个接口,不同的表现,可以实现不同的功能,加减乘除
//先有想法,后面再实现函数功能
//实现加法功能
func Add(a, b int) int {
    return a + b
}
 
//实现减法
func minus(a, b int) int {
    return a - b
}
 
func Calc(a, b int, fTest Functype) (result int) { //Calc这个函数可以拓展
    fmt.Println("calc")
    result = fTest(a, b) //这个函数还没有实现??
    return
}
 
func main() {
    a := Calc(1, 1, Add)
    fmt.Println("a= ", a)
    b := Calc(1, 1, minus)
    fmt.Println("b= ", b)
 
}
 匿名函数
package main
 
import "fmt"
 
func main() {
    a := 10
    str := "mike"
    //匿名函数,没有函数名字
    f1 := func() { //函数只是定义了,还没有调用,匿名函数形成的内部区域叫做闭包,匿名函数
        //可以调用函数外部的变量,如a,str
        fmt.Println("a= ", a)
        fmt.Println("str= ", str)
    }
    f1()
 
    //定义无参数的匿名函数,同时调用
    func() {
        fmt.Printf("a=%d,str=%s\n", a, str)
    }() //后面的()代表调用此匿名函数,因为无参数,所以括号里面为空。
    //定义带有参数的匿名函数
    f3 := func(i, j int) {
        fmt.Printf("i=%d,j=%d\n", i, j)
    }
    f3(1, 2)
    //定义匿名函数同时调用
    func(i, j int) {
        fmt.Printf("i=%d,j=%d\n", i, j)
    }(10, 20)
    //定义有参数有返回值的匿名函数
    x, y := func(i, j int) (max, min int) {
        if i > j {
            max = i
            min = j
        } else {
            max = j
            min = i
        }
        return
    }(10, 20)
    fmt.Printf("x=%d,y=%d", x, y)
}
 闭包以引用方式捕获外部变量
package main
 
import "fmt"
 
//闭包内部改了之后,这个变量将改变
func main() {
    a := 10
    str := "wanghong"
    func() { //闭包以引用方式,捕获外部变量,闭包内部改变变量,外部也将会改变
        a = 666
        str = "go"
        fmt.Printf("内部:a=%d,str=%s\n", a, str)
    }() //括号代表直接调用
 
    fmt.Printf("外部:a=%d,str=%s\n", a, str)
}
 闭包的特点
package main
 
import "fmt"
 
func test01() int {
    //函数被调用时,x才分配空间,才初始化为0。函数调用完毕,x自动释放为0
    var x int //没有初始化,值为0
    x++
    return x * x
}
 
//函数的返回值是一个匿名函数,返回一个函数类型,通过f来调用返回的匿名函数
func test02() func() int {
    var x int
    return func() int {
        x++
        return x * x
    }
}
func main() {
    //函数的返回值是一个匿名函数,返回一个函数类型,通过f来调用返回的匿名函数
    //它不关心不活的变量和常量是否已经超出了作用域
    //所以只要有闭包还在使用它,这些变量就会存在(不会被释放为0)
    f := test02()
    fmt.Println(f()) //1
    fmt.Println(f()) //4
    fmt.Println(f()) //9
    fmt.Println(f()) //16
 
}
func main01() {
    fmt.Println(test01())
    fmt.Println(test01())
    fmt.Println(test01())
    fmt.Println(test01())
 
}
defer的使用
package main
 
import "fmt"
 
func main() {
 
    //defer只能在函数或方法的内部使用
    //defer 延迟调用,main函数结束前调用
    defer fmt.Println("bbbbbbbbbbbbbbb")
 
    fmt.Println("aaaaaaaaaaaa")
 
}
 
/*
输出为
aaaaaaaaaaaa
bbbbbbbbbbbbbbb
如果没有defer输出为
bbbbbbbbbbbbbbb
aaaaaaaaaaaa
*/
 多个defer的调用顺序
package main
 
import "fmt"
 
func test(x int) {
    100 / x
}
 
func main() {
 
    //defer只能在函数或方法的内部使用
    //defer 延迟调用,main函数结束前调用
 
    fmt.Println("aaaaaaaaaaaa")
 
    fmt.Println("bbbbbbbbbbbbbbb")
 
    //调用一个函数导致内存出问题
    test(0)
 
    fmt.Println("cccccccccccccccccc")
 
}
 上面的程序由于0不能当被除数,所以发生了错误,结果如下 

 程序崩溃了之后,后面的“cccccccccccccccccccc”没有被打印

程序加了defer 之后

package main
 
import "fmt"
 
func test(x int) {
    result := 100 / x
    fmt.Println("result=", result)
}
 
func main() {
 
    //defer只能在函数或方法的内部使用
    //defer 延迟调用,main函数结束前调用
 
    defer fmt.Println("aaaaaaaaaaaa")
 
    defer fmt.Println("bbbbbbbbbbbbbbb")
 
    //调用一个函数导致内存出问题
    test(0)
 
    defer fmt.Println("cccccccccccccccccc")
 
}
结果为:先写defer的后调用 ,先进后出的顺序,在结果快执行完前执行按先写后执行的顺序执行defer语句

给test(0)前面加了defer之后,

 哪怕某一个程序发生错误,这些调用依旧会执行。

defer和匿名函数结合使用
package main
 
import "fmt"
 
func main() {
    a := 10
    b := 20
 
    defer func() {
        fmt.Printf("内部:a=%d,b=%d\n", a, b)
    }() //()代表调用此匿名函数
    a = 111
    b = 222
    fmt.Printf("外部:a=%d,b=%d\n", a, b)
 
}
 结果为:

package main
 
import "fmt"
 
func main() {
    a := 10
    b := 20
 
    defer func(a, b int) {
        fmt.Printf("内部:a=%d,b=%d\n", a, b)
    }(a, b) //()代表调用此匿名函数
    a = 111
    b = 222
    fmt.Printf("外部:a=%d,b=%d\n", a, b)
 
}
结果显示:

是因为执行到defer的时候,已经先传递参数了,此时的a=10,b=20,但是还没有调用。 

获取命令行参数
 

package main
 
import "fmt"
import "os"
 
func main() {
    //接受用户传递的参数,都是以字符串方式传递的
    list := os.Args
    n := len(list)
 
    fmt.Println("n= ", n) //结果是1
 
    //输入 xxx.exe a b
    for i := 0; i < n; i++ {
        fmt.Printf("list[%d]=%s\n", i, list[i])
    }
}
 结果为:

局部变量
package main
 
import "fmt"
 
func test() {
    a := 10
    fmt.Println("a=", a)
}
 
func main() {
    //定义在{}里面的变量就是局部变量,只能在{}里面有效
    //执行到定义变量那句话,才开始分配空间,离开作用域自动释放
    //作用域,变量其作用的范围
 
    //  a = 111//显示无定义a
    /*
        {
            i := 10
            fmt.Println("i= ", i)
        }
        i = 111//显示i没有定义
    */
    /*
        if flag := 3; flag == 3 {
            fmt.Println("flag=", flag)
        }
        flag = 4//显示flag无定义,因为flag是定义在if语句中的
    */
}
 全局变量
package main
 
import "fmt"
 
//定义在函数外部的变量是全局变量
var a int
 
func test() {
    fmt.Println("test a=", a)
}
func main() {
    a = 10
    fmt.Println("a= ", a)
    test()
}
 不同作用域同名变量
package main
 
import "fmt"
 
var a byte //定义一个全局变量
 
func main() {
    var a int
    //1.不同作用域,允许定义同名变量
    //2.使用变量的原则,就近原则
    fmt.Printf("%T\n", a)//int
    {
        var a float32
        fmt.Printf("%T\n", a)//float32
    }
}
 导入包
package main
 
 
//忽略此包
import _ "fmt"
 
func main() {
 
}
 
/*
//给包名起别名
import io "fmt"
 
func main() {
    io.Println("this is a test")
}
*/
/*
//.操作:调用函数无需包名
import . "fmt"
import . "os"
 
//点操作很容易使包的名和变量名一样,如Println或者Args类型的变量
func main() {
    Println("this is a test")
    Println("Args=", Args)
}
*/
/*
//方式1
import "fmt"//导入包:导入包之后必须使用,否则编译不过
import "os"
 
//方式2:常用
 
func main(){
    fmt.Println("this is a test")
    fmt.Println("os.Args=",os.Args)
 
}
 
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言学习笔记.pdf》是一本关于Go语言学习学习笔记,内容丰富且简洁明了。本书从基础知识开始,逐步介绍了Go语言的语法、特性和常用库函数等。在学习笔记中,作者通过实际的示例和练习帮助读者理解Go语言的概念和用法。 第一章介绍了Go语言的起源和发展,为读者提供了对Go语言背景的整体了解。第二章讲解了Go语言的基本语法,例如变量声明、循环和条件语句等。通过大量的代码示例,读者能够更好地理解Go语言的语法和结构。 接下来的章节重点介绍了Go语言的并发编程和高级特性。第三章详细介绍了Go语言中的goroutine和channel,这是Go语言并发编程的核心机制。作者通过生动的示例代码和实际应用案例,向读者展示了如何使用goroutine和channel实现并发编程。 第四章和第五章分别介绍了Go语言中的面向对象编程和函数式编程。通过深入讲解Go语言中的结构体、接口和函数,读者能够更好地应用这些特性进行代码设计和开发。 最后几章则介绍了Go语言中常用的库函数和工具。例如,第六章介绍了Go语言中用于网络编程的net包和http包。读者可以学习到如何使用这些库函数构建基于网络的应用程序。 总的来说,《Go语言学习笔记.pdf》是一本非常实用的Go语言学习资料。通过阅读这本书,读者能够系统地学习和理解Go语言的基本概念和高级特性,为之后的Go语言开发打下坚实的基础。无论是初学者还是有一定编程经验的开发者,都能从中获得丰富的知识和经验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值