一定要努力堵住那悠悠众口
Go的目录结构:
bin存放编译后的可执行文件;
pkg存放编译后的包文件;
src存放项目源文件
一、包的概念
包是go语言中不可缺少部分,在每个go源码的第一行进行定义,定义方式是:package "包名",并且该名称是作为调用该包时候所使用的名称。
包的概念总结:
每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名可能不同,为了规范,功能相似或者属于相同性质的源码归集到相同的包名中。
每个应用程序都必须有且只有一个包名称为main,是该程序的入口,执行时候会寻找包名为main的代码。
包与包之间可以被相互调用,主要为了减少代码重复性。
包一旦被引入就需要被使用,除非引入的时候忽略它(前面加_表示忽略它,只是用它来初始化),如果引入了包,而在代码中没用使用,运行时候会报错。
二、包的引入
1、包引入的方法
方法一:
import "fmt"
import "os"
方法二:
import "fmt"; import "os"
方法三(推荐)
import (
"fmt"
"os"
)
2、引入其他包
go语言中,引入包的路径是根据GOPATH/src 这个环境变量作为相对路径进行引入的,如果GOPATH存在多个,那么go将逐一寻找路径,直到找到,若GOPATH/src未找到则编译出错。
示例:
我的GOPATH为:/usr/local/go/awesomeProject
建立包文件:/usr/local/go/awesomeProject/src/day02/eg1/pkg.go
// pkg.go文件内容
package add
import "fmt"
func Sum(a int,b int) {
var c int
c = a + b
fmt.Println("res",c)
}
在main中进行引入:
package main
import (
"day02/eg1"
"fmt"
)
func main() {
add.Sum(2, 3) // 调用包
fmt.Println("Hello,World!")
}
三、变量
1、单个变量的声明
go语言中,所有变量都必须先声明再使用,下面是声明变量和赋值方法:
- 先声明后赋值: var <变量名称> <变量类型>
赋值:变量的赋值格式: <变量名称> = <表达式> - 声明的同时赋值:var <变量名称> <变量类型> = <表达式> (简写为 变量名称 := 表达式)
package main
import "fmt"
func main(){
var a string // 声明一个变量名为a,类型为字符串的变量
a = "wpr" // 对a变量赋值
var age int = 23 // 声明并赋值变量
fmt.Println(a,age)
}
2、多个变量声明
- 先声明后赋值:var <变量1>,<变量2>,<变量3> <变量类型>
赋值 : <变量1>,<变量2> = 值1,值2 - 声明同时赋值 :var <变量1>,<变量2> = 值1,值2
可简写为: <变量1>,<变量2> = 值1,值2 - 多个变量还可以使用var()声明
package main
import "fmt"
func main() {
var a,b int
a,b = 1,2
var c,d = 3,"wpr" // 类型自动推断
e,f :=4 ,"hello" //简写
fmt.Println(a,b,c,d,e,f)
}
使用var()声明
package main
import "fmt"
func main() {
var (
a int //默认值为0
b string // 默认值为空字符串("")
c = 3
)
fmt.Println(a,b,c)
}
3、变量名
命名规则:
- 首字符可以是任意的Unicode字符或者下划线;
- 剩余字符可以是Unicode字符、下划线、数字;
- 不能作为用以下关键字作为变量名
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
4、可见性
可见性即对包外可见,当其他包调用当前包的变量时候是否允许可见(可访问)。(类似python中的对外隐藏,以_开头)
- 变量开头字符大写,表示可见
- 变量开头字母非大写,则表示私有,不可见
5、变量的作用域
- 函数内定义的变量称为局部变量,其作用域在函数内
- 函数外定义的变量称为全局变量,作用于整个包,并且该变量首字母大写,则对其他包可见
四、常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
定义常量语法:
const 名称 类型 = value
多个常量定义语法:
const (
a = 0 // 类型自动推倒
b = 1
c = 2
)
iota解释:
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
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) // 0 1 2 ha ha 100 100 7 8
}
五、数据类型
1、布尔型
布尔类型 - 由两个预定义常量组成:true、false,默认值为false
package main
import "fmt"
func main() {
var (
a bool // 默认值为false
b bool = true
)
fmt.Println(a,b) // false true
}
2、数字型
整型:
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(IEEE-754 32位浮点型数)
float64(IEEE-754 64位浮点型数)
complex64(复数:32 位实数和虚数)
complex128(复数:64 位实数和虚数)
其他数字类型:
byte(字符类型,存储为ascii码,与uint8相同)
rune(与int32相同)
uint(32 或 64 位)
int(与 uint 大小相同)
uintptr(无符号整型,用于存放一个指针)
3、字符串类型
双引号(" ") —— 对特殊字符(如换行\t,\n等)进行转义。
反引号(``) —— 保留原始字符,打印时候原样输出。
package main
import "fmt"
func main() {
var (
a string = `wpr\n`
b string = "age\n"
)
fmt.Println(a,b) // wpr\n age
}
4、派生类型
指针类型
数组类型
结构类型
联合类型
函数类型
切片类型
接口类型
5、类型转换
不同的数据类型之间是不允许进行赋值或运算操作,必须经过类型转化才能进行运算或者赋值。
转换方法:数据类型()
package main
import "fmt"
func main() {
var a int32 = 1
var b int64 = 64
c:= int64(a)+ b //不转换编译报错
fmt.Println(c)
}
六、运算符
1、算数运算符
+相加 :x + y = z
-相减 : x - y = z
*相乘:x * y = z
% 求余: x % y =z
++ 自增 :x++
-- 自减:x--
2、关系运算符
== 判断两个值是否相等,如果相等返回 True,否则返回 False。
!= 判断两个值是否不相等,如果不相等返回 True, 否则返回 False。
> 判断左边值是否大于右边值,如果是返回 True,否则返回 False。
< 判断左边值是否小于右边值,如果是返回 True,否则返回 False。
>= 判断左边值是否大于等于右边值,如果是返回 True,否则返回 False。
<= 判断左边值是否小于等于右边值,如果是返回 True,否则返回 False。
3、逻辑运算符
&& 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。
4、位运算符
位运算符对整数在内存中的二进制位进行操作。
& 按位与运算符,"&"是双目运算符, 其功能是参与运算的两数各对应的二进位相与。
| 按位或运算符,"|"是双目运算符,其功能是参与运算的两数各对应的二进位相或。
^ 按位异或运算符,"^"是双目运算符, 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
<< 左移运算符,"<<"是双目运算符。左移n位就是乘以2的n次方。其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
>> 右移运算符,">>"是双目运算符。右移n位就是除以2的n次方。其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。
//假设A = 60, B = 13;
二进制格式表示
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
5、赋值运算
= 简单的赋值运算符,将一个表达式的值赋给一个左值
+= 相加后再赋值 (C += A 等于 C = C + A)
-= 相减后再赋值 (C -= A 等于 C = C - A)
*= 相乘后再赋值 (C *= A 等于 C = C * A)
/= 相除后再赋值 (C /= A 等于 C = C / A)
%= 求余后再赋值 (C %= A 等于 C = C % A)
<<= 左移后赋值 (C <<= 2 等于 C = C << 2)
>>= 右移后赋值 (C >>= 2 等于 C = C >> 2)
&= 按位与后赋值 (C &= 2 等于 C = C & 2)
^= 按位异或后赋值 (C ^= 2 等于 C = C ^ 2)
|= 按位或后赋值 (C |= 2 等于 C = C | 2)
6、变量运算符
& 取变量的地址(&a将给出变量a的实际地址)
* 取变量的指针( *a 是指向变量a的指针)
七、流程控制
1、if-else
if condition1 {
} else if condition2 { //else 一定要和大括号在一行
} else if condition3 {
} else {
}
2、for
一般for循环
for i := 0; i < 100; i++ { //语句使用分号隔开
}
死循环(类似python中的while)
for true {
}
//或者
for {
}
使用range遍历数组、slice、map、chan等
// 索引循环
package main
import "fmt"
func main() {
str := "hello world"
for k := range str{ // 字符串遍历
fmt.Printf("index=%d val=%c len=%d\n",k,str[k],len(str)) // index=0 val=h len=11...
}
}
// 同时循环index,val
package main
import "fmt"
func main(){
str := "hello world"
for index,val := range str{
fmt.Printf("index=%d val=%c len=%d\n",index,val,len(str)) // k代表索引,v代表值
}
}
3、switch
switch条件控制,当条件匹配当前case时候,执行当前case,不会进行往下执行,若都没有匹配到,则执行default,当然也可以使用fallthrought关键字强制执行下面语句。
switch var {
case 条件一:
case 条件二:
case 条件三:
default:
}
// switch简单示例
package main
import "fmt"
func main() {
var a = 0
switch a {
case 0:
fmt.Println("this is 0")
case 1:
fmt.Println("this is 1")
case 2:
fmt.Println("this is 2")
default:
fmt.Print("this is default")
}
}//结果 this is 0
// 多条件判断case
package main
import "fmt"
func main() {
var a = 0
switch { //这里没有变量
case a > 0 && a <3: //a in 0-3
fmt.Println("a in 0-3")
case a < -1 || a > 3:
fmt.Println("a <0 or a > 3")
case a == 0:
fmt.Println("a equal 0")
default:
fmt.Print("this is default")
} //结果 a equal 0
}
// 加入fallthrought
package main
import "fmt"
func main() {
var a = 0
switch {
case a > 0 && a <3: //a in 0-3
fmt.Println("a in 0-3")
case a < -1 || a > 3:
fmt.Println("a <0 or a > 3")
case a == 0:
fmt.Println("a equal 0")
fallthrough //使用fallthrought关键字当满足该条件时候,继续执行下面语句
default:
fmt.Printf("this is default\n")
}
//结果 a equal 0
//this is default
4、goto&label
label作为一个标记,语法是字符串+冒号定义,在循环中使用label可调至label的定义的位置。
package main
import (
"fmt"
"time"
)
func main() {
LABLE: //定义名称为LABLE
//fmt.Println("回到label")
for i := 0; i < 10; i++ {
fmt.Println("-->",i)
for true {
I++
if i == 6 {
time.Sleep(time.Second * 5)
break LABLE //跳出循环到LABLE定义的地方
}
fmt.Println(i)
}
}
}
结果:--> 0
1
2
3
4
5
goto作用在于跳转到label标签定义的地方继续执行。上述代码修改为goto,将是死循环
package main
import (
"fmt"
"time"
)
func main() {
LABLE: //定义名称为LABLE
//fmt.Println("回到label")
for i := 0; i < 10; i++ {
fmt.Println("-->",i)
for true {
I++
if i == 6 {
time.Sleep(time.Second * 5)
goto LABLE //跳出循环到LABLE定义的地方
}
fmt.Println(i)
}
}
} //结果:每5秒打印0,1,2,3,4,5
八、函数
函数是go语言编程的核心。特点:
不支持重载,一个包不能有两个名称一样的函数;
函数也是一种类型,一个函数可以赋值给变量(该特性和python一样);
函数也可以不用起名称,这种函数称为匿名函数;
go语言函数可以有多个返回值;
使用_标识符可忽略返回值。
函数定义语法:
func 函数名(形参1 类型,形参2 类型,...)(返回值名1 类型,返回值名2 类型,...){
函数体代码...
return 返回值1,返回值2
}
// 函数作为变量
package main
import (
"fmt"
)
func add(a int,b int) int {
return a + b
}
func main() {
c := add
d := c(1,2) // 加括号代表调用函数
fmt.Println(d)
} // 结果:3
// 忽略返回值
package main
import (
"fmt"
)
func add(a int,b int) (int,int) {
return a + b,a
}
func main() {
c := add
d , _ := c(1,2) //使用_忽略第二个返回值
fmt.Println(d)
}//结果:3
1、函数参数传递方式
值传递:一般基本的数据类型都是值传递,如数字、字符串。
引用传递:复杂的数据类型,如map、chan、interface,是引用传递。
注意:无论是值传递还是引用传递,传递给函数的都是变量的副本,不过值传递是值的拷贝,引用传递拷贝的是地址,一般来说地址拷贝更为高效。而值拷贝取决于拷贝对象大小,对象越大,则性能相对低。
2、函数返回值命名
go 函数的返回值或者结果参数可以指定一个名字(名字不是强制的),并且像原始的变量那样使用,就像输入参数那样。如果对其命名,在函数开始时,它们会用其类型的零值初始化;如果函数在不加参数的情况下执行了 return 语句,结果参数的当前值会作为返回值返回。
package main
import "fmt"
//斐波那契数列实现
func Factorial(x int) (result int) { //给函数返回值命名为result
if x <= 1 {
result = 1
} else {
result =Factorial(x - 2) + Factorial(x - 1)
}
return
}
func main() {
b := Factorial(4)
fmt.Println(b)
}//结果:5
3、匿名函数
匿名函数(对比javascript)即没有函数名的函数,只能放在函数中,可以实现函数嵌套定义的功能。
package main
import "fmt"
func main() {
c := func(a int,b int ) int {
return a + b
}(3,4)
fmt.Println(c)
} // 结果:7
4、函数的可变参数
go语言中,也可以像python一样传递可变参数(意思是传递时候才确定有几个参数,定义时候不知道参数个数),可以认为参数是数组形式:
func funcname(arg...type) { }
package main
import "fmt"
func add(a int, arg...int) int {
var sum = 0
for i := 0; i < len(arg); i++ {
sum += arg[i] //取参数的值
}
return sum
}
func main() {
c := add(1) //传递一个参数
d := add(2,3,4) //传递两个参数
fmt.Println(c,d)
}//结果:0,7
5、defer
当函数中存在defer关键字时候,一旦函数返回则执行defer语句,因此,defer可以做一些资源清理的事情。
特性:
多个defer语句,按照后进先出的方式执行(栈)。
defer语句中的变量,在defer声明时候就决定了。
package main
import "fmt"
func main() {
i := 0
defer fmt.Println(i) // 放入栈
I++
defer fmt.Println(i) //在放入栈,先执行
fmt.Println("wpr")
return
}//结果:wpr ,1,0
6、命令行参数
概念:
命令行参数(或参数):是指运行程序时提供的参数。
已定义命令行参数:是指程序中通过 flag.Xxx 等这种形式定义了的参数。输入参数时需要 -flag 形式。
非 flag(non-flag)命令行参数(或保留的命令行参数):不符合 -flag 形式的参数。- 、--和 --flag 都属于 non-flag 参数。
来自命令行的参数在程序中通过os.Args(对比python的sys.agrv) 获取,导入包 os 即可。其中 os.Args[0] 为执行的程序名,os.Args[1] ~ os.Args[n-1] 是具体的参数。
//示例
test.exe 1 2
//test.exe 为os.Args[0] ,1是 os.Args[1],2是os.Args[2]
7、go语言的初始化顺序
初始化顺序规则:引入的包 —> 包中的常量、变量、init —> 当前包的常量、变量、init —> main函数
注意事项:
- 当前go源文件中, 每一个被Import的包, 按其在源文件中出现顺序初始化。
- 如果当前包有多个init在不同的源文件中, 则按源文件名以字典顺序从小到大排序,小的先被执行到, 同一包且同一源文件中的init,则按其出现在文件中的先后顺序依次初始化; 当前包的package level变量常量也遵循这个规则; 其实准确来说,应是按提交给编译器的源文件名顺序为准,只是在提交编译器之前, go命令行工具对源文件名按字典序排序了。
- init只可以由go runtine自已调用, 我们在代码中不可以显示调用,也不可以被引用,如赋给a function variable。
- 包A 引入包B , 包B又引入包C, 则包的初始化顺序为: C -> B -> A
- 引入包,必须避免死循环,如 A 引 B , B引C, C引A
- 一个包被其它多个包引入,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次。
- 另一个大原则, 被依赖的总是先被初始化,main包总是被最后一个初始化,因为它总是依赖别的包。