GO 语言学习笔记

20个小时快速入门GO语言学习笔记:https://www.bilibili.com/video/av20432910?p=6 

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 语言学习笔记_第1张图片

变量命名 

 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)
}

基础数据类型

GO 语言学习笔记_第2张图片

上表注释: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)

}

运算符 

 GO 语言学习笔记_第3张图片

 GO 语言学习笔记_第4张图片

GO 语言学习笔记_第5张图片

GO 语言学习笔记_第6张图片 GO 语言学习笔记_第7张图片

GO 语言学习笔记_第8张图片

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不能当被除数,所以发生了错误,结果如下 

GO 语言学习笔记_第9张图片

 程序崩溃了之后,后面的“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语句

GO 语言学习笔记_第10张图片

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

GO 语言学习笔记_第11张图片

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

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])
	}
}

 结果为:

GO 语言学习笔记_第12张图片

局部变量

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)

}

*/

 

你可能感兴趣的:(GO)